From 41db56f119b14366354a774ecb28d16010047729 Mon Sep 17 00:00:00 2001 From: Maciej Torhan Date: Thu, 22 Jan 2026 11:56:49 +0100 Subject: [PATCH 01/94] [#90315] third-party: mockturtle: Add mockturtle Signed-off-by: Maciej Torhan --- third-party/CMakeLists.txt | 10 + third-party/mockturtle/LICENSE | 21 + .../mockturtle/algorithms/aig_balancing.hpp | 482 ++ .../mockturtle/algorithms/aig_resub.hpp | 986 +++ .../mockturtle/algorithms/akers_synthesis.hpp | 880 +++ .../algorithms/aqfp/aqfp_assumptions.hpp | 138 + .../algorithms/aqfp/aqfp_cleanup.hpp | 160 + .../mockturtle/algorithms/aqfp/aqfp_db.hpp | 450 ++ .../algorithms/aqfp/aqfp_fanout_resyn.hpp | 172 + .../algorithms/aqfp/aqfp_legalization.hpp | 331 + .../algorithms/aqfp/aqfp_node_resyn.hpp | 253 + .../algorithms/aqfp/aqfp_rebuild.hpp | 280 + .../algorithms/aqfp/aqfp_resynthesis.hpp | 378 ++ .../algorithms/aqfp/aqfp_retiming.hpp | 681 ++ .../algorithms/aqfp/buffer_insertion.hpp | 1980 ++++++ .../algorithms/aqfp/buffer_verification.hpp | 292 + .../mockturtle/algorithms/aqfp/detail/dag.hpp | 159 + .../algorithms/aqfp/detail/dag_cost.hpp | 508 ++ .../algorithms/aqfp/detail/dag_gen.hpp | 389 ++ .../algorithms/aqfp/detail/dag_util.hpp | 316 + .../algorithms/aqfp/detail/db_builder.hpp | 319 + .../algorithms/aqfp/detail/db_utils.hpp | 278 + .../algorithms/aqfp/detail/npn_cache.hpp | 89 + .../algorithms/aqfp/detail/partial_dag.hpp | 243 + .../mig_algebraic_rewriting_splitters.hpp | 407 ++ .../algorithms/aqfp/mig_resub_splitters.hpp | 581 ++ .../aqfp/optimal_buffer_insertion.hpp | 563 ++ .../mockturtle/algorithms/balancing.hpp | 446 ++ .../algorithms/balancing/esop_balancing.hpp | 305 + .../algorithms/balancing/sop_balancing.hpp | 201 + .../mockturtle/algorithms/balancing/utils.hpp | 80 + .../algorithms/bi_decomposition.hpp | 171 + .../mockturtle/algorithms/cell_window.hpp | 509 ++ .../algorithms/circuit_validator.hpp | 799 +++ .../include/mockturtle/algorithms/cleanup.hpp | 657 ++ .../include/mockturtle/algorithms/cnf.hpp | 507 ++ .../mockturtle/algorithms/collapse_mapped.hpp | 463 ++ .../mockturtle/algorithms/cover_to_graph.hpp | 306 + .../mockturtle/algorithms/cut_enumeration.hpp | 1840 +++++ .../algorithms/cut_enumeration/cnf_cut.hpp | 109 + .../cut_enumeration/exact_map_cut.hpp | 100 + .../algorithms/cut_enumeration/gia_cut.hpp | 82 + .../algorithms/cut_enumeration/mf_cut.hpp | 102 + .../cut_enumeration/rewrite_cut.hpp | 88 + .../algorithms/cut_enumeration/spectr_cut.hpp | 197 + .../cut_enumeration/tech_map_cut.hpp | 100 + .../mockturtle/algorithms/cut_rewriting.hpp | 868 +++ .../mockturtle/algorithms/decomposition.hpp | 301 + .../algorithms/detail/database_generator.hpp | 96 + .../algorithms/detail/mffc_utils.hpp | 135 + .../algorithms/detail/minmc_xags.hpp | 198 + .../algorithms/detail/resub_utils.hpp | 782 +++ .../algorithms/detail/switching_activity.hpp | 70 + .../mockturtle/algorithms/dont_cares.hpp | 358 + .../algorithms/dsd_decomposition.hpp | 238 + .../include/mockturtle/algorithms/emap.hpp | 5972 +++++++++++++++++ .../algorithms/equivalence_checking.hpp | 325 + .../algorithms/equivalence_classes.hpp | 156 + .../algorithms/exact_mc_synthesis.hpp | 638 ++ .../mockturtle/algorithms/exorcism.hpp | 82 + .../experimental/boolean_optimization.hpp | 356 + .../experimental/cost_generic_resub.hpp | 426 ++ .../algorithms/experimental/cost_resyn.hpp | 1289 ++++ .../experimental/decompose_multioutput.hpp | 397 ++ .../algorithms/experimental/sim_resub.hpp | 545 ++ .../algorithms/experimental/window_resub.hpp | 834 +++ .../mockturtle/algorithms/explorer.hpp | 996 +++ .../mockturtle/algorithms/extract_adders.hpp | 971 +++ .../mockturtle/algorithms/extract_linear.hpp | 208 + .../algorithms/functional_reduction.hpp | 488 ++ .../mockturtle/algorithms/gates_to_nodes.hpp | 174 + .../mockturtle/algorithms/klut_to_graph.hpp | 131 + .../algorithms/linear_resynthesis.hpp | 1008 +++ .../mockturtle/algorithms/lut_mapper.hpp | 3015 +++++++++ .../mockturtle/algorithms/lut_mapping.hpp | 546 ++ .../include/mockturtle/algorithms/mapper.hpp | 3483 ++++++++++ .../algorithms/mig_algebraic_rewriting.hpp | 376 ++ .../algorithms/mig_inv_optimization.hpp | 394 ++ .../algorithms/mig_inv_propagation.hpp | 374 ++ .../mockturtle/algorithms/mig_resub.hpp | 875 +++ .../include/mockturtle/algorithms/miter.hpp | 113 + .../algorithms/network_fuzz_tester.hpp | 260 + .../algorithms/node_resynthesis.hpp | 357 + .../algorithms/node_resynthesis/akers.hpp | 76 + .../node_resynthesis/bidecomposition.hpp | 82 + .../algorithms/node_resynthesis/cached.hpp | 317 + .../algorithms/node_resynthesis/composed.hpp | 83 + .../algorithms/node_resynthesis/davio.hpp | 150 + .../algorithms/node_resynthesis/direct.hpp | 264 + .../algorithms/node_resynthesis/dsd.hpp | 147 + .../algorithms/node_resynthesis/exact.hpp | 659 ++ .../algorithms/node_resynthesis/mig_npn.hpp | 234 + .../algorithms/node_resynthesis/null.hpp | 57 + .../algorithms/node_resynthesis/shannon.hpp | 100 + .../node_resynthesis/sop_factoring.hpp | 453 ++ .../algorithms/node_resynthesis/traits.hpp | 92 + .../algorithms/node_resynthesis/xag_minmc.hpp | 472 ++ .../node_resynthesis/xag_minmc2.hpp | 172 + .../algorithms/node_resynthesis/xag_npn.hpp | 691 ++ .../algorithms/node_resynthesis/xmg3_npn.hpp | 347 + .../algorithms/node_resynthesis/xmg_npn.hpp | 292 + .../algorithms/pattern_generation.hpp | 603 ++ .../mockturtle/algorithms/reconv_cut.hpp | 461 ++ .../mockturtle/algorithms/refactoring.hpp | 416 ++ .../mockturtle/algorithms/resubstitution.hpp | 859 +++ .../resyn_engines/aig_enumerative.hpp | 472 ++ .../algorithms/resyn_engines/dump_resyn.hpp | 120 + .../resyn_engines/mig_enumerative.hpp | 284 + .../algorithms/resyn_engines/mig_resyn.hpp | 1344 ++++ .../algorithms/resyn_engines/mux_resyn.hpp | 226 + .../algorithms/resyn_engines/xag_resyn.hpp | 890 +++ .../mockturtle/algorithms/retiming.hpp | 808 +++ .../include/mockturtle/algorithms/rewrite.hpp | 953 +++ .../mockturtle/algorithms/satlut_mapping.hpp | 480 ++ .../mockturtle/algorithms/sim_resub.hpp | 432 ++ .../mockturtle/algorithms/simulation.hpp | 982 +++ .../algorithms/testcase_minimizer.hpp | 558 ++ .../algorithms/window_rewriting.hpp | 799 +++ .../algorithms/xag_algebraic_rewriting.hpp | 565 ++ .../mockturtle/algorithms/xag_balancing.hpp | 729 ++ .../algorithms/xag_optimization.hpp | 273 + .../mockturtle/algorithms/xag_resub.hpp | 216 + .../algorithms/xag_resub_withDC.hpp | 989 +++ .../algorithms/xmg_algebraic_rewriting.hpp | 518 ++ .../algorithms/xmg_optimization.hpp | 101 + .../mockturtle/algorithms/xmg_resub.hpp | 346 + .../mockturtle/generators/arithmetic.hpp | 411 ++ .../include/mockturtle/generators/control.hpp | 217 + .../include/mockturtle/generators/legacy.hpp | 209 + .../mockturtle/generators/majority.hpp | 136 + .../mockturtle/generators/majority_n.hpp | 90 + .../generators/modular_arithmetic.hpp | 796 +++ .../mockturtle/generators/random_network.hpp | 672 ++ .../mockturtle/generators/self_dualize.hpp | 129 + .../include/mockturtle/generators/sorting.hpp | 148 + .../include/mockturtle/interface.hpp | 768 +++ .../include/mockturtle/io/aiger_reader.hpp | 233 + .../include/mockturtle/io/bench_reader.hpp | 194 + .../include/mockturtle/io/blif_reader.hpp | 335 + .../include/mockturtle/io/bristol_reader.hpp | 149 + .../include/mockturtle/io/dimacs_reader.hpp | 113 + .../include/mockturtle/io/genlib_reader.hpp | 169 + .../include/mockturtle/io/pla_reader.hpp | 154 + .../include/mockturtle/io/serialize.hpp | 377 ++ .../include/mockturtle/io/super_reader.hpp | 103 + .../include/mockturtle/io/verilog_reader.hpp | 636 ++ .../include/mockturtle/io/write_aiger.hpp | 199 + .../include/mockturtle/io/write_bench.hpp | 164 + .../include/mockturtle/io/write_blif.hpp | 430 ++ .../include/mockturtle/io/write_dimacs.hpp | 126 + .../include/mockturtle/io/write_dot.hpp | 456 ++ .../include/mockturtle/io/write_genlib.hpp | 105 + .../include/mockturtle/io/write_patterns.hpp | 87 + .../include/mockturtle/io/write_verilog.hpp | 1315 ++++ .../include/mockturtle/mockturtle.hpp | 218 + .../mockturtle/networks/abstract_xag.hpp | 892 +++ .../include/mockturtle/networks/aig.hpp | 1265 ++++ .../include/mockturtle/networks/aqfp.hpp | 1121 ++++ .../include/mockturtle/networks/block.hpp | 1078 +++ .../include/mockturtle/networks/buffered.hpp | 1030 +++ .../include/mockturtle/networks/cover.hpp | 808 +++ .../include/mockturtle/networks/crossed.hpp | 867 +++ .../mockturtle/networks/detail/foreach.hpp | 224 + .../include/mockturtle/networks/events.hpp | 135 + .../include/mockturtle/networks/generic.hpp | 1065 +++ .../include/mockturtle/networks/gia.hpp | 269 + .../include/mockturtle/networks/klut.hpp | 707 ++ .../include/mockturtle/networks/mig.hpp | 1249 ++++ .../include/mockturtle/networks/muxig.hpp | 235 + .../mockturtle/networks/sequential.hpp | 925 +++ .../include/mockturtle/networks/storage.hpp | 259 + .../include/mockturtle/networks/tig.hpp | 1133 ++++ .../include/mockturtle/networks/xag.hpp | 1288 ++++ .../include/mockturtle/networks/xmg.hpp | 1493 +++++ .../mockturtle/properties/aqfpcost.hpp | 237 + .../include/mockturtle/properties/litcost.hpp | 270 + .../include/mockturtle/properties/mccost.hpp | 301 + .../include/mockturtle/properties/migcost.hpp | 117 + .../include/mockturtle/properties/xmgcost.hpp | 129 + .../mockturtle/include/mockturtle/traits.hpp | 2674 ++++++++ .../include/mockturtle/utils/abc.hpp | 118 + .../include/mockturtle/utils/algorithm.hpp | 230 + .../mockturtle/utils/cost_functions.hpp | 146 + .../include/mockturtle/utils/cuts.hpp | 591 ++ .../mockturtle/utils/debugging_utils.hpp | 551 ++ .../mockturtle/utils/hash_functions.hpp | 156 + .../mockturtle/utils/include/percy.hpp | 51 + .../mockturtle/utils/include/supergate.hpp | 92 + .../utils/index_list/index_list.hpp | 1124 ++++ .../utils/index_list/list_simulator.hpp | 179 + .../include/mockturtle/utils/json_utils.hpp | 53 + .../include/mockturtle/utils/mixed_radix.hpp | 113 + .../include/mockturtle/utils/name_utils.hpp | 159 + .../mockturtle/utils/network_cache.hpp | 173 + .../mockturtle/utils/network_utils.hpp | 237 + .../include/mockturtle/utils/node_map.hpp | 555 ++ .../include/mockturtle/utils/null_utils.hpp | 46 + .../include/mockturtle/utils/progress_bar.hpp | 178 + .../utils/recursive_cost_functions.hpp | 122 + .../include/mockturtle/utils/sop_utils.hpp | 594 ++ .../mockturtle/utils/standard_cell.hpp | 98 + .../include/mockturtle/utils/stopwatch.hpp | 180 + .../include/mockturtle/utils/string_utils.hpp | 63 + .../mockturtle/utils/struct_library.hpp | 1542 +++++ .../include/mockturtle/utils/super_utils.hpp | 466 ++ .../include/mockturtle/utils/tech_library.hpp | 1652 +++++ .../mockturtle/utils/truth_table_cache.hpp | 172 + .../mockturtle/utils/truth_table_utils.hpp | 63 + .../include/mockturtle/utils/window_utils.hpp | 1046 +++ .../include/mockturtle/views/binding_view.hpp | 261 + .../include/mockturtle/views/cell_view.hpp | 297 + .../include/mockturtle/views/choice_view.hpp | 592 ++ .../include/mockturtle/views/cnf_view.hpp | 691 ++ .../include/mockturtle/views/color_view.hpp | 244 + .../include/mockturtle/views/cost_view.hpp | 281 + .../include/mockturtle/views/cut_view.hpp | 232 + .../include/mockturtle/views/depth_view.hpp | 363 + .../mockturtle/views/dont_care_view.hpp | 481 ++ .../mockturtle/views/dont_touch_view.hpp | 142 + .../mockturtle/views/fanout_limit_view.hpp | 293 + .../include/mockturtle/views/fanout_view.hpp | 350 + .../mockturtle/views/immutable_view.hpp | 89 + .../include/mockturtle/views/mapping_view.hpp | 276 + .../include/mockturtle/views/mffc_view.hpp | 310 + .../include/mockturtle/views/names_view.hpp | 210 + .../include/mockturtle/views/rank_view.hpp | 384 ++ .../include/mockturtle/views/topo_view.hpp | 324 + .../include/mockturtle/views/window_view.hpp | 308 + .../mockturtle/lib/kitty/kitty/affine.hpp | 217 + .../mockturtle/lib/kitty/kitty/algorithm.hpp | 489 ++ .../lib/kitty/kitty/bit_operations.hpp | 580 ++ .../mockturtle/lib/kitty/kitty/cnf.hpp | 79 + .../lib/kitty/kitty/constructors.hpp | 1587 +++++ .../mockturtle/lib/kitty/kitty/cube.hpp | 333 + .../lib/kitty/kitty/decomposition.hpp | 956 +++ .../lib/kitty/kitty/detail/constants.hpp | 196 + .../kitty/kitty/detail/linear_constants.hpp | 51 + .../lib/kitty/kitty/detail/mscfix.hpp | 40 + .../lib/kitty/kitty/detail/shift.hpp | 1002 +++ .../lib/kitty/kitty/detail/utils.hpp | 85 + .../lib/kitty/kitty/dynamic_truth_table.hpp | 191 + .../lib/kitty/kitty/enumeration.hpp | 105 + .../mockturtle/lib/kitty/kitty/esop.hpp | 349 + .../mockturtle/lib/kitty/kitty/hash.hpp | 95 + .../mockturtle/lib/kitty/kitty/implicant.hpp | 244 + .../mockturtle/lib/kitty/kitty/isop.hpp | 130 + .../lib/kitty/kitty/karnaugh_map.hpp | 188 + .../mockturtle/lib/kitty/kitty/kitty.hpp | 70 + .../mockturtle/lib/kitty/kitty/npn.hpp | 1327 ++++ .../mockturtle/lib/kitty/kitty/operations.hpp | 2489 +++++++ .../mockturtle/lib/kitty/kitty/operators.hpp | 507 ++ .../lib/kitty/kitty/partial_truth_table.hpp | 300 + .../lib/kitty/kitty/permutation.hpp | 286 + .../mockturtle/lib/kitty/kitty/print.hpp | 570 ++ .../mockturtle/lib/kitty/kitty/properties.hpp | 374 ++ .../kitty/kitty/quaternary_truth_table.hpp | 265 + .../mockturtle/lib/kitty/kitty/spectral.hpp | 1204 ++++ .../mockturtle/lib/kitty/kitty/spp.hpp | 156 + .../lib/kitty/kitty/static_truth_table.hpp | 290 + .../lib/kitty/kitty/ternary_truth_table.hpp | 262 + .../mockturtle/lib/kitty/kitty/traits.hpp | 55 + .../mockturtle/lib/lorina/lorina/aiger.hpp | 922 +++ .../mockturtle/lib/lorina/lorina/bench.hpp | 345 + .../mockturtle/lib/lorina/lorina/blif.hpp | 504 ++ .../mockturtle/lib/lorina/lorina/bristol.hpp | 217 + .../mockturtle/lib/lorina/lorina/common.hpp | 44 + .../detail/call_in_topological_order.hpp | 408 ++ .../lib/lorina/lorina/detail/tokenizer.hpp | 148 + .../lib/lorina/lorina/detail/utils.hpp | 242 + .../lib/lorina/lorina/diagnostics.hpp | 322 + .../lib/lorina/lorina/diagnostics.inc | 91 + .../mockturtle/lib/lorina/lorina/dimacs.hpp | 221 + .../mockturtle/lib/lorina/lorina/genlib.hpp | 383 ++ .../mockturtle/lib/lorina/lorina/lorina.hpp | 42 + .../mockturtle/lib/lorina/lorina/pla.hpp | 360 + .../mockturtle/lib/lorina/lorina/super.hpp | 287 + .../mockturtle/lib/lorina/lorina/verilog.hpp | 1774 +++++ .../lib/lorina/lorina/verilog_regex.hpp | 51 + .../parallel_hashmap/parallel_hashmap/btree.h | 4050 +++++++++++ .../parallel_hashmap/conanfile.py | 36 + .../parallel_hashmap/meminfo.h | 195 + .../parallel_hashmap/parallel_hashmap/phmap.h | 4544 +++++++++++++ .../parallel_hashmap/phmap_base.h | 5171 ++++++++++++++ .../parallel_hashmap/phmap_bits.h | 663 ++ .../parallel_hashmap/phmap_config.h | 764 +++ .../parallel_hashmap/phmap_dump.h | 256 + .../parallel_hashmap/phmap_fwd_decl.h | 154 + .../parallel_hashmap/phmap_utils.h | 376 ++ 288 files changed, 148785 insertions(+) create mode 100644 third-party/mockturtle/LICENSE create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aig_balancing.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aig_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/akers_synthesis.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_assumptions.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_cleanup.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_db.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_rebuild.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_retiming.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_verification.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_cost.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_gen.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_util.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_builder.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/npn_cache.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/partial_dag.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_resub_splitters.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/aqfp/optimal_buffer_insertion.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/balancing.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/balancing/esop_balancing.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/balancing/sop_balancing.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/balancing/utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/bi_decomposition.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cell_window.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/circuit_validator.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cleanup.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cnf.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/collapse_mapped.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cover_to_graph.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/cnf_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/gia_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/rewrite_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/spectr_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/cut_rewriting.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/decomposition.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/detail/database_generator.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/detail/mffc_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/detail/minmc_xags.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/detail/resub_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/detail/switching_activity.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/dont_cares.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/dsd_decomposition.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/emap.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/equivalence_checking.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/equivalence_classes.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/exact_mc_synthesis.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/exorcism.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/experimental/boolean_optimization.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_generic_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_resyn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/experimental/decompose_multioutput.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/experimental/sim_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/experimental/window_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/explorer.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/extract_adders.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/extract_linear.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/functional_reduction.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/gates_to_nodes.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/klut_to_graph.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/linear_resynthesis.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/lut_mapper.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/lut_mapping.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/mapper.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/mig_algebraic_rewriting.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/mig_inv_optimization.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/mig_inv_propagation.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/mig_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/miter.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/network_fuzz_tester.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/akers.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/bidecomposition.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/cached.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/composed.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/davio.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/direct.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/dsd.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/exact.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/mig_npn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/null.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/shannon.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/sop_factoring.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/traits.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg_npn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/pattern_generation.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/reconv_cut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/refactoring.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/resubstitution.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/aig_enumerative.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/dump_resyn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_enumerative.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_resyn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mux_resyn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/xag_resyn.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/retiming.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/rewrite.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/satlut_mapping.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/sim_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/simulation.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/testcase_minimizer.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/window_rewriting.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xag_algebraic_rewriting.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xag_balancing.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xag_optimization.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xag_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xag_resub_withDC.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xmg_algebraic_rewriting.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xmg_optimization.hpp create mode 100644 third-party/mockturtle/include/mockturtle/algorithms/xmg_resub.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/arithmetic.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/control.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/legacy.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/majority.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/majority_n.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/modular_arithmetic.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/random_network.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/self_dualize.hpp create mode 100644 third-party/mockturtle/include/mockturtle/generators/sorting.hpp create mode 100644 third-party/mockturtle/include/mockturtle/interface.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/aiger_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/bench_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/blif_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/bristol_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/dimacs_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/genlib_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/pla_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/serialize.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/super_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/verilog_reader.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_aiger.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_bench.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_blif.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_dimacs.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_dot.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_genlib.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_patterns.hpp create mode 100644 third-party/mockturtle/include/mockturtle/io/write_verilog.hpp create mode 100644 third-party/mockturtle/include/mockturtle/mockturtle.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/abstract_xag.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/aig.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/aqfp.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/block.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/buffered.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/cover.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/crossed.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/detail/foreach.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/events.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/generic.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/gia.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/klut.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/mig.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/muxig.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/sequential.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/storage.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/tig.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/xag.hpp create mode 100644 third-party/mockturtle/include/mockturtle/networks/xmg.hpp create mode 100644 third-party/mockturtle/include/mockturtle/properties/aqfpcost.hpp create mode 100644 third-party/mockturtle/include/mockturtle/properties/litcost.hpp create mode 100644 third-party/mockturtle/include/mockturtle/properties/mccost.hpp create mode 100644 third-party/mockturtle/include/mockturtle/properties/migcost.hpp create mode 100644 third-party/mockturtle/include/mockturtle/properties/xmgcost.hpp create mode 100644 third-party/mockturtle/include/mockturtle/traits.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/abc.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/algorithm.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/cost_functions.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/cuts.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/debugging_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/hash_functions.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/include/percy.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/include/supergate.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/index_list/index_list.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/index_list/list_simulator.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/json_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/mixed_radix.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/name_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/network_cache.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/network_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/node_map.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/null_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/progress_bar.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/recursive_cost_functions.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/sop_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/standard_cell.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/stopwatch.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/string_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/struct_library.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/super_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/tech_library.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/truth_table_cache.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/truth_table_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/utils/window_utils.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/binding_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/cell_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/choice_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/cnf_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/color_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/cost_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/cut_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/depth_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/dont_care_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/dont_touch_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/fanout_limit_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/fanout_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/immutable_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/mapping_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/mffc_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/names_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/rank_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/topo_view.hpp create mode 100644 third-party/mockturtle/include/mockturtle/views/window_view.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/affine.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/algorithm.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/bit_operations.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/cnf.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/constructors.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/cube.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/decomposition.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/detail/constants.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/detail/linear_constants.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/detail/mscfix.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/detail/shift.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/detail/utils.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/dynamic_truth_table.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/enumeration.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/esop.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/hash.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/implicant.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/isop.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/karnaugh_map.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/kitty.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/npn.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/operations.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/operators.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/partial_truth_table.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/permutation.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/print.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/properties.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/quaternary_truth_table.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/spectral.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/spp.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/static_truth_table.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/ternary_truth_table.hpp create mode 100644 third-party/mockturtle/lib/kitty/kitty/traits.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/aiger.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/bench.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/blif.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/bristol.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/common.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/detail/call_in_topological_order.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/detail/tokenizer.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/detail/utils.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/diagnostics.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/diagnostics.inc create mode 100644 third-party/mockturtle/lib/lorina/lorina/dimacs.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/genlib.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/lorina.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/pla.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/super.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/verilog.hpp create mode 100644 third-party/mockturtle/lib/lorina/lorina/verilog_regex.hpp create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/btree.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/conanfile.py create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/meminfo.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_base.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_bits.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_config.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_dump.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_fwd_decl.h create mode 100644 third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_utils.h diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index a308f2136cb..bb6cc00e86a 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -56,3 +56,13 @@ add_compile_options( add_subdirectory(abc) endif() + +add_library(mockturtle INTERFACE) + +target_include_directories(mockturtle + INTERFACE + ./mockturtle/include/ + ./mockturtle/lib/kitty/ + ./mockturtle/lib/lorina/ + ./mockturtle/lib/parallel_hashmap/ +) diff --git a/third-party/mockturtle/LICENSE b/third-party/mockturtle/LICENSE new file mode 100644 index 00000000000..4b575c201a2 --- /dev/null +++ b/third-party/mockturtle/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2019 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aig_balancing.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aig_balancing.hpp new file mode 100644 index 00000000000..cf5a47530f4 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aig_balancing.hpp @@ -0,0 +1,482 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aig_balancing.hpp + \brief Balances the AIG to reduce the depth + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +#include "cleanup.hpp" +#include "../networks/aig.hpp" +#include "../traits.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" + +namespace mockturtle +{ + +struct aig_balancing_params +{ + /*! \brief Minimizes the number of levels. */ + bool minimize_levels{ true }; + + /*! \brief Use fast version, it may not find some area optimizations. */ + bool fast_mode{ true }; +}; + +namespace detail +{ + +template +class aig_balance_impl +{ +public: + static constexpr size_t storage_init_size = 30; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using storage_t = std::vector>; + +public: + aig_balance_impl( Ntk& ntk, aig_balancing_params const& ps ) + : ntk( ntk ), ps( ps ), storage( storage_init_size ) + { + } + + void run() + { + ntk.clear_values(); + + for ( auto i = 0; i < storage_init_size; ++i ) + storage[i].reserve( 10 ); + + /* balance every CO */ + ntk.foreach_co( [&]( auto const& f ) { + balance_rec( ntk.get_node( f ), 0 ); + } ); + } + +private: + signal balance_rec( node const& n, uint32_t level ) + { + if ( ntk.is_ci( n ) ) + return ntk.make_signal( n ); + + /* node has been replaced in a previous recursion */ + if ( ntk.is_dead( n ) || ntk.value( n ) > 0 ) + { + return ntk.make_signal( find_substituted_node( n ) ); + } + + if ( level >= storage.size() ) + { + storage.emplace_back( std::vector() ); + storage.back().reserve( 10 ); + } + + /* collect leaves of the AND tree */ + collect_leaves( n, storage[level] ); + + if ( storage[level].size() == 0 ) + { + ntk.substitute_node( n, ntk.get_constant( false ) ); + return ntk.get_constant( false ); + } + + /* recur over the leaves */ + for ( auto& f : storage[level] ) + { + signal new_signal = balance_rec( ntk.get_node( f ), level + 1 ); + f = new_signal ^ ntk.is_complemented( f ); + } + + assert( storage[level].size() > 1 ); + + /* sort by decreasing level */ + std::stable_sort( storage[level].begin(), storage[level].end(), [this]( auto const& a, auto const& b ) { + return ntk.level( ntk.get_node( a ) ) > ntk.level( ntk.get_node( b ) ); + } ); + + /* mark TFI cone of n */ + ntk.incr_trav_id(); + mark_tfi( ntk.make_signal( n ), true ); + + /* generate the AND tree */ + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + if ( ps.fast_mode ) + { + if ( ps.minimize_levels ) + pick_nodes_fast( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_area_fast( storage[level] ); + } + else + { + if ( ps.minimize_levels ) + pick_nodes( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_area( storage[level] ); + } + + /* pop the two selected nodes to create the new AND gate */ + signal child1 = storage[level].back(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_and( child1, child2 ); + + /* update level for AND node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted( storage[level], new_sig ); + } + + signal root = storage[level][0]; + + /* replace if new */ + if ( n != ntk.get_node( root ) ) + { + ntk.substitute_node_no_restrash( n, root ); + } + + /* remember the substitution and the new node as already balanced */ + ntk.set_value( n, ntk.node_to_index( ntk.get_node( root ) ) ); + ntk.set_value( ntk.get_node( root ), ntk.node_to_index( ntk.get_node( root ) ) ); + + /* clean leaves storage */ + storage[level].clear(); + + return root; + } + + void collect_leaves( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_rec( ntk.make_signal( n ), leaves, true ); + + /* check for constant false */ + if ( ret < 0 ) + { + leaves.clear(); + } + } + + int collect_leaves_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + for ( signal const& s : leaves ) + { + if ( ntk.get_node( s ) != n ) + continue; + + if ( s == f ) + return 1; /* same polarity: duplicate */ + else + return -1; /* opposite polarity: const0 */ + } + + return 0; + } + + /* set as leaf if signal is complemented or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret |= collect_leaves_rec( child, leaves, false ); + } ); + + return ret; + } + + size_t find_left_most_at_level( std::vector const& leaves ) + { + size_t pointer = leaves.size() - 1; + uint32_t current_level = ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ); + + while ( pointer > 0 ) + { + if ( ntk.level( ntk.get_node( leaves[pointer - 1] ) ) > current_level ) + break; + + --pointer; + } + + assert( ntk.level( ntk.get_node( leaves[pointer] ) ) == current_level ); + return pointer; + } + + inline void pick_nodes( std::vector& leaves, size_t left_most ) + { + size_t right_most = leaves.size() - 2; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + inline void pick_nodes_fast( std::vector& leaves, size_t left_most ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_area( std::vector& leaves ) + { + for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + inline void pick_nodes_area_fast( std::vector& leaves ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + void insert_node_sorted( std::vector& leaves, signal const& f ) + { + node n = ntk.get_node( f ); + + /* check uniqueness */ + for ( auto const& s : leaves ) + { + if ( s == f ) + return; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void update_level( node const& n ) + { + uint32_t l = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + l = std::max( l, ntk.level( ntk.get_node( f ) ) ); + } ); + + ntk.set_level( n, l + 1 ); + } + + node find_substituted_node( node n ) + { + while ( ntk.is_dead( n ) ) + n = ntk.index_to_node( ntk.value( n ) ); + + return n; + } + + void mark_tfi( signal const& f, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + + /* set as leaf if signal is complemented or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + return; + } + + ntk.foreach_fanin( n, [&]( auto const& child ) { + mark_tfi( child, false ); + } ); + } + +private: + Ntk& ntk; + aig_balancing_params const& ps; + + storage_t storage; +}; + +} /* namespace detail */ + +/*! \brief AIG balancing. + * + * This method balance the AIG to reduce the + * depth. Level minimization can be turned off. + * In this case, balancing tries to reconstruct + * AND trees such that logic sharing is maximized. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + * - `has_and` + */ +template +void aig_balance( Ntk& ntk, aig_balancing_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_clone_node_v, "Ntk does not implement the clone_node method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_has_and_v, "Ntk does not implement the has_and method" ); + + fanout_view f_ntk{ ntk }; + depth_view> d_ntk{ f_ntk }; + + detail::aig_balance_impl p( d_ntk, ps ); + p.run(); + + ntk = cleanup_dangling( ntk ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aig_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aig_resub.hpp new file mode 100644 index 00000000000..d0dbae9d2ed --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aig_resub.hpp @@ -0,0 +1,986 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aig_resub.hpp + \brief Resubstitution + + \author Alessandro Tempia Calvino + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../utils/index_list/index_list.hpp" +#include "../utils/truth_table_utils.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/xag_resyn.hpp" + +namespace mockturtle +{ + +struct aig_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for 12-resub. */ + stopwatch<>::duration time_resub12{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Accumulated runtime for three-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted single AND-resubsitutions */ + uint64_t num_div1_and_accepts{ 0 }; + + /*! \brief Number of accepted single OR-resubsitutions */ + uint64_t num_div1_or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions using triples of unate divisors */ + uint64_t num_div12_accepts{ 0 }; + + /*! \brief Number of accepted single 2AND-resubsitutions */ + uint64_t num_div12_2and_accepts{ 0 }; + + /*! \brief Number of accepted single 2OR-resubsitutions */ + uint64_t num_div12_2or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + /*! \brief Number of accepted double AND-OR-resubsitutions */ + uint64_t num_div2_and_or_accepts{ 0 }; + + /*! \brief Number of accepted double OR-AND-resubsitutions */ + uint64_t num_div2_or_and_accepts{ 0 }; + + /*! \brief Number of accepted three resubsitutions */ + uint64_t num_div3_accepts{ 0 }; + + /*! \brief Number of accepted AND-2OR-resubsitutions */ + uint64_t num_div3_and_2or_accepts{ 0 }; + + /*! \brief Number of accepted OR-2AND-resubsitutions */ + uint64_t num_div3_or_2and_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: aig_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} ({:>5.2f} secs)\n", + num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] 12-resub {:6d} = {:6d} 2AND + {:6d} 2OR ({:>5.2f} secs)\n", + num_div12_accepts, num_div12_2and_accepts, num_div12_2or_accepts, to_seconds( time_resub12 ) ); + std::cout << fmt::format( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + std::cout << fmt::format( "[i] 2-resub {:6d} = {:6d} AND-OR + {:6d} OR-AND ({:>5.2f} secs)\n", + num_div2_accepts, num_div2_and_or_accepts, num_div2_or_and_accepts, to_seconds( time_resub2 ) ); + std::cout << fmt::format( "[i] 3-resub {:6d} = {:6d} AND-2OR + {:6d} OR-2AND ({:>5.2f} secs)\n", + num_div3_accepts, num_div3_and_2or_accepts, num_div3_or_2and_accepts, to_seconds( time_resub3 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_div1_accepts + num_div12_accepts + num_div2_accepts + num_div3_accepts ) ); + } +}; /* aig_resub_stats */ + +template +struct aig_resub_functor +{ +public: + using node = aig_network::node; + using signal = aig_network::signal; + using stats = aig_resub_stats; + + struct unate_divisors + { + using signal = typename aig_network::signal; + + std::vector positive_divisors; + std::vector negative_divisors; + std::vector next_candidates; + + void clear() + { + positive_divisors.clear(); + negative_divisors.clear(); + next_candidates.clear(); + } + }; + + struct binate_divisors + { + using signal = typename aig_network::signal; + + std::vector positive_divisors0; + std::vector positive_divisors1; + std::vector negative_divisors0; + std::vector negative_divisors1; + + void clear() + { + positive_divisors0.clear(); + positive_divisors1.clear(); + negative_divisors0.clear(); + negative_divisors1.clear(); + } + }; + +public: + explicit aig_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t max_depth, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, max_depth ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, max_depth ); + } ); + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, max_depth ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + if ( max_inserts == 1 || num_mffc == 2 ) + return std::nullopt; + + /* consider triples */ + g = call_with_stopwatch( st.time_resub12, [&]() { return resub_div12( root, max_depth ); } ); + if ( g ) + { + ++st.num_div12_accepts; + last_gain = num_mffc - 2; + return g; /* accepted resub */ + } + + /* collect level two divisors */ + call_with_stopwatch( st.time_collect_binate_divisors, [&]() { + collect_binate_divisors( root, max_depth ); + } ); + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, max_depth ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = num_mffc - 2; + return g; /* accepted resub */ + } + + if ( max_inserts == 2 || num_mffc == 3 ) + return std::nullopt; + + /* consider three nodes */ + g = call_with_stopwatch( st.time_resub3, [&]() { return resub_div3( root, max_depth ); } ); + if ( g ) + { + ++st.num_div3_accepts; + last_gain = num_mffc - 3; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root ) const + { + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t max_depth ) const + { + (void)max_depth; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + continue; /* next */ + + assert( ntk.level( d ) <= max_depth ); + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t max_depth ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( ntk.level( d ) > max_depth - 1 ) + continue; + + auto const& tt_d = sim.get_tt( ntk.make_signal( d ) ); + + /* check positive containment */ + if ( kitty::implies( tt_d, tt ) ) + { + udivs.positive_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + /* check negative containment */ + if ( kitty::implies( tt, tt_d ) ) + { + udivs.negative_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + if ( true ) // ( ps.fix_bug ) + { + /* unreachable case */ + // if ( kitty::implies( ~tt_d, tt ) ) + // { + // udivs.positive_divisors.emplace_back( !ntk.make_signal( d ) ); + // continue; + // } + if ( kitty::implies( tt, ~tt_d ) ) + { + udivs.negative_divisors.emplace_back( !ntk.make_signal( d ) ); + continue; + } + } + + udivs.next_candidates.emplace_back( ntk.make_signal( d ) ); + } + } + + std::optional resub_div1( node const& root, uint32_t max_depth ) + { + (void)max_depth; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + /* check for positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const& s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const& s1 = udivs.positive_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( ( tt_s0 | tt_s1 ) == tt ) + { + ++st.num_div1_or_accepts; + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + assert( ntk.level( ntk.get_node( l ) ) <= max_depth - 1 && ntk.level( ntk.get_node( r ) ) <= max_depth - 1 ); + return sim.get_phase( root ) ? !ntk.create_or( l, r ) : ntk.create_or( l, r ); + } + } + } + + /* check for negative unate divisors */ + for ( auto i = 0u; i < udivs.negative_divisors.size(); ++i ) + { + auto const& s0 = udivs.negative_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.negative_divisors.size(); ++j ) + { + auto const& s1 = udivs.negative_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( ( tt_s0 & tt_s1 ) == tt ) + { + ++st.num_div1_and_accepts; + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + assert( ntk.level( ntk.get_node( l ) ) <= max_depth - 1 && ntk.level( ntk.get_node( r ) ) <= max_depth - 1 ); + return sim.get_phase( root ) ? !ntk.create_and( l, r ) : ntk.create_and( l, r ); + } + } + } + + return std::nullopt; + } + + std::optional resub_div12( node const& root, uint32_t max_depth ) + { + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + + /* check positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( ( tt_s0 | tt_s1 | tt_s2 ) == tt ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= max_depth - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + if ( ntk.level( ntk.get_node( min0 ) ) > max_level - 2 || ntk.level( ntk.get_node( min1 ) ) > max_level - 2 ) + continue; + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + ++st.num_div12_2or_accepts; + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_or( b, c ) ) : ntk.create_or( a, ntk.create_or( b, c ) ); + } + } + } + } + + /* check negative unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( ( tt_s0 & tt_s1 & tt_s2 ) == tt ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= max_depth - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + if ( ntk.level( ntk.get_node( min0 ) ) > max_level - 2 || ntk.level( ntk.get_node( min1 ) ) > max_level - 2 ) + continue; + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + ++st.num_div12_2and_accepts; + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_and( b, c ) ) : ntk.create_and( a, ntk.create_and( b, c ) ); + } + } + } + } + + return std::nullopt; + } + + void collect_binate_divisors( node const& root, uint32_t max_depth ) + { + bdivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < udivs.next_candidates.size(); ++i ) + { + auto const& s0 = udivs.next_candidates.at( i ); + if ( ntk.level( ntk.get_node( s0 ) ) > max_depth - 2 ) + continue; + + for ( auto j = i + 1; j < udivs.next_candidates.size(); ++j ) + { + auto const& s1 = udivs.next_candidates.at( j ); + if ( ntk.level( ntk.get_node( s1 ) ) > max_depth - 2 ) + continue; + + if ( bdivs.positive_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( ~tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( ~tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + } + + if ( bdivs.negative_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt, tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + } + } + } + } + + std::optional resub_div2( node const& root, uint32_t max_depth ) + { + (void)max_depth; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + + /* check positive unate divisors */ + for ( const auto& s0 : udivs.positive_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.positive_divisors0.size(); ++j ) + { + auto const s1 = bdivs.positive_divisors0.at( j ); + auto const s2 = bdivs.positive_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( ( tt_s0 | ( tt_s1 & tt_s2 ) ) == tt ) + { + ++st.num_div2_or_and_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 1 ); + assert( ntk.level( ntk.get_node( b ) ) <= max_depth - 2 && ntk.level( ntk.get_node( c ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_and( b, c ) ) : ntk.create_or( a, ntk.create_and( b, c ) ); + } + } + } + + /* check negative unate divisors */ + for ( const auto& s0 : udivs.negative_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.negative_divisors0.size(); ++j ) + { + auto const s1 = bdivs.negative_divisors0.at( j ); + auto const s2 = bdivs.negative_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( ( tt_s0 | ( tt_s1 & tt_s2 ) ) == tt ) + { + ++st.num_div2_or_and_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 1 ); + assert( ntk.level( ntk.get_node( b ) ) <= max_depth - 2 && ntk.level( ntk.get_node( c ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_or( b, c ) ) : ntk.create_and( a, ntk.create_or( b, c ) ); + } + } + } + + return std::nullopt; + } + + std::optional resub_div3( node const& root, uint32_t max_depth ) + { + (void)max_depth; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + + for ( auto i = 0u; i < bdivs.positive_divisors0.size(); ++i ) + { + auto const s0 = bdivs.positive_divisors0.at( i ); + auto const s1 = bdivs.positive_divisors1.at( i ); + + for ( auto j = i + 1; j < bdivs.positive_divisors0.size(); ++j ) + { + auto const s2 = bdivs.positive_divisors0.at( j ); + auto const s3 = bdivs.positive_divisors1.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + auto const& tt_s3 = sim.get_tt( s3 ); + + if ( ( ( tt_s0 | tt_s1 ) & ( tt_s2 | tt_s3 ) ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto const d = sim.get_phase( ntk.get_node( s3 ) ) ? !s3 : s3; + + ++st.num_div3_and_2or_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 2 && ntk.level( ntk.get_node( b ) ) <= max_depth - 2 ); + assert( ntk.level( ntk.get_node( c ) ) <= max_depth - 2 && ntk.level( ntk.get_node( d ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_and( ntk.create_or( a, b ), ntk.create_or( c, d ) ) : ntk.create_and( ntk.create_or( a, b ), ntk.create_or( c, d ) ); + } + } + } + + for ( auto i = 0u; i < bdivs.negative_divisors0.size(); ++i ) + { + auto const s0 = bdivs.negative_divisors0.at( i ); + auto const s1 = bdivs.negative_divisors1.at( i ); + + for ( auto j = i + 1; j < bdivs.negative_divisors0.size(); ++j ) + { + auto const s2 = bdivs.negative_divisors0.at( j ); + auto const s3 = bdivs.negative_divisors1.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + auto const& tt_s3 = sim.get_tt( s3 ); + + if ( ( ( tt_s0 & tt_s1 ) | ( tt_s2 & tt_s3 ) ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto const d = sim.get_phase( ntk.get_node( s3 ) ) ? !s3 : s3; + + ++st.num_div3_or_2and_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 2 && ntk.level( ntk.get_node( b ) ) <= max_depth - 2 ); + assert( ntk.level( ntk.get_node( c ) ) <= max_depth - 2 && ntk.level( ntk.get_node( d ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_or( ntk.create_and( a, b ), ntk.create_and( c, d ) ) : ntk.create_or( ntk.create_and( a, b ), ntk.create_and( c, d ) ); + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; + binate_divisors bdivs; +}; /* aig_resub_functor */ + +struct aig_resyn_resub_stats +{ + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_compute_function{ 0 }; + + /*! \brief Number of found solutions. */ + uint32_t num_success{ 0 }; + + /*! \brief Number of times that no solution can be found. */ + uint32_t num_fail{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] #solution = {:6d}\n", num_success ); + fmt::print( "[i] #invoke = {:6d}\n", num_success + num_fail ); + fmt::print( "[i] engine time: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + } +}; /* aig_resyn_resub_stats */ + +/*! \brief Interfacing resubstitution functor with AIG resynthesis engines for `window_based_resub_engine`. + */ +template>> +struct aig_resyn_functor +{ +public: + using node = aig_network::node; + using signal = aig_network::signal; + using stats = aig_resyn_resub_stats; + using TT = typename ResynEngine::truth_table_t; + + static_assert( std::is_same_v, "truth table type of the simulator does not match" ); + +public: + explicit aig_resyn_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), tts( ntk ), divs( divs ), st( st ) + { + assert( divs.size() == num_divs ); + (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TTcare care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)required; + TT target = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + TT care_transformed = target.construct(); + care_transformed = care; + + typename ResynEngine::stats st_eng; + ResynEngine engine( st_eng ); + for ( auto const& d : divs ) + { + div_signals.emplace_back( sim.get_phase( d ) ? !ntk.make_signal( d ) : ntk.make_signal( d ) ); + tts[d] = sim.get_tt( ntk.make_signal( d ) ); + } + + auto const res = call_with_stopwatch( st.time_compute_function, [&]() { + return engine( target, care_transformed, std::begin( divs ), std::end( divs ), tts, std::min( potential_gain - 1, max_inserts ) ); + } ); + if ( res ) + { + ++st.num_success; + signal ret; + real_gain = potential_gain - ( *res ).num_gates(); + insert( ntk, div_signals.begin(), div_signals.end(), *res, [&]( signal const& s ) { ret = s; } ); + return ret; + } + else + { + ++st.num_fail; + return std::nullopt; + } + } + +private: + Ntk& ntk; + Simulator const& sim; + unordered_node_map tts; + std::vector const& divs; + std::vector div_signals; + stats& st; +}; /* aig_resyn_functor */ + +template +void aig_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + /* TODO: check if basetype of ntk is aig */ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + if ( ps.max_pis == 8 ) + { + using truthtable_t = kitty::static_truth_table<8u>; + using truthtable_dc_t = kitty::dynamic_truth_table; + using resub_impl_t = detail::resubstitution_impl, truthtable_dc_t>>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using resub_impl_t = detail::resubstitution_impl, truthtable_dc_t>>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +/*! \brief AIG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from aig_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void aig_resubstitution2( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not aig_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = aig_resyn_functor, truthtable_dc_t>; + + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/akers_synthesis.hpp b/third-party/mockturtle/include/mockturtle/algorithms/akers_synthesis.hpp new file mode 100644 index 00000000000..187d9242694 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/akers_synthesis.hpp @@ -0,0 +1,880 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file akers_synthesis.hpp + \brief Akers synthesis + + \author Alessandro Tempia Calvino + \author Eleonora Testa + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/* cube operators needed from Kitty : +- & (bitwise and ) +- is_subset_of +- is_const0 +*/ + +inline kitty::cube operator&( kitty::cube a, kitty::cube b ) +{ + assert( a.num_literals() == b.num_literals() ); + kitty::cube c; + for ( auto h = 0; h < a.num_literals(); h++ ) + { + c.add_literal( h, a.get_bit( h ) & b.get_bit( h ) ); + } + return c; +} + +inline bool is_subset_of( kitty::cube a, kitty::cube b ) +{ + assert( a.num_literals() == b.num_literals() ); + for ( auto h = 0; h < a.num_literals(); h++ ) + { + if ( ( a.get_bit( h ) == 1 ) && ( b.get_bit( h ) == 1 ) ) + continue; + else if ( ( a.get_bit( h ) == 1 ) && ( b.get_bit( h ) == 0 ) ) + return false; + } + return true; +} + +inline bool all_zeros( kitty::cube a ) +{ + for ( auto g = 0; g < a.num_literals(); g++ ) + { + if ( a.get_bit( g ) == 0 ) + continue; + else + return false; + } + return true; +} + +class unitized_table +{ +public: + using row_t = kitty::cube; + + unitized_table( const std::string& columns ) + : columns( columns ) + { + } + + row_t create_row() const + { + row_t r( 0, ( 1 << columns.size() ) - 1 ); + // for ( auto i = 0u; i < columns.size(); i++ ) + // r.add_literal( i, 1 ); + return r; + } + + row_t create_mask() const + { + row_t r( ( 1 << columns.size() ) - 1, ( 1 << columns.size() ) - 1 ); + // for ( auto i = 0u; i < columns.size(); i++ ) + // r.add_literal( i, 0 ); + return r; + } + + void add_row( const row_t& row ) + { + rows.push_back( row ); + } + + void reduce() + { + auto progress{ true }; + + while ( progress ) + { + progress = reduce_columns(); + progress = progress || reduce_rows(); + } + } + + friend std::ostream& operator<<( std::ostream& os, const unitized_table& table ); + + inline std::vector::const_iterator begin() const + { + return rows.begin(); + } + + inline std::vector::const_iterator end() const + { + return rows.end(); + } + + inline int operator[]( unsigned index ) const + { + return (int)(unsigned char)columns[index]; + } + + inline bool is_opposite( unsigned c1, unsigned c2 ) const + { + const auto _n1 = columns[c1]; + const auto _n2 = columns[c2]; + + const auto n1 = std::min( _n1, _n2 ); + const auto n2 = std::max( _n1, _n2 ); + + return ( n1 == 0x30 && n2 == 0x31 ) || ( ( n1 + 0x20 ) == n2 ); + } + + inline auto num_columns() const + { + return columns.size(); + } + + int add_gate( const std::set& gate ) + { + assert( gate.size() == 3u ); + + auto it = gate.begin(); + const auto c1 = *it++; + const auto c2 = *it++; + const auto c3 = *it; + + for ( auto& r : rows ) + { + const auto bi1 = r.get_bit( c1 ); + const auto bi2 = r.get_bit( c2 ); + const auto bi3 = r.get_bit( c3 ); + + r.add_literal( r.num_literals(), ( bi1 && bi2 ) || ( bi1 && bi3 ) || ( bi2 && bi3 ) ); + } + + auto ret = (int)next_gate_id; + columns.push_back( next_gate_id++ ); + return ret; + } + + unsigned count_essential_ones( bool skip_last_column = true ) const + { + auto count = 0u; + auto end = columns.size(); + if ( skip_last_column ) + { + --end; + } + + for ( auto column = 0u; column < end; ++column ) + { + std::vector one_rows; + + /* find rows with a 1 at column */ + for ( const auto& row : rows ) + { + if ( row.get_bit( column ) ) + { + auto rt = row; + rt.clear_bit( column ); + one_rows.push_back( rt ); + } + } + + /* find essential rows */ + for ( auto i = 0u; i < one_rows.size(); ++i ) + { + for ( auto j = 0u; j < one_rows.size(); ++j ) + { + if ( i == j ) + { + continue; + } + if ( all_zeros( one_rows[i] & one_rows[j] ) ) + { + ++count; /* entry i is essential in column */ + break; + } + } + } + } + + return count; + } + +private: + bool reduce_rows() + { + std::vector to_be_removed; + + for ( auto i = 0u; i < rows.size(); ++i ) + { + for ( auto j = i + 1u; j < rows.size(); ++j ) + { + if ( rows[i] == rows[j] ) + { + to_be_removed.push_back( i ); + } + else + { + if ( is_subset_of( rows[i], rows[j] ) ) + { + to_be_removed.push_back( j ); + } + if ( is_subset_of( rows[j], rows[i] ) ) + { + to_be_removed.push_back( i ); + } + } + } + } + + std::stable_sort( to_be_removed.begin(), to_be_removed.end() ); + to_be_removed.erase( std::unique( to_be_removed.begin(), to_be_removed.end() ), to_be_removed.end() ); + + std::reverse( std::begin( to_be_removed ), std::end( to_be_removed ) ); + + for ( auto index : to_be_removed ) + { + rows.erase( rows.begin() + index ); + } + return !to_be_removed.empty(); + } + + bool reduce_columns() + { + std::vector to_be_removed; + + auto mask = create_mask(); + + for ( auto c = 0u; c < columns.size(); ++c ) + { + mask.clear_bit( c ); + auto can_be_removed = true; + + /* pretend column c is removed */ + for ( auto i = 0u; i < rows.size(); ++i ) + { + for ( auto j = i + 1u; j < rows.size(); ++j ) + { + const auto result = ( rows[i] & rows[j] ) & mask; + if ( all_zeros( result ) ) + { + can_be_removed = false; + break; + } + } + + if ( !can_be_removed ) + { + break; + } + } + + if ( can_be_removed ) + { + to_be_removed.push_back( c ); + } + else + { + mask.set_bit( c ); + } + } + + /* remove columns */ + std::reverse( to_be_removed.begin(), to_be_removed.end() ); + + for ( auto index : to_be_removed ) + { + columns.erase( columns.begin() + index ); + + for ( auto& row : rows ) + { + erase_bit( row, index ); + } + } + + return !to_be_removed.empty(); + } + + void erase_bit( row_t& row, unsigned pos ) const + { + for ( auto i = pos + 1; i < unsigned( row.num_literals() ); ++i ) + { + if ( row.get_bit( i ) == 1 ) + row.set_bit( i - 1u ); + else + row.clear_bit( i - 1u ); + } + row.remove_literal( row.num_literals() - 1u ); + } + +public: + std::string columns; + + std::vector rows; + + unsigned char next_gate_id = 'z' + 0x21; +}; + +inline std::ostream& operator<<( std::ostream& os, const unitized_table& table ) +{ + os << table.columns << std::endl; + + for ( const auto& row : table.rows ) + { + std::string buffer; + std::bitset<32> to_print; + for ( auto i = 0; i < row.num_literals(); i++ ) + to_print[i] = row.get_bit( i ); + buffer = to_print.to_string(); + std::string reversed = buffer; + std::reverse( reversed.begin(), reversed.end() ); + os << reversed << std::endl; + } + + return os; +} + +inline bool operator==( unitized_table const& table, unitized_table const& original_table ) +{ + if ( table.rows.size() != original_table.rows.size() ) + return false; + for ( auto x = 0u; x < table.rows.size(); x++ ) + { + if ( table.rows[x] == original_table.rows[x] ) + continue; + else + return false; + } + + return true; +} + +namespace detail +{ +template +class akers_synthesis_impl +{ +public: + akers_synthesis_impl( Ntk ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care, LeavesIterator begin, LeavesIterator end ) + : ntk( ntk ), + func( func ), + care( care ), + begin( begin ), + end( end ) + { + } + +public: + signal run() + { + auto table = create_unitized_table(); + return synthesize( table ); + } + +private: + unitized_table create_unitized_table() + { + const unsigned int num_vars = func.num_vars(); + + /* create column names */ + std::string columns; + + for ( auto i = 0u; i < num_vars; ++i ) + { + columns += 'a' + i; + } + for ( auto i = 0u; i < num_vars; ++i ) + { + columns += 'A' + i; + } + columns += '0'; + columns += '1'; + + unitized_table table( columns ); + + /* add rows */ + for ( auto pos = 0u; pos < care.num_bits(); pos++ ) + { + auto half = std::bitset<16>( pos ); + auto row = table.create_row(); + /* copy the values */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( half[i] == 0 ) + { + row.clear_bit( i ); + row.set_bit( i + num_vars ); + } + else + { + row.set_bit( i ); + row.clear_bit( i + num_vars ); + } + } + row.clear_bit( num_vars << 1 ); + row.set_bit( ( num_vars << 1 ) + 1 ); + + if ( !kitty::get_bit( func, pos ) ) + { + for ( auto i = 0; i < row.num_literals(); ++i ) + { + if ( row.get_bit( i ) == 0 ) + { + row.set_bit( i ); + } + else + { + row.clear_bit( i ); + } + } + } + table.add_row( row ); + } + table.reduce(); + + return table; + } + + std::set> find_gates_for_column( const unitized_table& table, unsigned column ) const + { + std::vector one_rows; + std::vector matrix; + /* find rows with a 1 at column */ + + for ( const auto& row : table ) + { + if ( row.get_bit( column ) ) + { + auto rt = row; + rt.clear_bit( column ); + one_rows.push_back( rt ); + } + } + + /* find essential rows */ + for ( auto i = 0u; i < one_rows.size(); ++i ) + { + for ( auto j = 0u; j < one_rows.size(); ++j ) + { + if ( i == j ) + { + continue; + } + if ( all_zeros( one_rows[i] & one_rows[j] ) ) + { + for ( auto k = 0; k < one_rows[i].num_literals(); ++k ) + matrix.push_back( one_rows[i].get_bit( k ) ); + break; + } + } + } + return clauses_to_products_enumerative( table, column, matrix ); + } + + std::set find_gate_for_table( unitized_table& table ) + { + + std::map, unsigned> gates; + std::vector> random_gates; + auto g_count = 0u; + + for ( auto c = 0u; c < table.num_columns(); ++c ) + { + for ( const auto& g : find_gates_for_column( table, c ) ) + { + assert( g.size() == 3u ); + gates[g]++; + g_count++; + } + } + + if ( gates.empty() ) + { + reduce++; + return find_gate_for_table_brute_force( table ); + } + if ( gates.size() == previous_size ) + { + reduce++; + return find_gate_for_table_brute_force( table ); + } + + assert( !gates.empty() ); + reduce = 0; + previous_size = gates.size(); + using pair_t = decltype( gates )::value_type; + + for ( auto f = 0u; f < g_count; f++ ) + { + auto pr = std::max_element( std::begin( gates ), std::end( gates ), []( const pair_t& p1, const pair_t& p2 ) { return p1.second < p2.second; } ); + random_gates.push_back( pr->first ); + gates.erase( pr->first ); + if ( gates.size() == 0 ) + break; + } + + auto this_table = table; + for ( auto f = 0u; f < random_gates.size(); f++ ) + { + table.add_gate( random_gates[f] ); + table.reduce(); + if ( ( table.rows.size() != this_table.rows.size() ) || ( table.columns.size() != this_table.columns.size() - 1 ) ) + { + table = this_table; + return random_gates[f]; + } + table = this_table; + } + + reduce++; + return random_gates[0u]; + } + + std::set find_gate_for_table_brute_force( const unitized_table& table ) const + { + auto best_count_iter = std::numeric_limits::max(); + std::set best_gate_iter; + + std::vector numbers( table.num_columns() ); + std::iota( numbers.begin(), numbers.end(), 0u ); + + for ( auto i = 0u; i < table.num_columns(); i++ ) + { + for ( auto j = i + 1u; j < table.num_columns(); j++ ) + { + for ( auto k = j + 1u; k < table.num_columns(); k++ ) + { + std::set gate; + gate.insert( numbers[i] ); + gate.insert( numbers[j] ); + gate.insert( numbers[k] ); + + auto table_copy = table; + table_copy.add_gate( gate ); + + const auto new_count = table_copy.count_essential_ones(); + if ( new_count < best_count_iter ) + { + best_count_iter = new_count; + best_gate_iter = gate; + } + } + } + } + return best_gate_iter; + } + + signal synthesize( unitized_table& table ) + { + + std::unordered_map> c_to_f; + + c_to_f[0x30] = ntk.get_constant( false ); + c_to_f[0x31] = ntk.get_constant( true ); + + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + auto pi = *begin++; // should take the leaves values + c_to_f[0x41 + i] = !pi; + c_to_f[0x61 + i] = pi; + } + + auto last_gate_id = 0; + + while ( table.num_columns() ) + { + auto gate = find_gate_for_table( table ); + + auto it = gate.begin(); + const auto f1 = *it++; + const auto f2 = *it++; + const auto f3 = *it; + + last_gate_id = table.add_gate( gate ); + + c_to_f[last_gate_id] = ntk.create_maj( c_to_f[table[f1]], c_to_f[table[f2]], c_to_f[table[f3]] ); + + if ( reduce == 0 ) + table.reduce(); + } + + if ( ntk.node_to_index( ntk.get_node( c_to_f[last_gate_id] ) ) == 0 ) + return ntk.get_constant( 0 ^ ntk.is_complemented( c_to_f[last_gate_id] ) ); + + return c_to_f[last_gate_id]; + } + + std::vector> create_gates( const unitized_table& table ) + { + const auto num_vars = func.num_vars(); + std::vector count( table.columns.size(), 0 ); + auto best_count = 0; + + std::vector> gates; + + for ( auto c = 0u; c < table.columns.size(); ++c ) + { + for ( const auto& row : table ) + { + best_count++; + if ( row.get_bit( c ) == 0 ) + { + count[c]++; + } + } + } + + auto best_column = 0u; + + for ( auto c = 0u; c < count.size(); ++c ) + { + if ( count[c] < best_count ) + { + best_column = c; + best_count = count[c]; + } + } + + auto icx = 0; + auto name = table.columns[best_column]; + if ( islower( name ) ) + icx = name - 'a'; + else if ( isupper( name ) ) + icx = name - 'A' + num_vars; + else if ( name == '0' ) + icx = num_vars * 2; + else + icx = num_vars * 2 + 1; + + std::vector best_c; + + best_c.push_back( icx ); + gates.push_back( best_c ); + + for ( const auto& row : table ) + { + + if ( row.get_bit( best_column ) == 0 ) + { + std::vector gate1; + for ( auto c = 0u; c < table.num_columns(); ++c ) + { + if ( c == best_column ) + continue; + if ( row.get_bit( c ) == 1 ) + { + unsigned icx; + auto name = table.columns[c]; + if ( islower( name ) ) + icx = name - 'a'; + else if ( isupper( name ) ) + icx = name - 'A' + num_vars; + else if ( name == '0' ) + icx = num_vars * 2; + else + icx = num_vars * 2 + 1; + gate1.push_back( icx ); + } + } + gates.push_back( gate1 ); + } + } + std::vector g; + g.push_back( num_vars ); + + gates.push_back( g ); + return gates; + } + + std::set> clauses_to_products_enumerative( const unitized_table& table, unsigned column, + const std::vector& matrix ) const + { + std::set> products; + + const auto num_columns = table.num_columns(); + const auto num_rows = matrix.size() / num_columns; + + for ( auto i = 0u; i < num_columns; ++i ) + { + if ( table.is_opposite( column, i ) ) + { + continue; + } + if ( column == i ) + continue; + for ( auto j = i + 1u; j < num_columns; ++j ) + { + if ( table.is_opposite( i, j ) || table.is_opposite( column, j ) ) + { + continue; + } + if ( column == j ) + continue; + auto found = true; + std::size_t offset = 0u; + for ( auto r = 0u; r < num_rows; ++r, offset += num_columns ) + { + if ( !matrix[offset + i] && !matrix[offset + j] ) + { + found = false; + break; + } + } + + if ( found ) + { + std::set product; + product.insert( i ); + product.insert( j ); + product.insert( column ); + assert( product.size() == 3 ); + products.insert( product ); + } + } + } + + return products; + } + +private: + Ntk ntk; + kitty::dynamic_truth_table const& func; + kitty::dynamic_truth_table const& care; + LeavesIterator begin; + LeavesIterator end; + + unsigned reduce{ 0 }; + std::size_t previous_size{ 0 }; +}; + +} // namespace detail + +/*! \brief Performs Akers majority-3 synthesis inside network. + * + * Note that the number of variables in `func` and `care` must be the same. + * Also the distance between `begin` and `end` must equal the number of + * variables in `func`. + * + * **Required network functions:** + * - `create_maj` + * + * \param ntk Network + * \param func Function as truth table + * \param care Care set of the function (as truth table) + * \param begin Begin iterator to child signals + * \param end End iterator to child signals + * \return Signal that realizes function in terms of child signals + */ +template +signal akers_synthesis( Ntk& ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care, LeavesIterator begin, LeavesIterator end ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + + assert( func.num_vars() == care.num_vars() ); + assert( std::distance( begin, end ) == func.num_vars() ); + + auto pi = begin; + + if ( is_const0( func ) ) + return ntk.get_constant( 0 ); + auto tt_1 = unary_not( func ); + if ( is_const0( tt_1 ) ) + return ntk.get_constant( 1 ); + + for ( auto i = 0u; i < func.num_vars(); i++ ) + { + create_nth_var( tt_1, i ); + auto it = *pi++; + if ( tt_1 == func ) + { + return it; + } + tt_1 = unary_not( tt_1 ); + if ( tt_1 == func ) + { + return !it; + } + } + + detail::akers_synthesis_impl tt( ntk, func, care, begin, end ); + return tt.run(); +} + +/*! \brief Performs Akers majority-3 synthesis to create network. + * + * Note that the number of variables in `func` and `care` must be the same. + * The function will create a network with as many primary inputs as number of + * variables in `func` and a single output. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_maj` + * + * \param func Function as truth table + * \param care Care set of the function (as truth table) + * \return A network that realizes the function + */ +template +Ntk akers_synthesis( kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + + Ntk ntk; + std::vector> pis; + + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + pis.push_back( ntk.create_pi() ); + } + + const auto f = akers_synthesis( ntk, func, care, pis.begin(), pis.end() ); + ntk.create_po( f ); + return ntk; +} +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_assumptions.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_assumptions.hpp new file mode 100644 index 00000000000..b8f22e3d74e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_assumptions.hpp @@ -0,0 +1,138 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_assumptions.hpp + \brief Technology assumptions for AQFP + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +namespace mockturtle +{ + +/*! \brief More realistic AQFP technology assumptions. */ +struct aqfp_assumptions_realistic +{ + /*! \brief Whether CIs and COs need to be path-balanced. */ + bool balance_cios{ false }; + + /*! \brief Ignores the complementations of COs because they can be merged into register inputs. */ + bool ignore_co_negation{ true }; + + /*! \brief Number of phases per clock cycle (for phase alignment). + * + * Each CO (a node with external reference) must be scheduled at a level being a multiple of + * `num_phases` (i.e., an imaginary CO node should be placed at a level `num_phases * k + 1`). + */ + uint32_t num_phases{ 4u }; + + /*! \brief The maximum number of fanouts a splitter/buffer can have. */ + uint32_t splitter_capacity{ 3u }; + + /*! \brief The maximum number of fanouts a mega splitter can have. */ + //uint32_t mega_splitter_capacity{ 7u }; + + /*! \brief The maximum number of fanouts a CI can have. */ + uint32_t ci_capacity{ 1u }; // simplicity + //uint32_t ci_capacity{ 2u }; // best possible + + /*! \brief The phase offsets (after a change in register input) when new register output is available. + * + * Assumes that the register inputs (D and E) are scheduled at phase 0 (i.e., the last phase of + * the previous clock cycle), a new state is available to be taken at these numbers of phases + * afterwards. + * + * An ascending order is assumed. At least one element should be given. + * + * Each CI must be scheduled at a level `num_phases * k + ci_phases[i]` (for any `i`; for any + * integer `k >= 0` when `balance_cios = false`, or `k=0` otherwise). + */ + std::vector ci_phases{ { 4u } }; // simplicity + //std::vector ci_phases{ { 3u, 4u, 5u } }; // best possible + + /*! \brief Maximum phase-skip (in consideration of clock skew). */ + uint32_t max_phase_skip{ 4u }; +}; + +/*! \brief AQFP technology assumptions. + * + * POs count toward the fanout sizes and always have to be branched. + * If PIs need to be balanced, then they must also need to be branched. + */ +struct aqfp_assumptions_legacy +{ + /*! \brief Whether PIs need to be branched with splitters. */ + bool branch_pis{ true }; + + /*! \brief Whether PIs need to be path-balanced. */ + bool balance_pis{ false }; + + /*! \brief Whether POs need to be path-balanced. */ + bool balance_pos{ false }; + + /*! \brief The maximum number of fanouts each splitter (buffer) can have. */ + uint32_t splitter_capacity{ 3u }; +}; + +using aqfp_assumptions = aqfp_assumptions_legacy; + +/* Temporary helper function to bridge old and new code. */ +inline aqfp_assumptions_realistic legacy_to_realistic( aqfp_assumptions_legacy const& legacy ) +{ + aqfp_assumptions_realistic realistic; + + if ( !legacy.branch_pis ) + { + realistic.ci_capacity = std::numeric_limits::max(); + } + else + { + realistic.ci_capacity = 1u; + } + + if ( legacy.balance_pis && legacy.balance_pos ) + { + realistic.balance_cios = true; + } + else if ( !legacy.balance_pis && !legacy.balance_pos ) + { + realistic.balance_cios = false; + } + else + { + std::cerr << "[e] Cannot convert this combinaiton of assumptions.\n"; + } + + realistic.splitter_capacity = legacy.splitter_capacity; + realistic.num_phases = 1u; // no phase alignment + realistic.ci_phases = {0u}; // PIs at level 0 + realistic.max_phase_skip = std::numeric_limits::max(); // no clock skew issue + return realistic; +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_cleanup.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_cleanup.hpp new file mode 100644 index 00000000000..c269b3f4f04 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_cleanup.hpp @@ -0,0 +1,160 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_cleanup.hpp + \brief Buffered network cleanup and types conversion + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include "../../networks/buffered.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Buffered cleanup dangling. + * + * This function implements `cleanup_dangling` for buffered networks. + * + * \param ntk buffered network type + */ +template +Ntk cleanup_dangling_buffered( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( is_buffered_network_type_v, "Ntk is not a buffered network type" ); + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + static_assert( has_create_buf_v, "Ntk does not implement the create_buf method" ); + + using signal = typename Ntk::signal; + + Ntk res; + node_map old2new( ntk ); + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + old2new[ntk.get_constant( true )] = res.get_constant( true ); + } + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ ntk.is_complemented( f ) ); + } ); + + signal f; + if ( ntk.is_buf( n ) ) + { + f = res.create_buf( children[0] ); + } + else + { + f = res.clone_node( ntk, n, children ); + } + + old2new[n] = f; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + + return res; +} + +/*! \brief Converts a `buffered_mig_network` to a `buffered_aqfp_network`. + * + * This function converts a `buffered_mig_network` to a `buffered_aqfp_network`. + * + * \param ntk `buffered_mig_network` + */ +buffered_aqfp_network convert_buffered_mig_to_aqfp( buffered_mig_network const& ntk ) +{ + using signal = typename buffered_aqfp_network::signal; + + buffered_aqfp_network res; + node_map old2new( ntk ); + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ ntk.is_complemented( f ) ); + } ); + + signal f; + if ( ntk.is_buf( n ) ) + { + f = res.create_buf( children[0] ); + } + else + { + f = res.create_maj( children[0], children[1], children[2] ); + } + + old2new[n] = f; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_db.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_db.hpp new file mode 100644 index 00000000000..301800ee535 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_db.hpp @@ -0,0 +1,450 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_db.hpp + \brief AQFP DAG database + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "detail/dag.hpp" +#include "detail/dag_cost.hpp" +#include "detail/npn_cache.hpp" + +namespace mockturtle +{ + +template +T bitwise_majority( const T& a, const T& b, const T& c ) +{ + return ( a & b ) | ( c & ( a | b ) ); +} + +template +T bitwise_majority( const T& a, const T& b, const T& c, const T& d, const T& e ) +{ + return ( a & b & c ) | ( a & b & d ) | ( a & b & e ) | ( a & c & d ) | ( a & c & e ) | + ( a & d & e ) | ( b & c & d ) | ( b & c & e ) | ( b & d & e ) | ( c & d & e ); +} + +/*! \brief Returns the level of input with index `input_idx` from level configuration `lvl_cfg`. */ +inline uint32_t level_of_input( uint64_t lvl_cfg, uint32_t input_idx ) +{ + return ( lvl_cfg >> ( 8u * input_idx ) ) & 0xff; +} + +/*! \brief Returns the vector representation of the level configuration `lvl_cfg`. */ +inline std::vector lvl_cfg_to_vec( uint64_t lvl_cfg, uint32_t num_leaves ) +{ + std::vector res( num_leaves ); + for ( auto i = 0u; i < num_leaves; i++ ) + { + res[i] = level_of_input( lvl_cfg, i ); + } + return res; +} + +/*! \brief Returns the level configuration for levels represented by `levels`. */ +inline uint64_t lvl_cfg_from_vec( std::vector levels ) +{ + uint64_t res = 0u; + for ( auto i = 0u; i < levels.size(); i++ ) + { + res |= ( levels[i] << ( 8u * i ) ); + } + return res; +} + +/*! \brief A class to represent an AQFP exact synthesis database. */ +template> +class aqfp_db +{ + +public: + struct replacement + { + double cost; + Ntk ntk; + std::vector input_levels; // input levels + std::vector input_perm; // input permutation so that ntk compute the npn-class + }; + + aqfp_db( + const std::unordered_map& gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }, + const std::unordered_map& splitters = { { 1u, 2.0 }, { 4u, 2.0 } } ) + : gate_costs( gate_costs ), splitters( splitters ), db( get_default_db() ), cc( gate_costs, splitters ) + { + } + + aqfp_db( const std::unordered_map>& db, + const std::unordered_map& gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }, + const std::unordered_map& splitters = { { 1u, 2.0 }, { 4u, 2.0 } } ) + : gate_costs( gate_costs ), splitters( splitters ), db( db ), cc( gate_costs, splitters ) + { + } + + using gate_info = std::vector; // fanin list with lsb denoting the inversion + using mig_structure = std::tuple, std::vector, bool>; // (gates, levels, output inverted flag); + + template + mig_structure get_best_replacement( uint64_t f, std::vector _levels, std::vector _is_const, ComparisonFn&& comparison_fn ) + { + /* find the npn class for the function */ + auto tmp = npndb( f ); + auto& npntt = std::get<0>( tmp ); + auto& npnperm = std::get<2>( tmp ); + + if ( db[npntt].empty() ) + { + assert( false ); + return { {}, {}, false }; + } + + /* map input levels */ + std::vector levels( _levels.size() ); + std::vector is_const( _levels.size() ); + for ( auto i = 0u; i < levels.size(); i++ ) + { + levels[i] = _levels[npnperm[i]]; + is_const[i] = _is_const[npnperm[i]]; + } + + double best_cost = std::numeric_limits::infinity(); + uint32_t best_lev = std::numeric_limits::max(); + replacement best = db[npntt].begin()->second; + uint32_t best_ind = 0; + + uint32_t temp_ind = 0; + for ( auto it = db[npntt].begin(); it != db[npntt].end(); it++ ) + { + const auto& lvl_cfg = it->first; + const auto& r = it->second; + + uint32_t max_lev = 0u; + for ( auto i = 0u; i < levels.size(); i++ ) + { + auto temp = levels[i] + level_of_input( lvl_cfg, i ); + max_lev = std::max( max_lev, temp ); + } + uint32_t buffer_count = 0u; + for ( auto i = 0u; i < levels.size(); i++ ) + { + if ( !is_const[i] ) + { + auto temp = levels[i] + level_of_input( lvl_cfg, i ); + buffer_count += ( max_lev - temp ); + } + } + + double cost = buffer_count * splitters.at( 1u ) + r.cost; + + if ( comparison_fn( { cost, max_lev }, { best_cost, best_lev } ) ) + { + best_cost = cost; + best_lev = max_lev; + best = r; + best_ind = temp_ind; + } + temp_ind++; + } + + usage_stats[{ npntt, best_ind }]++; + return compute_replacement_structure( best, f ); + } + + /*! \brief Load database from input stream `is`. */ + void load_db( std::istream& is, uint32_t version = 1u ) + { + load_db( is, db, version ); + } + + /*! \brief Load database from input stream `is`. */ + template + static void load_db( std::istream& is, T& db, uint32_t version = 1u ) + { + std::string line; + + std::getline( is, line ); + uint32_t num_func = std::stoul( line ); + + for ( auto func = 0u; func < num_func; func++ ) + { + uint32_t N = 4; + if ( version > 1u ) + { + std::getline( is, line ); + N = std::stoul( line ); + } + + std::getline( is, line ); + uint64_t npn = std::stoul( line, 0, 16 ); + + std::getline( is, line ); + uint32_t num_entries = std::stoul( line ); + + for ( auto j = 0u; j < num_entries; j++ ) + { + std::getline( is, line ); + uint64_t lvl_cfg = std::stoul( line, 0, 16 ); + auto levels = lvl_cfg_to_vec( lvl_cfg, N ); + + std::getline( is, line ); + double cost = std::stod( line ); + + std::getline( is, line ); + Ntk ntk( line ); + + std::vector perm( N ); + for ( int i = 0; i < N; i++ ) + { + uint32_t t; + is >> t; + perm[i] = t; + } + std::getline( is, line ); // ignore the current line end + + lvl_cfg = lvl_cfg_from_vec( levels ); + + if ( !db[npn].count( lvl_cfg ) || db[npn][lvl_cfg].cost > cost ) + { + db[npn][lvl_cfg] = { cost, ntk, levels, perm }; + } + } + } + } + + void print_usage_state( std::ostream& os ) + { + os << "printing stats\n"; + for ( auto& x : usage_stats ) + { + os << fmt::format( "{}, {}, {}\n", x.first.first, x.first.second, x.second ); + } + os << "printing stats done\n"; + } + + template + void for_each_db_entry( Fn&& func ) + { + for ( const auto& [npn_class, entries_for_npn_class] : db ) + { + for ( const auto& [depth_config, replacement] : entries_for_npn_class ) + { + func( npn_class, compute_replacement_structure( replacement, npn_class ), replacement.cost ); + } + } + } + +private: + std::unordered_map gate_costs; + std::unordered_map splitters; + std::unordered_map> db; + std::map, uint32_t> usage_stats; + dag_aqfp_cost_and_depths cc; + npn_cache npndb; + + std::pair> inverter_config_for_func( const std::vector& input_tt, const Ntk& net, uint64_t func ) + { + uint32_t num_inputs = net.input_slots.size(); + if ( net.zero_input != 0 ) + { + num_inputs--; + } + + std::vector tt( net.nodes.size(), input_tt[0] ); + auto input_ind = 1u; + + auto tmp_input_slots = net.input_slots; + std::stable_sort( tmp_input_slots.begin(), tmp_input_slots.end() ); + assert( tmp_input_slots == net.input_slots ); + + for ( auto i : net.input_slots ) + { + if ( i != (int)net.zero_input ) + { + tt[i] = input_tt[input_ind++]; + } + } + + auto shift = 0u; + for ( auto i = 0u; i < net.num_gates(); i++ ) + { + shift += ( net.nodes[i].size() - 1 ); + } + + const auto n_gates = net.num_gates(); + std::vector res( n_gates ); + + for ( auto inv_config_itr = 0ul; inv_config_itr < ( 1ul << shift ); inv_config_itr++ ) + { + auto inv_config = inv_config_itr; + + for ( auto i = n_gates; i > 0; i-- ) + { + const auto& n = net.nodes[i - 1]; + + const auto n_fanin = n.size(); + const auto shift = n_fanin - 1; + const auto mask = ( 1 << shift ) - 1; + const auto ith_gate_config = ( inv_config & mask ); + + res[i - 1] = ith_gate_config; + + // only consider half the inverter configurations, the other half is covered by output inversion + if ( n_fanin == 3u ) + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + tt[n[2]] ); + } + else + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + ( ith_gate_config & 4 ) ? ~tt[n[2]] : tt[n[2]], + ( ith_gate_config & 8 ) ? ~tt[n[3]] : tt[n[3]], + tt[n[4]] ); + } + inv_config >>= shift; + } + + if ( ( func & 0xffff ) == ( tt[0] & 0xffff ) ) + { + return { false, res }; + } + if ( ( ~func & 0xffff ) == ( tt[0] & 0xffff ) ) + { + return { true, res }; + } + } + + assert( false ); + return {}; + } + + mig_structure compute_replacement_structure( const replacement& rep, uint64_t func ) + { + std::vector levs( 4u ); + for ( auto i = 0u; i < 4u; i++ ) + { + levs[i] = rep.input_levels[rep.input_perm[i]]; + } + + auto [new_cost, gate_levels] = cc( rep.ntk, levs ); + (void)new_cost; + + auto [npntt, npn_inv, npnperm] = npndb( func ); + + std::vector ind = { 0u, 1u, 2u, 3u }; + + std::vector ind_func_from_npn = { + ind[npnperm[0]], + ind[npnperm[1]], + ind[npnperm[2]], + ind[npnperm[3]] }; + + std::vector ind_func_from_dag = { + ind_func_from_npn[rep.input_perm[0]], + ind_func_from_npn[rep.input_perm[1]], + ind_func_from_npn[rep.input_perm[2]], + ind_func_from_npn[rep.input_perm[3]] }; + + auto input_perm = ind_func_from_dag; + + std::vector input_tt = { + 0xaaaaUL, + 0xccccUL, + 0xf0f0UL, + 0xff00UL }; + + std::vector input_perm_tt = { + 0x0000UL, + input_tt[input_perm[0]], + input_tt[input_perm[1]], + input_tt[input_perm[2]], + input_tt[input_perm[3]] }; + + auto [output_inv, inverter_config] = inverter_config_for_func( input_perm_tt, rep.ntk, func ); + + std::vector gates{ {}, {}, {}, {}, {} }; + std::vector depths{ 0u, 0u, 0u, 0u, 0u }; + + std::map sigmap; + + std::vector inputs; + auto i = 0u; + for ( auto x : rep.ntk.input_slots ) + { + if ( x == rep.ntk.zero_input ) + { + sigmap[x] = 0u; + } + else + { + sigmap[x] = input_perm[i++] + 1; + } + depths[sigmap[x]] = gate_levels[x]; + } + + for ( auto i = rep.ntk.num_gates(); i > 0; i-- ) + { + auto j = i - 1; + sigmap[j] = rep.ntk.num_gates() + 4u - j; + + gates.push_back( {} ); + assert( gates.size() == sigmap[j] + 1 ); + depths.push_back( gate_levels[j] ); + + auto type = inverter_config[j]; + auto& node = rep.ntk.nodes[j]; + for ( auto k = 0u; k < node.size(); k++ ) + { + auto new_fanin_id = sigmap[node[k]]; + auto new_fanin_inv = ( type & ( 1u << k ) ) > 0; + gates[sigmap[j]].push_back( ( new_fanin_id << 1 ) | ( new_fanin_inv ? 1u : 0u ) ); + } + } + + return { gates, depths, output_inv }; + } + + static std::unordered_map> get_default_db() + { + return {}; + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp new file mode 100644 index 00000000000..d9b41d24e4c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp @@ -0,0 +1,172 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_fanout_resyn.hpp + \brief AQFP fanout resynthesis strategy + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include "../../traits.hpp" +#include "aqfp_assumptions.hpp" + +namespace mockturtle +{ + +/*! \brief A callback to determine the fanout levels. + * + * This is intended to be used with AQFP networks. Levels are determined assuming that + * a nearly-balanced splitter tree is used for each the considered fanout net. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aqfp_assumptions assume = { false, false, true, 4u }; + aqfp_fanout_resyn fanout_resyn{ assume }; + + std::unordered_map gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }; + std::unordered_map splitters = { { 1u, 2.0 }, { assume.splitter_capacity, 2.0 } }; + aqfp_node_resyn_param ps{ assume, splitters, aqfp_node_resyn_strategy::delay }; + + aqfp_db<> db( gate_costs, splitters ); + db.load_db( ... ); // from an input-stream (e.g., std::ifstream or std::stringstream) + + aqfp_node_resyn node_resyn( db, ps ); + + klut_network src_ntk = ...; + aqfp_network dst_ntk; + auto res = aqfp_resynthesis( dst_ntk, src_ntk, node_resyn, fanout_resyn ); + + \endverbatim + */ +struct aqfp_fanout_resyn +{ + /*! \brief Default constructor. + * + * \param assume AQFP synthesis assumptions (only the fields `splitter_capacity` and `branch_pis` will be used) + */ + aqfp_fanout_resyn( const aqfp_assumptions& assume ) + : splitter_capacity{ assume.splitter_capacity }, branch_pis{ assume.branch_pis } {} + + /*! \brief Determine the relative levels of fanouts of a node assuming a nearly balanced splitter tree. + * + * \param ntk_src Source network with `depth()` and `level()` member functions. + * \param n Node in `ntk_src` for which the fanout levels are to be determined. + * \param fanouts_n Fanout nodes of `n` in `ntk_dest`. + * \param ntk_dest Destination network which is being synthesized as an AQFP network. + * \param f The signal in `ntk_dest` that correspond to source node `n`. + * \param level_f The level of `f` in `ntk_dest`. + * \param fanout_node_fn Callback with arguments (source network node, level in destination network). + * \param fanout_co_fn Callback with arguments (index of the combinational output, level in destination network). + */ + template + void operator()( const NtkSrc& ntk_src, node n, FOutsSrc& fanouts_n, const NtkDest& ntk_dest, signal f, uint32_t level_f, + FanoutNodeCallback&& fanout_node_fn, FanoutPoCallback&& fanout_co_fn ) + { + static_assert( has_depth_v, + "The source network does not provide a method named depth()." ); + static_assert( has_level_v, + "The source network does not provide a method named level(node)." ); + static_assert( std::is_invocable_v, uint32_t>, + "FanoutNodeCallback is not callable with arguments (node, level)" ); + static_assert( std::is_invocable_v, + "FanoutNodeCallback is not callable with arguments (index, level)" ); + + if ( ntk_src.fanout_size( n ) == 0 ) + return; + + auto offsets = balanced_splitter_tree_offsets( ntk_src.fanout_size( n ) ); + + std::stable_sort( fanouts_n.begin(), fanouts_n.end(), [&]( auto f1, auto f2 ) { return ( ntk_src.depth() - ntk_src.level( f1 ) ) > ( ntk_src.depth() - ntk_src.level( f2 ) ) || + ( ( ntk_src.depth() - ntk_src.level( f1 ) ) == ( ntk_src.depth() - ntk_src.level( f2 ) ) && ( f1 < f2 ) ); } ); + + auto n_dest = ntk_dest.get_node( f ); + auto no_splitters = ntk_dest.is_constant( n_dest ) || ( !branch_pis && ntk_dest.is_ci( n_dest ) ); + + uint32_t foind = 0u; + for ( auto fo : fanouts_n ) + { + fanout_node_fn( fo, no_splitters ? level_f : level_f + offsets[foind] ); + foind++; + } + + // remaining fanouts are either combinational outputs (primary outputs or register inputs) + for ( auto i = foind; i < ntk_src.fanout_size( n ); i++ ) + { + auto co_index = i - foind; + fanout_co_fn( co_index, no_splitters ? level_f : level_f + offsets[foind] ); + foind++; + } + } + +private: + uint32_t splitter_capacity; + bool branch_pis; + + /*! \brief Determines the relative levels of the fanouts of a balanced splitter tree with `num_fanouts` many fanouts. */ + std::vector balanced_splitter_tree_offsets( uint32_t num_fanouts ) + { + if ( num_fanouts == 1u ) + { + return { 0u }; + } + + // to get the minimum level, choose the splitters with max fanout size + + uint32_t num_splitters = 1u; + uint32_t num_levels = 1u; + uint32_t num_leaves = splitter_capacity; + + while ( num_leaves < num_fanouts ) + { + num_splitters += num_leaves; + num_leaves *= splitter_capacity; + num_levels += 1u; + } + + // we need at least `num_levels` levels, but we might not need all + std::vector result( num_fanouts, num_levels ); + uint32_t i = 0u; + while ( num_leaves >= num_fanouts + ( splitter_capacity - 1 ) ) + { + num_splitters--; + num_leaves -= ( splitter_capacity - 1 ); + result[i++]--; + } + + return result; + }; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp new file mode 100644 index 00000000000..9de592070de --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp @@ -0,0 +1,331 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_legalization.hpp + \brief Legalization + buffer optimization flow for AQFP networks + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../networks/aqfp.hpp" +#include "../../networks/buffered.hpp" +#include "../../networks/mig.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/depth_view.hpp" +#include "../cleanup.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_rebuild.hpp" +#include "aqfp_retiming.hpp" +#include "buffer_insertion.hpp" + +namespace mockturtle +{ + +struct aqfp_legalization_params +{ + aqfp_legalization_params() + { + aqfp_assumptions_ps.branch_pis = true; + aqfp_assumptions_ps.balance_pis = true; + aqfp_assumptions_ps.balance_pos = true; + } + + /*! \brief legalization mode. */ + enum + { + better, + portfolio + } legalization_mode = portfolio; + + /*! \brief AQFP technology assumptions. */ + aqfp_assumptions_legacy aqfp_assumptions_ps{}; + + /*! \brief Max number of optimization rounds (zero performs only insertion)*/ + uint32_t optimization_rounds{ 10 }; + + /*! \brief Maximum chunk size for chunk optimization. */ + uint32_t max_chunk_size{ 100 }; + + /*! \brief Maximum number of iterations for optimization using retiming. */ + uint32_t retime_iterations{ 250 }; + + /*! \brief Enable optimization of splitters using retiming. */ + bool retime_splitters{ true }; + + /*! \brief Enables the randomization of topological order. */ + bool topological_randomization{ true }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct aqfp_legalization_stats +{ + /*! \brief Number of Josephson Junctions (JJs). */ + uint32_t num_jjs{ 0 }; + + /*! \brief Number of buffers and splitters. */ + uint32_t num_bufs{ 0 }; + + /*! \brief Depth of the circuit. */ + uint32_t depth{ 0 }; + + /*! \brief Total number of optimization rounds. */ + uint32_t rounds_total{ 0 }; + + /*! \brief Time insertion. */ + stopwatch<>::duration time_insertion{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] JJs = {:7d}\t B/S = {:7d}\t Depth = {:5d}\n", num_jjs, num_bufs, depth ); + std::cout << fmt::format( "[i] Rounds = {:7d}\t Time Insertion = {:>5.2f} secs\t Total Runtime = {:>5.2f} secs\n", rounds_total, to_seconds( time_insertion ), to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class aqfp_legalization_impl +{ +public: + explicit aqfp_legalization_impl( Ntk const& ntk, aqfp_legalization_params const& ps, aqfp_legalization_stats& st ) + : _ntk( ntk ), _ps( ps ), _st( st ) + { + } + +public: + buffered_aqfp_network run() + { + stopwatch t( _st.time_total ); + _st.rounds_total = 0; + + /* convert the initial circuit as an AQFP network */ + aqfp_network aqfp_start = cleanup_dangling( _ntk ); + /* creates buffer_insertion instance for scheduling (can be reused) */ + buffer_insertion_params insertion_ps; + insertion_ps.optimization_effort = buffer_insertion_params::none; + insertion_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + buffer_insertion buf_inst( aqfp_start, insertion_ps ); + + bool retiming_backward_first = false; + + /* Starndard flow: insertion + optimization */ + if ( _ps.legalization_mode == aqfp_legalization_params::better ) + { + buffered_aqfp_network buffered_aqfp = aqfp_buffer_insertion( buf_inst, retiming_backward_first ); + buffered_aqfp_network aqfp_res = aqfp_buffer_optimize( buffered_aqfp, retiming_backward_first ); + compute_stats( aqfp_res ); + return aqfp_res; + } + + /* Portfolio flow: 2 insertions + 2 optimizations */ + buffered_aqfp_network buffered_aqfp_alap = aqfp_buffer_insertion( buf_inst, retiming_backward_first, true ); + buffered_aqfp_network aqfp_res_alap = aqfp_buffer_optimize( buffered_aqfp_alap, retiming_backward_first ); + + buffered_aqfp_network buffered_aqfp_asap = aqfp_buffer_insertion( buf_inst, retiming_backward_first, false ); + buffered_aqfp_network aqfp_res_asap = aqfp_buffer_optimize( buffered_aqfp_asap, retiming_backward_first ); + + buffered_aqfp_network aqfp_res; + if ( aqfp_res_alap.size() < aqfp_res_asap.size() ) + { + aqfp_res = aqfp_res_alap; + } + else + { + aqfp_res = aqfp_res_asap; + } + + compute_stats( aqfp_res ); + return aqfp_res; + } + +private: + buffered_aqfp_network aqfp_buffer_insertion( buffer_insertion& buf_inst, bool& direction, bool is_alap = false ) + { + stopwatch t( _st.time_insertion ); + + if ( _ps.legalization_mode == aqfp_legalization_params::portfolio ) + { + if ( is_alap ) + { + buf_inst.set_scheduling_policy( buffer_insertion_params::ALAP_depth ); + } + else + { + buf_inst.set_scheduling_policy( buffer_insertion_params::ASAP_depth ); + } + } + else if ( _ps.legalization_mode == aqfp_legalization_params::better ) + { + buf_inst.set_scheduling_policy( buffer_insertion_params::better_depth ); + } + + buffered_aqfp_network buffered_aqfp; + buf_inst.run( buffered_aqfp ); + direction = buf_inst.is_scheduled_ASAP(); + + return buffered_aqfp; + } + + buffered_aqfp_network aqfp_buffer_optimize( buffered_aqfp_network& start, bool direction ) + { + if ( _ps.optimization_rounds == 0 ) + return start; + + /* retiming params */ + aqfp_retiming_params aps; + aps.aqfp_assumptions_ps = _ps.aqfp_assumptions_ps; + aps.backwards_first = direction; + aps.iterations = _ps.retime_iterations; + aps.retime_splitters = _ps.retime_splitters; + + /* chunk movement params */ + buffer_insertion_params buf_ps; + buf_ps.scheduling = buffer_insertion_params::provided; + buf_ps.optimization_effort = buffer_insertion_params::until_sat; + buf_ps.max_chunk_size = _ps.max_chunk_size; + buf_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + + aqfp_reconstruct_params reconstruct_ps; + aqfp_reconstruct_stats reconstruct_st; + reconstruct_ps.buffer_insertion_ps = buf_ps; + + /* aqfp network */ + buffered_aqfp_network buffered_aqfp; + + /* first retiming */ + { + auto buf_aqfp_ret = aqfp_retiming( start, aps ); + buffered_aqfp = buf_aqfp_ret; + } + + /* repeat loop */ + uint32_t iterations = _ps.optimization_rounds; + aps.det_randomization = _ps.topological_randomization; + std::default_random_engine rng( 111 ); + while ( iterations-- > 0 ) + { + uint32_t size_previous = buffered_aqfp.size(); + + /* chunk movement */ + auto buf_aqfp_chunk = aqfp_reconstruct( buffered_aqfp, reconstruct_ps, &reconstruct_st ); + + /* retiming */ + aps.seed = rng(); + auto buf_aqfp_ret = aqfp_retiming( buf_aqfp_chunk, aps ); + + _st.rounds_total++; + + if ( buf_aqfp_ret.size() >= size_previous ) + break; + + buffered_aqfp = buf_aqfp_ret; + } + + return buffered_aqfp; + } + + void compute_stats( buffered_aqfp_network const& buffered_aqfp ) + { + _st.depth = depth_view( buffered_aqfp ).depth(); + _st.num_bufs = 0; + _st.num_jjs = 0; + + buffered_aqfp.foreach_node( [&]( auto const& n ) { + if ( buffered_aqfp.is_pi( n ) || buffered_aqfp.is_constant( n ) ) + return; + if ( buffered_aqfp.is_buf( n ) ) + { + _st.num_jjs += 2; + _st.num_bufs++; + } + else + { + _st.num_jjs += 6; + } + } ); + } + +private: + Ntk const& _ntk; + aqfp_legalization_params const& _ps; + aqfp_legalization_stats& _st; +}; + +} /* namespace detail */ + +/*! \brief AQFP legalization. + * + * This function returns an optimized AQFP circuit + * derived from the input one by inserting buffer + * and splitter elements and optimizing their number. + * + * Parameters can be used to set the B/S insertion and + * optimization. + * + * \param ntk Boolean network as an MIG or AQFP network + * \param ps AQFP legalization parameters + */ +template +buffered_aqfp_network aqfp_legalization( Ntk const& ntk, aqfp_legalization_params const& ps = {}, aqfp_legalization_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( std::is_same_v || std::is_same_v, "Ntk in not an MIG or AQFP network type" ); + + aqfp_legalization_stats st; + + detail::aqfp_legalization_impl p( ntk, ps, st ); + auto res = p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + *pst = st; + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp new file mode 100644 index 00000000000..4cfe6f38788 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp @@ -0,0 +1,253 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_node_resyn.hpp + \brief AQFP node resynthesis strategy + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include "../../traits.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_db.hpp" + +namespace mockturtle +{ + +/*! \brief Strategy for resynthesizing a node. */ +enum class aqfp_node_resyn_strategy +{ + area, /*!< choose the database entry that gives the minimum area and break ties uing the delay */ + delay /*!< choose the database entry that gives the minimum delay and break ties uing the cost */ +}; + +/*! \brief AQFP node re-synthesis parameters. */ +struct aqfp_node_resyn_param +{ + aqfp_assumptions assume = { false, false, true, 4u }; + std::unordered_map splitters = { { 1u, 2.0 }, { 4u, 2.0 } }; + aqfp_node_resyn_strategy strategy = aqfp_node_resyn_strategy::area; +}; + +/*! \brief A callback to re-synthesize a node in an AQFP network. + * + * A suitable AQFP sub-graph structure for a given node will be chosen from + * a database of AQFP structures. + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aqfp_assumptions assume = { false, false, true, 4u }; + aqfp_fanout_resyn fanout_resyn{ assume }; + + std::unordered_map gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }; + std::unordered_map splitters = { { 1u, 2.0 }, { assume.splitter_capacity, 2.0 } }; + aqfp_node_resyn_param ps{ assume, splitters, aqfp_node_resyn_strategy::delay }; + + aqfp_db<> db( gate_costs, splitters ); + db.load_db( ... ); // from an input-stream (e.g., std::ifstream or std::stringstream) + + aqfp_node_resyn node_resyn( db, ps ); + + klut_network src_ntk = ...; + aqfp_network dst_ntk; + auto res = aqfp_resynthesis( dst_ntk, src_ntk, node_resyn, fanout_resyn ); + + \endverbatim + */ +struct aqfp_node_resyn +{ + + /*! \brief Default constructor. + * + * \param db AQFP database. + * \param ps AQFP note re-synthesis parameters. + */ + aqfp_node_resyn( aqfp_db<>& db, const aqfp_node_resyn_param& ps ) : params( ps ), db( db ) + { + /*! must provide the cost of a simple buffer with one output */ + assert( ps.splitters.count( 1u ) > 0 ); + /*! must provide the cost of a splitter with the maximum capacity */ + assert( ps.splitters.count( ps.assume.splitter_capacity ) > 0 ); + } + + /*! Re-synthesize a given node as a sub-graph in an AQFP network. + * + * \param ntk_dest AQFP network that is being synthesized. + * \param f Function computed by the source node that is being re-synthesized. + * \param leaves_begin Iterator (begin) for the leaves of the source node. + * \param leaves_end Iterator (end) for the leaves of the source node. + * \param level_update_callback Callback with parameters (new node, level of the new node). + * \param resyn_performed_callback Callback with the signal that correspond to the source node and its level. + */ + template + void operator()( NtkDest& ntk_dest, const TruthTable& f, LeavesIterator leaves_begin, LeavesIterator leaves_end, + LevelUpdateCallback&& level_update_callback, ResynPerformedCallback&& resyn_performed_callback ) + { + static_assert( std::is_invocable_v, uint32_t>, + "LevelUpdateCallback must be callable with arguments of types (node, level)" ); + static_assert( std::is_invocable_v, uint32_t>, + "ResynPerformedCallback must be callable with arguments of type (signal, level)" ); + + std::vector> leaves; + std::vector leaf_levels; + std::vector leaf_no_splitters; + for ( auto it = leaves_begin; it != leaves_end; it++ ) + { + auto leaf = std::get<0>( *it ); + auto leaf_level = std::get<1>( *it ); + + leaves.push_back( leaf ); + leaf_levels.push_back( leaf_level ); + leaf_no_splitters.push_back( ntk_dest.is_constant( ntk_dest.get_node( leaf ) ) || ( ntk_dest.is_ci( ntk_dest.get_node( leaf ) ) && !params.assume.branch_pis ) ); + } + + // should not have more than 4 fanin nodes + assert( leaves.size() <= 4u ); + + // if less than 4 fanins, add dummy inputs + while ( leaves.size() < 4u ) + { + leaves.push_back( ntk_dest.get_constant( false ) ); + leaf_levels.push_back( 0u ); + leaf_no_splitters.push_back( true ); + } + + auto tt = kitty::extend_to( f, 4u ); + + auto new_n = ntk_dest.get_constant( false ); + auto n_lev = 0u; + switch ( tt._bits[0] ) + { + case 0x0000u: + new_n = ntk_dest.get_constant( false ); + break; + case 0xffffu: + new_n = ntk_dest.get_constant( true ); + break; + case 0x5555u: + new_n = !leaves[0]; + n_lev = leaf_levels[0]; + break; + case 0xaaaau: + new_n = leaves[0]; + n_lev = leaf_levels[0]; + break; + case 0x3333u: + new_n = !leaves[1]; + n_lev = leaf_levels[1]; + break; + case 0xccccu: + new_n = leaves[1]; + n_lev = leaf_levels[1]; + break; + case 0x0f0fu: + new_n = !leaves[2]; + n_lev = leaf_levels[2]; + break; + case 0xf0f0u: + new_n = leaves[2]; + n_lev = leaf_levels[2]; + break; + case 0x00ffu: + new_n = !leaves[3]; + n_lev = leaf_levels[3]; + break; + case 0xff00u: + new_n = leaves[3]; + n_lev = leaf_levels[3]; + break; + default: + auto [mig, depths, output_inv] = db.get_best_replacement( + tt._bits[0], leaf_levels, leaf_no_splitters, + [&]( const std::pair& f, const std::pair& s ) { + if ( params.strategy == aqfp_node_resyn_strategy::area ) + { + return ( f.first < s.first || ( f.first == s.first && f.second < s.second ) ); + } + else + { + assert( params.strategy == aqfp_node_resyn_strategy::delay ); + return ( f.second < s.second || ( f.second == s.second && f.first < s.first ) ); + } + } ); + + std::vector> sig_map( mig.size() ); + std::vector lev_map( mig.size() ); + + sig_map[0] = ntk_dest.get_constant( false ); + lev_map[0] = 0u; + for ( auto i = 1u; i <= 4u; i++ ) + { + sig_map[i] = leaves[i - 1]; + lev_map[i] = leaf_levels[i - 1]; + } + for ( auto i = 5u; i < mig.size(); i++ ) + { + std::vector> fanin; + for ( auto fin : mig[i] ) + { + const auto fin_inv = ( ( fin & 1u ) == 1u ); + const auto fin_id = ( fin >> 1 ); + fanin.push_back( fin_inv ? !sig_map[fin_id] : sig_map[fin_id] ); + } + sig_map[i] = ntk_dest.create_maj( fanin ); + lev_map[i] = 0u; + + const auto node_i = ntk_dest.get_node( sig_map[i] ); + if ( !( ntk_dest.is_constant( node_i ) || ntk_dest.is_ci( node_i ) ) ) + { + for ( auto fin : mig[i] ) + { + const auto fin_id = ( fin >> 1u ); + const auto lev_dif = ( depths[fin_id] > depths[i] ) ? depths[fin_id] - depths[i] : 1u; + lev_map[i] = std::max( lev_map[i], lev_map[fin_id] + lev_dif ); + } + } + + level_update_callback( ntk_dest.get_node( sig_map[i] ), lev_map[i] ); + } + n_lev = lev_map[mig.size() - 1]; + new_n = output_inv ? !sig_map[mig.size() - 1] : sig_map[mig.size() - 1]; + } + + resyn_performed_callback( new_n, n_lev ); + } + +private: + aqfp_node_resyn_param params; + aqfp_db<>& db; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_rebuild.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_rebuild.hpp new file mode 100644 index 00000000000..880d7c22bda --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_rebuild.hpp @@ -0,0 +1,280 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_rebuild.hpp + \brief Rebuilds buffer-splitter tree in AQFP networks + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../../networks/buffered.hpp" +#include "../../networks/generic.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/depth_view.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_cleanup.hpp" +#include "buffer_insertion.hpp" +#include "buffer_verification.hpp" + +namespace mockturtle +{ + +struct aqfp_reconstruct_params +{ + /*! \brief AQFP buffer insertion parameters. */ + buffer_insertion_params buffer_insertion_ps{}; + + /*! \brief Randomize topological order. */ + bool det_randomization{ false }; + + /*! \brief Seed for random selection of splitters to relocate. */ + std::default_random_engine::result_type seed{ 1 }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct aqfp_reconstruct_stats +{ + /*! \brief Number of buffers and splitters after reconstruction. */ + uint32_t num_buffers{ 0 }; + + /*! \brief Total runtime */ + stopwatch<>::duration total_time{ 0 }; + + /*! \brief Report stats */ + void report() + { + std::cout << fmt::format( "[i] Buffers = {}\t Total time = {}\n", num_buffers, to_seconds( total_time ) ); + } +}; + +namespace detail +{ + +class aqfp_reconstruct_impl +{ +public: + using node = typename aqfp_network::node; + using signal = typename aqfp_network::signal; + +public: + explicit aqfp_reconstruct_impl( buffered_aqfp_network const& ntk, aqfp_reconstruct_params const& ps, aqfp_reconstruct_stats& st ) + : _ntk( ntk ), _ps( ps ), _st( st ), _topo_order() + { + } + + buffered_aqfp_network run() + { + stopwatch( _st.total_time ); + + /* save the level of each node */ + depth_view ntk_level{ _ntk }; + + /* create a network removing the splitter trees */ + aqfp_network clean_ntk; + node_map old2new( _ntk ); + remove_splitter_trees( clean_ntk, old2new ); + + /* compute the node level on the new network */ + node_map levels( clean_ntk ); + _ntk.foreach_gate( [&]( auto const& n ) { + levels[old2new[n]] = ntk_level.level( n ); + } ); + + uint32_t max_po_level = 0; + clean_ntk.foreach_po( [&]( auto const& f ){ + uint32_t spl = std::ceil( std::log( clean_ntk.fanout_size( clean_ntk.get_node( f ) ) ) / std::log( _ps.buffer_insertion_ps.assume.splitter_capacity ) ); + max_po_level = std::max( max_po_level, levels[f] + spl ); + }); + std::vector po_levels; + for ( auto i = 0u; i < _ntk.num_pos(); ++i ) + { + po_levels.emplace_back( max_po_level + 1 ); + } + + /* recompute splitter trees and return the new buffered network */ + buffered_aqfp_network res; + buffer_insertion buf_inst( clean_ntk, levels, po_levels, _ps.buffer_insertion_ps ); + _st.num_buffers = buf_inst.run( res ); + return res; + } + +private: + void remove_splitter_trees( aqfp_network& res, node_map& old2new ) + { + topo_sorting(); + + old2new[_ntk.get_constant( false )] = res.get_constant( false ); + _ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + for ( auto const& n : _topo_order ) + { + if ( _ntk.is_pi( n ) || _ntk.is_constant( n ) ) + continue; + + std::vector children; + _ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ _ntk.is_complemented( f ) ); + } ); + + if ( _ntk.is_buf( n ) ) + { + old2new[n] = children[0]; + } + else if ( children.size() == 3 ) + { + old2new[n] = res.create_maj( children[0], children[1], children[2] ); + } + else + { + old2new[n] = res.create_maj( children ); + } + } + + _ntk.foreach_po( [&]( auto const& f ) { + res.create_po( old2new[f] ^ _ntk.is_complemented( f ) ); + } ); + } + + void topo_sorting() + { + _ntk.incr_trav_id(); + _ntk.incr_trav_id(); + _topo_order.reserve( _ntk.size() ); + + seed = _ps.seed; + + /* constants and PIs */ + const auto c0 = _ntk.get_node( _ntk.get_constant( false ) ); + _topo_order.push_back( c0 ); + _ntk.set_visited( c0, _ntk.trav_id() ); + + if ( const auto c1 = _ntk.get_node( _ntk.get_constant( true ) ); _ntk.visited( c1 ) != _ntk.trav_id() ) + { + _topo_order.push_back( c1 ); + _ntk.set_visited( c1, _ntk.trav_id() ); + } + + _ntk.foreach_ci( [&]( auto const& n ) { + if ( _ntk.visited( n ) != _ntk.trav_id() ) + { + _topo_order.push_back( n ); + _ntk.set_visited( n, _ntk.trav_id() ); + } + } ); + + _ntk.foreach_co( [&]( auto const& f ) { + /* node was already visited */ + if ( _ntk.visited( _ntk.get_node( f ) ) == _ntk.trav_id() ) + return; + + topo_sorting_rec( _ntk.get_node( f ) ); + } ); + } + + void topo_sorting_rec( node const& n ) + { + /* is permanently marked? */ + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return; + + /* ensure that the node is not temporarily marked */ + assert( _ntk.visited( n ) != _ntk.trav_id() - 1 ); + + /* mark node temporarily */ + _ntk.set_visited( n, _ntk.trav_id() - 1 ); + + /* mark children */ + if ( !_ps.det_randomization ) + { + _ntk.foreach_fanin( n, [this]( signal const& f ) { + topo_sorting_rec( _ntk.get_node( f ) ); + } ); + } + else + { + std::vector fanins; + _ntk.foreach_fanin( n, [this, &fanins]( signal const& f ) { + fanins.push_back( _ntk.get_node( f ) ); + } ); + std::shuffle( fanins.begin(), fanins.end(), std::default_random_engine( seed++ ) ); + + for ( node const& g : fanins ) + topo_sorting_rec( g ); + } + + /* mark node n permanently */ + _ntk.set_visited( n, _ntk.trav_id() ); + + /* visit node */ + _topo_order.push_back( n ); + } + +private: + buffered_aqfp_network const& _ntk; + aqfp_reconstruct_params const& _ps; + aqfp_reconstruct_stats& _st; + + std::vector _topo_order; + std::default_random_engine::result_type seed{ 1 }; +}; + +} /* namespace detail */ + +/*! \brief Rebuilds buffer/splitter trees in an AQFP network. + * + * This function rebuilds buffer/splitter trees in an AQFP network. + * + * \param ntk Buffered AQFP network + */ +buffered_aqfp_network aqfp_reconstruct( buffered_aqfp_network const& ntk, aqfp_reconstruct_params const& ps = {}, aqfp_reconstruct_stats* pst = nullptr ) +{ + aqfp_reconstruct_stats st; + + detail::aqfp_reconstruct_impl p( ntk, ps, st ); + auto res = p.run(); + + if ( pst ) + *pst = st; + + if ( ps.verbose ) + st.report(); + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp new file mode 100644 index 00000000000..0021f4107b8 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp @@ -0,0 +1,378 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_resynthesis.hpp + \brief Resynthesis of path balanced networks + + \author Dewmini Sudara Marakkalage + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for aqfp_resynthesis. + * + * The data structure `aqfp_resynthesis_params` holds configurable parameters + * with default arguments for `node_resynthesis`. + */ +struct aqfp_resynthesis_params +{ + bool verbose{ false }; +}; + +/*! \brief Statistics of aqfp_resynthesis. + * + * The data structure `aqfp_resynthesis_stats` holds data collected during AQFP re-synthesis. + */ +struct aqfp_resynthesis_stats +{ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>8.2f} secs\n", to_seconds( time_total ) ); + } +}; + +/*! \brief Results of aqfp_resynthesis. + * + * The data structure `aqfp_resynthesis_result` holds the resulting level assignment of nodes + * and the level of the critical primary output. + */ +template +struct aqfp_resynthesis_result +{ + std::unordered_map, uint32_t> node_level; + std::unordered_map, uint32_t> po_level; + + uint32_t critical_po_level() + { + return max_element( po_level.begin(), po_level.end(), [&]( auto n1, auto n2 ) { return n1.second < n2.second; } ) + ->second; + } +}; + +namespace detail +{ + +/*! \brief Implementation of the AQFP re-synthesis algorithm. */ +template +class aqfp_resynthesis_impl +{ +public: + aqfp_resynthesis_impl( + NtkDest& ntk_dest, + NtkSrc const& ntk_src, + NodeResynFn&& node_resyn_fn, + FanoutResynFn&& fanout_resyn_fn, + aqfp_resynthesis_params const& ps, + aqfp_resynthesis_stats& st ) + : ntk_dest( ntk_dest ), + ntk_src( ntk_src ), + node_resyn_fn( node_resyn_fn ), + fanout_resyn_fn( fanout_resyn_fn ), + ps( ps ), + st( st ) + { + } + + aqfp_resynthesis_result run() + { + stopwatch t( st.time_total ); + + node_map, NtkSrc> node2new( ntk_src ); + node_map level_of_src_node( ntk_src ); + + std::unordered_map, uint32_t> level_of_node; + std::unordered_map, uint32_t> po_level_of_node; + std::map, node>, uint32_t> level_for_fanout; + + std::unordered_map, std::vector>> fanouts; + ntk_src.foreach_gate( [&]( auto n ) { ntk_src.foreach_fanin( n, [&]( auto fi ) { fanouts[ntk_src.get_node( fi )].push_back( n ); } ); } ); + + depth_view ntk_depth{ ntk_src }; + topo_view ntk_topo{ ntk_depth }; + + /* map constants */ + auto c0 = ntk_dest.get_constant( false ); + node2new[ntk_src.get_node( ntk_src.get_constant( false ) )] = c0; + level_of_node[ntk_dest.get_node( c0 )] = 0u; + + if ( ntk_src.get_node( ntk_src.get_constant( true ) ) != ntk_src.get_node( ntk_src.get_constant( false ) ) ) + { + auto c1 = ntk_dest.get_constant( true ); + node2new[ntk_src.get_node( ntk_src.get_constant( true ) )] = c1; + level_of_node[ntk_dest.get_node( c1 )] = 0u; + } + + /* map primary inputs */ + ntk_src.foreach_pi( [&]( auto n ) { + auto pi = ntk_dest.create_pi(); + node2new[n] = pi; + level_of_node[ntk_dest.get_node( pi )] = 0u; + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk_src.has_name( ntk_src.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk_src.get_name( ntk_src.make_signal( n ) ) ); + } + + /* synthesize fanout net of `n`*/ + auto fanout_node_callback = [&]( const auto& f, const auto& level ) { + level_for_fanout[{ n, f }] = level; + }; + + auto fanout_po_callback = [&]( const auto& index, const auto& level ) { + (void)index; + auto node = ntk_dest.get_node(node2new[n]); + po_level_of_node[node] = std::max(po_level_of_node[node], level); + }; + + fanout_resyn_fn( ntk_topo, n, fanouts[n], ntk_dest, node2new[n], 0u, fanout_node_callback, fanout_po_callback ); } ); + + /* map register outputs */ + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + ntk_src.foreach_ro( [&]( auto n, auto i ) { + auto ro = ntk_dest.create_ro(); + node2new[n] = ro; + level_of_node[ntk_dest.get_node( ro )] = 0u; + + ntk_dest.set_register( i, ntk_src.register_at( i ) ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk_src.has_name( ntk_src.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk_src.get_name( ntk_src.make_signal( n ) ) ); + } + + auto fanout_node_callback = [&]( const auto& f, const auto& level ) { + level_for_fanout[{ n, f }] = level; + }; + + auto fanout_po_callback = [&]( const auto& index, const auto& level ) { + (void)index; + auto node = ntk_dest.get_node(node2new[n]); + po_level_of_node[node] = std::max(po_level_of_node[node], level); + }; + + fanout_resyn_fn( ntk_topo, n, fanouts[n], ntk_dest, node2new[n], 0u, fanout_node_callback, fanout_po_callback ); } ); + } + + /* map nodes */ + ntk_topo.foreach_node( [&]( auto n ) { + if ( ntk_topo.is_constant( n ) || ntk_topo.is_ci( n ) ) + return; + + /* synthesize node `n` */ + std::vector, uint32_t>> children; + ntk_topo.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( { ntk_topo.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f], level_for_fanout[{ ntk_topo.get_node( f ), n }] } ); + } ); + + auto performed_resyn = false; + + auto level_update_callback = + [&]( const auto& n, uint32_t level ) { + if ( !level_of_node.count( n ) ) + { + level_of_node[n] = level; + } + }; + + auto resyn_performed_callback = + [&]( const auto& f, auto new_level ) { + node2new[n] = f; + level_of_src_node[n] = new_level; + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk_topo.has_name( ntk_topo.make_signal( n ) ) ) + ntk_dest.set_name( f, ntk_topo.get_name( ntk_topo.make_signal( n ) ) ); + } + + performed_resyn = true; + }; + + node_resyn_fn( ntk_dest, ntk_topo.node_function( n ), children.begin(), children.end(), level_update_callback, resyn_performed_callback ); + + if ( !performed_resyn ) + { + fmt::print( "[e] could not perform resynthesis for node {} in node_resynthesis\n", ntk_topo.node_to_index( n ) ); + std::abort(); + } + + /* synthesize fanout net of `n` */ + auto fanout_node_callback = [&]( const auto& f, const auto& level ) { + level_for_fanout[{ n, f }] = std::max(level_for_fanout[{n, f}], level); + }; + + auto fanout_po_callback = [&]( const auto& index, const auto& level ) { + (void)index; + auto node = ntk_dest.get_node(node2new[n]); + po_level_of_node[node] = std::max(po_level_of_node[node], level); + }; + + fanout_resyn_fn( ntk_topo, n, fanouts[n], ntk_dest, node2new[n], level_of_src_node[n], fanout_node_callback, fanout_po_callback ); } ); + + /* map primary outputs */ + ntk_src.foreach_po( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk_src.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_po( o ); + + assert(ntk_dest.is_constant(ntk_dest.get_node(o)) || po_level_of_node.count(ntk_dest.get_node(o)) > 0); + assert(ntk_dest.is_constant(ntk_dest.get_node(o)) || po_level_of_node.at(ntk_dest.get_node(o)) >= level_of_node.at(ntk_dest.get_node(o))); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk_src.has_output_name( index ) ) + { + ntk_dest.set_output_name( index, ntk_src.get_output_name( index ) ); + } + } } ); + + /* map register inputs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk_src.foreach_ri( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk_src.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_ri( o ); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk_src.has_output_name( index ) ) + { + ntk_dest.set_output_name( index + ntk_src.num_pos(), ntk_src.get_output_name( index + ntk_src.num_pos() ) ); + } + } } ); + } + + return { level_of_node, po_level_of_node }; + } + +private: + NtkDest& ntk_dest; + NtkSrc const& ntk_src; + NodeResynFn&& node_resyn_fn; + FanoutResynFn&& fanout_resyn_fn; + aqfp_resynthesis_params const& ps; + aqfp_resynthesis_stats& st; +}; + +} /* namespace detail */ + +/*! \brief Re-synthesize a given source network as a path-balanced AQFP network. + * + * The algorithm outputs an AQFP network with level assignments to its nodes and combinational outputs. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aqfp_assumptions assume = { false, false, true, 4u }; + aqfp_fanout_resyn fanout_resyn{ assume }; + + std::unordered_map gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }; + std::unordered_map splitters = { { 1u, 2.0 }, { assume.splitter_capacity, 2.0 } }; + aqfp_node_resyn_param ps{ assume, splitters, aqfp_node_resyn_strategy::delay }; + + aqfp_db<> db( gate_costs, splitters ); + db.load_db( ... ); // from an input-stream (e.g., std::ifstream or std::stringstream) + + aqfp_node_resyn node_resyn( db, ps ); + + klut_network src_ntk = ...; + aqfp_network dst_ntk; + auto res = aqfp_resynthesis( dst_ntk, src_ntk, node_resyn, fanout_resyn ); + + \endverbatim + */ +template +aqfp_resynthesis_result aqfp_resynthesis( + NtkDest& ntk_dest, + NtkSrc const& ntk_src, + NodeResynFn&& node_resyn_fn, + FanoutResynFn&& fanout_resyn_fn, + aqfp_resynthesis_params const& ps = { false }, + aqfp_resynthesis_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSrc does not implement the is_complemented method" ); + static_assert( has_foreach_fanin_v, "NtkSrc does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "NtkSrc does not implement the node_function method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + + aqfp_resynthesis_stats st; + + detail::aqfp_resynthesis_impl p( ntk_dest, ntk_src, node_resyn_fn, fanout_resyn_fn, ps, st ); + auto result = p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return result; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_retiming.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_retiming.hpp new file mode 100644 index 00000000000..65abffb6660 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/aqfp_retiming.hpp @@ -0,0 +1,681 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_retiming.hpp + \brief Retiming for AQFP networks + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../networks/buffered.hpp" +#include "../../networks/generic.hpp" +#include "../../networks/klut.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/choice_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "../retiming.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_rebuild.hpp" + +namespace mockturtle +{ + +struct aqfp_retiming_params +{ + /*! \brief AQFP technology assumptions. */ + aqfp_assumptions aqfp_assumptions_ps{}; + + /*! \brief Max number of iterations */ + uint32_t iterations{ UINT32_MAX }; + + /*! \brief Enable splitter retiming. */ + bool retime_splitters{ true }; + + /*! \brief Order of retiming is backward first. */ + bool backwards_first{ true }; + + /*! \brief Adds an additional try for retiming */ + uint32_t additional_try_iterations{ 1 }; + + /*! \brief Forward retiming only. */ + bool forward_only{ false }; + + /*! \brief Backward retiming only. */ + bool backward_only{ false }; + + /*! \brief Random seed. */ + std::default_random_engine::result_type seed{ 1 }; + + /*! \brief Randomize the network. */ + bool det_randomization{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct aqfp_retiming_stats +{ + /*! \brief Initial number of buffers/splitters. */ + uint32_t buffers_pre{ 0 }; + + /*! \brief Number of buffers/splitters after the algorithm. */ + uint32_t buffers_post{ 0 }; + + /*! \brief Total iterations. */ + uint32_t rounds_total{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] Initial B/S = {:7d}\t Final B/S = {:7d}\n", buffers_pre, buffers_post ); + std::cout << fmt::format( "[i] Total rounds = {:7d}\n", rounds_total ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class aqfp_retiming_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using signal_g = typename generic_network::signal; + using classes_t = std::vector>; + +public: + explicit aqfp_retiming_impl( Ntk& ntk, aqfp_retiming_params const& ps, aqfp_retiming_stats& st ) + : _ntk( ntk ), _ps( ps ), _st( st ) + { + } + +public: + Ntk run() + { + stopwatch t( _st.time_total ); + + retime_params rps; + std::default_random_engine::result_type seed = _ps.seed; + + _st.buffers_pre = get_stats( _ntk ); + + Ntk ntk = _ntk; + uint32_t additional_try = _ps.additional_try_iterations; + + if ( _ps.aqfp_assumptions_ps.splitter_capacity != 2 ) + rps.iterations = 1; + + buffer_insertion_params buf_ps; + buf_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + buf_ps.scheduling = buffer_insertion_params::provided; + buf_ps.optimization_effort = buffer_insertion_params::none; + aqfp_reconstruct_params reconstruct_ps; + aqfp_reconstruct_stats reconstruct_st; + reconstruct_ps.buffer_insertion_ps = buf_ps; + reconstruct_ps.det_randomization = _ps.det_randomization; + + /* retiming first direction pass */ + rps.forward_only = !_ps.backwards_first; + rps.backward_only = _ps.backwards_first; + uint32_t i = _ps.iterations; + + if ( ( _ps.backwards_first && !_ps.forward_only ) || ( !_ps.backwards_first && !_ps.backward_only ) ) + { + while ( i-- > 0 ) + { + auto net = to_generic( ntk, seed, !_ps.backwards_first ); + auto num_registers_before = net.num_registers(); + + retime( net, rps ); + + if ( net.num_registers() >= num_registers_before ) + { + if ( additional_try-- == 0 ) + break; + } + else if ( additional_try ) + { + additional_try = _ps.additional_try_iterations; + } + + ntk = to_buffered( net ); + ++_st.rounds_total; + } + } + + /* retiming second direction pass */ + rps.forward_only = _ps.backwards_first; + rps.backward_only = !_ps.backwards_first; + i = _ps.iterations; + additional_try = _ps.additional_try_iterations; + + if ( ( !_ps.backwards_first && !_ps.forward_only ) || ( _ps.backwards_first && !_ps.backward_only ) ) + { + while ( i-- > 0 ) + { + auto net = to_generic( ntk, seed, _ps.backwards_first ); + auto num_registers_before = net.num_registers(); + + retime( net, rps ); + + if ( net.num_registers() >= num_registers_before ) + { + if ( additional_try-- == 0 ) + break; + } + else if ( additional_try ) + { + additional_try = _ps.additional_try_iterations; + } + ntk = to_buffered( net ); + ++_st.rounds_total; + } + } + + auto res = aqfp_reconstruct( ntk, reconstruct_ps, &reconstruct_st ); + _st.buffers_post = reconstruct_st.num_buffers; + return res; + } + +private: + uint32_t get_stats( Ntk& ntk ) + { + uint32_t bs_count = 0; + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_buf( n ) ) + ++bs_count; + } ); + + return bs_count; + } + + generic_network to_generic( Ntk& ntk, std::default_random_engine::result_type& seed, bool forward ) + { + node_map old2new( ntk ); + generic_network res; + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old2new[ntk.get_constant( true )] = res.get_constant( true ); + } + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + /* suppose network is in topological order */ + if ( _ps.retime_splitters && _ps.aqfp_assumptions_ps.splitter_capacity != 2 ) + { + select_retimeable_elements_random( ntk, seed, forward ); + } + else + { + select_buffers( ntk ); + } + + create_generic_network( ntk, res, old2new ); + + return res; + } + + Ntk to_buffered( generic_network const& ntk ) + { + node_map old2new( ntk ); + Ntk res; + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old2new[ntk.get_constant( true )] = res.get_constant( true ); + } + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + topo_view topo{ ntk }; + + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return true; + + /* remove not represented nodes */ + if ( ntk.is_box_input( n ) || ntk.is_box_output( n ) || ntk.is_po( n ) ) + { + signal children; + ntk.foreach_fanin( n, [&]( auto const& f ) { + children = old2new[f]; + } ); + + old2new[n] = children; + return true; + } + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ); + } ); + + signal f; + + if ( ntk.fanin_size( n ) >= 3 ) + { + if constexpr ( has_create_maj_odd_v ) + { + if ( ntk.fanin_size( n ) > 3 ) + f = res.create_maj( children ); + else + f = res.create_maj( children[0], children[1], children[2] ); + } + else + { + f = res.create_maj( children[0], children[1], children[2] ); + } + } + else if ( ntk.fanin_size( n ) == 1 && ntk.node_function( n )._bits[0] == 0x1 ) + { + /* not */ + assert( children.size() == 1 ); + f = !children[0]; + } + else + { + /* buffer */ + /* not balanced PIs */ + if ( !_ps.aqfp_assumptions_ps.balance_pis && ( res.is_pi( res.get_node( children[0] ) ) || res.is_constant( res.get_node( children[0] ) ) ) ) + f = children[0]; + else + f = res.create_buf( children[0] ); + } + + old2new[n] = f; + return true; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + res.create_po( old2new[f] ); + } ); + + return res; + } + + void select_retimeable_elements_random( Ntk& ntk, std::default_random_engine::result_type& seed, bool forward ) + { + fanout_view fntk{ ntk }; + + ntk.clear_values(); + + /* select buffers and splitters to retime as soon as found some */ + ntk.incr_trav_id(); + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return true; + + if ( ntk.is_buf( n ) ) + { + if ( ntk.fanout_size( n ) == 1 ) + { + if ( forward ) + { + fntk.foreach_fanout( n, [&]( auto const& f ) { + if ( !ntk.is_buf( f ) || ntk.fanout_size( f ) != 1 ) + ntk.set_visited( n, ntk.trav_id() ); + } ); + } + else + { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() || !ntk.is_buf( ntk.get_node( f ) ) || ntk.fanout_size( ntk.get_node( f ) ) != 1 ) + ntk.set_visited( n, ntk.trav_id() ); + } ); + } + } + else if ( ntk.visited( n ) != ntk.trav_id() || ntk.value( n ) > 0 ) + { + int free_spots; + if ( ntk.value( n ) > 0 ) + { + free_spots = rec_fetch_root( ntk, n ); /* aparently useless */ + if ( free_spots == 0 ) + return true; + } + else + { + free_spots = _ps.aqfp_assumptions_ps.splitter_capacity - ntk.fanout_size( n ); + } + + int total_fanout = 0; + std::vector fanout_splitters; + + /* select retimeable splitters */ + fntk.foreach_fanout( n, [&]( auto const f ) { + if ( ntk.is_buf( f ) && ntk.fanout_size( f ) > 1 && free_spots >= ntk.fanout_size( f ) - 1 ) + { + fanout_splitters.push_back( f ); + total_fanout += ntk.fanout_size( f ) - 1; + } + } ); + + /* check if they are all retimeable together */ + if ( free_spots >= total_fanout ) + { + for ( auto f : fanout_splitters ) + { + ntk.set_value( f, free_spots - total_fanout ); + ntk.set_visited( f, ntk.trav_id() ); + } + rec_update_root( ntk, n, free_spots - total_fanout ); + return true; + } + /* select one randomly */ + std::default_random_engine gen( seed++ ); + std::uniform_int_distribution dist( 0ul, fanout_splitters.size() - 1 ); + auto index = dist( gen ); + ntk.set_value( fanout_splitters[index], free_spots - ntk.fanout_size( fanout_splitters[index] ) + 1 ); + ntk.set_visited( fanout_splitters[index], ntk.trav_id() ); + rec_update_root( ntk, n, free_spots - ntk.fanout_size( fanout_splitters[index] ) + 1 ); + } + } + return true; + } ); + } + + void create_generic_network( Ntk& ntk, generic_network& res, node_map& old2new ) + { + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return true; + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + children.push_back( res.create_not( old2new[f] ) ); + else + children.push_back( old2new[f] ); + } ); + + if ( ntk.is_buf( n ) && ntk.visited( n ) == ntk.trav_id() ) + { + auto const in_register = res.create_box_input( children[0] ); + auto const node_register = res.create_register( in_register ); + auto const node_register_out = res.create_box_output( node_register ); + old2new[n] = node_register_out; + } + else + { + const auto f = res.create_node( children, ntk.node_function( n ) ); + old2new[n] = f; + } + + return true; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + } + + void forward_compatibility( Ntk& ntk, choice_view& choice_ntk ) + { + ntk.foreach_node( [&]( auto const n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + /* assign classes */ + unsigned value = 0; + ntk.foreach_fanin( n, [&]( auto const f ) { + if ( ntk.value( ntk.get_node( f ) ) ) + { + if ( value ) + choice_ntk.add_choice( value, ntk.value( ntk.get_node( f ) ) ); + else + value = ntk.value( ntk.get_node( f ) ); + } + } ); + + /* propagate classes */ + if ( ntk.visited( n ) != ntk.trav_id() && !ntk.value( n ) ) + ntk.set_value( n, value ); + } ); + } + + void backward_compatibility( Ntk& ntk, choice_view& choice_ntk, fanout_view& fntk ) + { + std::vector topo_order; + topo_order.reserve( ntk.size() ); + ntk.foreach_node( [&]( auto const n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + topo_order.push_back( n ); + } ); + + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + /* assign classes */ + unsigned value = 0; + fntk.foreach_fanout( *it, [&]( auto const f ) { + if ( ntk.value( f ) ) + { + if ( value ) + choice_ntk.add_choice( value, ntk.value( f ) ); + else + value = ntk.value( f ); + } + } ); + + /* propagate classes */ + if ( ntk.visited( *it ) != ntk.trav_id() && !ntk.value( *it ) ) + ntk.set_value( *it, value ); + } + } + + classes_t create_classes( choice_view& choice_ntk ) + { + classes_t classes; + choice_ntk.foreach_node( [&]( auto const n ) { + if ( choice_ntk.is_pi( n ) || choice_ntk.is_constant( n ) ) + return; + /* create unique classes */ + if ( choice_ntk.value( n ) == n ) + { + std::vector comp_class; + choice_ntk.foreach_choice( n, [&]( auto const& f ) { + comp_class.push_back( f ); + choice_ntk.set_value( f, 0 ); + return true; + } ); + classes.push_back( comp_class ); + } + } ); + + std::stable_sort( classes.begin(), classes.end(), [&]( std::vector const& a, std::vector const& b ) { return a.size() > b.size(); } ); + + return classes; + } + + uint32_t rec_fetch_root( Ntk& ntk, node const n ) + { + uint32_t value; + + if ( ntk.visited( n ) != ntk.trav_id() ) + return ntk.value( n ); + + ntk.foreach_fanin( n, [&]( auto const f ) { + auto g = ntk.get_node( f ); + if ( !ntk.is_buf( g ) || ntk.fanout_size( g ) == 1 ) + value = ntk.value( n ); + else + value = rec_fetch_root( ntk, g ); + } ); + + return value; + } + + void rec_update_root( Ntk& ntk, node const n, uint32_t const update ) + { + if ( !ntk.is_buf( n ) || ntk.fanout_size( n ) == 1 ) + return; + + ntk.set_value( n, update ); + + if ( ntk.visited( n ) != ntk.trav_id() ) + return; + + ntk.foreach_fanin( n, [&]( auto const f ) { + rec_update_root( ntk, ntk.get_node( f ), update ); + } ); + } + + void select_buffers( Ntk& ntk ) + { + ntk.incr_trav_id(); + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_buf( n ) && ntk.fanout_size( n ) == 1 ) + ntk.set_visited( n, ntk.trav_id() ); + } ); + } + + Ntk create_supersplitters() + { + Ntk res; + node_map old2new( _ntk ); + + old2new[_ntk.get_constant( false )] = res.get_constant( false ); + if ( _ntk.get_node( _ntk.get_constant( true ) ) != _ntk.get_node( _ntk.get_constant( false ) ) ) + { + old2new[_ntk.get_constant( true )] = res.get_constant( true ); + } + _ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( _ntk.is_pi( n ) || _ntk.is_constant( n ) ) + return; + + std::vector children; + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ _ntk.is_complemented( f ) ); + } ); + + signal f; + if ( _ntk.is_buf( n ) ) + { + uint32_t supersplitter = res.value( res.get_node( children[0] ) ); + if ( !supersplitter ) + { + bool is_complemented = res.is_complemented( children[0] ); + f = res.create_buf( children[0] ^ is_complemented ); + res.set_value( res.get_node( children[0] ), res.node_to_index( res.get_node( f ) ) ); + f = f ^ is_complemented; + } + else + { + f = res.make_signal( res.index_to_node( supersplitter ) ) ^ res.is_complemented( children[0] ); + } + } + else + { + f = res.clone_node( _ntk, n, children ); + } + old2new[n] = f; + } ); + + _ntk.foreach_po( [&]( auto const& f ) { + if ( _ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + + return res; + } + +private: + Ntk& _ntk; + aqfp_retiming_params const& _ps; + aqfp_retiming_stats& _st; +}; + +} /* namespace detail */ + +/*! \brief AQFP retiming. + * + * This function applies a retiming-based approach + * for splitters and buffers minimization. + * + * \param ntk Buffered network + * \param ps AQFP retiming params + */ +template +Ntk aqfp_retiming( Ntk& ntk, aqfp_retiming_params const& ps = {}, aqfp_retiming_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "NtkDest does not implement the is_complemented method" ); + static_assert( is_buffered_network_type_v, "BufNtk is not a buffered network type" ); + static_assert( has_is_buf_v, "BufNtk does not implement the is_buf method" ); + static_assert( has_create_buf_v, "BufNtk does not implement the create_buf method" ); + + aqfp_retiming_stats st; + + detail::aqfp_retiming_impl p( ntk, ps, st ); + auto res = p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + *pst = st; + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp new file mode 100644 index 00000000000..3251aa88b26 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp @@ -0,0 +1,1980 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file buffer_insertion.hpp + \brief Insert buffers and splitters for the AQFP technology + + \author Siang-Yun (Sonia) Lee + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "aqfp_assumptions.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for (AQFP) buffer insertion. + */ +struct buffer_insertion_params +{ + /*! \brief Technology assumptions. */ + aqfp_assumptions_realistic assume; + + /*! \brief The scheduling strategy to get the initial depth assignment. + * - `provided` = An initial level assignment is given in the constructor, thus + * no scheduling is performed. It is the user's responsibility to ensure that + * the provided assignment is legal. + * - `ASAP` = Classical As-Soon-As-Possible scheduling + * - `ASAP_depth` = As-Soon-As-Possible scheduling with depth optimality + * - `ALAP` = ASAP (to obtain depth) followed by As-Late-As-Possible scheduling + * - `ALAP_depth` = As-Late-As-Possible scheduling with depth optimality + * - `better` = ASAP followed by ALAP, then count buffers for both assignments + * and choose the better one + * - `better_depth` = ALAP_depth followed by ASAP_depth, then count buffers for both assignments + * and choose the better one + */ + enum scheduling_policy + { + provided, + ASAP, + ASAP_depth, + ALAP, + ALAP_depth, + better, + better_depth + } scheduling = ASAP; + + /*! \brief The level of optimization effort. + * - `none` = No optimization + * - `one_pass` = Try to form a chunk starting from each gate, once + * for all gates + * - `until_sat` = Iterate over all gates until no more beneficial + * chunk movement can be found + * - `optimal` = Use an SMT solver to find the global optimal + */ + enum + { + none, + one_pass, + until_sat, + optimal + } optimization_effort = none; + + /*! \brief The maximum size of a chunk. */ + uint32_t max_chunk_size{ 100u }; +}; + +/*! \brief Insert buffers and splitters for the AQFP technology. + * + * In the AQFP technology, (1) logic gates can only have one fanout. If more than one + * fanout is needed, a splitter has to be inserted in between, which also + * takes one clocking phase (counted towards the network depth). (2) All fanins of + * a logic gate have to arrive at the same time (be at the same level). If one + * fanin path is shorter, buffers have to be inserted to balance it. + * Buffers and splitters are essentially the same component in this technology. + * + * With a given level assignment to all gates in the network, the minimum number of + * buffers needed is determined. This class implements algorithms to count such + * "irredundant buffers" and to insert them to obtain a buffered network. Moreover, + * as buffer optimization is essentially a problem of obtaining a good level assignment, + * this class also implements algorithms to obtain an initial, legal assignment using + * scheduling algorithms and to further adjust and optimize it. + * + * This class provides two easy-to-use top-level functions which wrap all the above steps + * together: `run` and `dry_run`. In addition, the following interfaces are kept for + * more fine-grained usage: + * - Query the current level assignment (`level`, `depth`) + * - Count irredundant buffers based on the current level assignment (`count_buffers`, + * `num_buffers`) + * - Optimize buffer count by scheduling (`schedule`, `ASAP`, `ALAP`) and by adjusting + * the level assignment with chunked movement (`optimize`) + * - Dump the resulting network into a network type which provides representation for + * buffers (`dump_buffered_network`) + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + mig_network mig = ... + + buffer_insertion_params ps; + ps.scheduling = buffer_insertion_params::ALAP; + ps.optimization_effort = buffer_insertion_params::one_pass; + + buffer_insertion buffering( mig, ps ); + buffered_mig_network buffered_mig; + auto const num_buffers = buffering.run( buffered_mig ); + + std::cout << num_buffers << std::endl; + assert( verify_aqfp_buffer( buffered_mig, ps.assume ) ); + write_verilog( buffered_mig, "buffered.v" ); + \endverbatim + * + * **Required network functions:** + * - `foreach_node` + * - `foreach_gate` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_fanin` + * - `is_pi` + * - `is_constant` + * - `get_node` + * - `fanout_size` + * - `size` + * - `set_visited` + * - `set_value` + * + */ +template +class buffer_insertion +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit buffer_insertion( Ntk const& ntk, buffer_insertion_params const& ps = {} ) + : _ntk( ntk ), _ps( ps ), _levels( _ntk ), _po_levels( _ntk.num_pos(), 0u ), _timeframes( _ntk ), _fanouts( _ntk ), _num_buffers( _ntk ) + { + static_assert( !is_buffered_network_type_v, "Ntk is already buffered" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + + assert( _ps.scheduling != buffer_insertion_params::provided ); + + // checks for assumptions + assert( _ps.assume.ci_phases.size() > 0 ); + assert( _ps.assume.ignore_co_negation ); // consideration of CO negation is too complicated and neglected for now + } + + explicit buffer_insertion( Ntk const& ntk, node_map const& levels, std::vector const& po_levels, buffer_insertion_params const& ps = {} ) + : _ntk( ntk ), _ps( ps ), _levels( levels ), _po_levels( po_levels ), _timeframes( _ntk ), _fanouts( _ntk ), _num_buffers( _ntk ) + { + static_assert( !is_buffered_network_type_v, "Ntk is already buffered" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + + assert( _ps.scheduling == buffer_insertion_params::provided ); + assert( _po_levels.size() == _ntk.num_pos() ); + } + + /*! \brief Insert buffers and obtain a buffered network. + * + * \param bufntk An empty network of an appropriate buffered network type to + * to store the buffer-insertion result + * \return The number of buffers in the resulting network + */ + template + uint32_t run( BufNtk& bufntk ) + { + dry_run(); + dump_buffered_network( bufntk ); + return num_buffers(); + } + + /*! \brief Insert buffers and obtain a buffered network. + * + * It is suggested to write the `pi_levels` information into a dumped file + * for easier recovery of the scheduled phase assignment. + * + * \param bufntk An empty network of an appropriate buffered network type to + * to store the buffer-insertion result + * \param pi_lvls A vector which will store the PI level assignment (it is + * recommended to store this information together with the buffered network) + * \return The number of buffers in the resulting network + */ + template + uint32_t run( BufNtk& bufntk, std::vector& pi_lvls ) + { + dry_run(); + dump_buffered_network( bufntk ); + pi_lvls = pi_levels(); + return num_buffers(); + } + + /*! \brief Count the number of buffers without dumping the result into a buffered network. + * + * This function saves some runtime for dumping the resulting network and + * allows users to experiment on the algorithms with new network types whose + * corresponding buffered_network are not implemented yet. + * + * `pLevels` and `pPOLevels` can be used to create another `buffer_insertion` instance of + * the same state (current schedule), which also define a unique buffered network. (Set + * `ps.scheduling = provided` and `ps.optimization_effort = none`) + * + * \return The number of buffers in the resulting network + */ + uint32_t dry_run() + { + schedule(); + optimize(); + count_buffers(); + return num_buffers(); + } + +#pragma region Query + node_map const& levels() const + { + return _levels; + } + + /*! \brief Level of node `n` considering buffer/splitter insertion. */ + uint32_t level( node const& n ) const + { + assert( n < _ntk.size() ); + return _levels[n]; + } + + std::vector const& po_levels() const + { + return _po_levels; + } + + /*! \brief Level of the `idx`-th PO (imaginary dummy PO node, not counted in depth). */ + uint32_t po_level( uint32_t idx ) const + { + assert( idx < _ntk.num_pos() ); + return _po_levels[idx]; + } + + std::vector pi_levels() const + { + std::vector lvls; + _ntk.foreach_pi( [&]( auto n ){ + lvls.emplace_back( _levels[n] ); + } ); + return lvls; + } + + /*! \brief Network depth considering AQFP buffers/splitters. + * + * Should be equal to `max( po_level(i) - 1 )`. + * + * This is the number of phases from the previous-stage register to the + * next-stage register, including the depth of the previous-stage register + * (i.e., from one register input to the next register input). + */ + uint32_t depth() const + { + return _depth; + } + + /*! \brief The total number of buffers in the network under the current + * level assignment. */ + uint32_t num_buffers() const + { + assert( !_outdated && "Please call `count_buffers()` first." ); + + uint32_t count = 0u; + _ntk.foreach_node( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + count += num_buffers( n ); + } ); + return count; + } + + /*! \brief The number of buffers between `n` and all of its fanouts under + * the current level assignment. */ + uint32_t num_buffers( node const& n ) const + { + assert( !_outdated && "Please call `count_buffers()` first." ); + return _num_buffers[n]; + } + + /*! \brief The chosen schedule is ASAP */ + bool is_scheduled_ASAP() const + { + return _is_scheduled_ASAP; + } +#pragma endregion + +#pragma region Count buffers + /*! \brief Count the number of buffers needed at the fanout of each gate + * according to the current level assignment. + * + * This function must be called after level (re-)assignment and before + * querying `num_buffers`. + */ + void count_buffers() + { + if ( _outdated ) + { + update_fanout_info(); + } + + _ntk.foreach_node( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + _num_buffers[n] = count_buffers( n ); + } ); + } + +private: + uint32_t count_buffers( node const& n ) const + { + assert( !_outdated && "Please call `update_fanout_info()` first." ); + auto const& fo_infos = _fanouts[n]; + + if ( _ntk.fanout_size( n ) == 0u ) /* dangling */ + { + if ( !_ntk.is_pi( n ) ) + std::cerr << "[w] node " << n << " (which is not a PI) is dangling.\n"; + return 0u; + } + + if ( _ntk.fanout_size( n ) == 1u ) /* single fanout */ + { + assert( fo_infos.size() == 1u ); + return fo_infos.front().relative_depth - 1u; + } + + if ( _ps.assume.ci_capacity > 1 && _ntk.is_pi( n ) ) + { + if ( fo_infos.size() == 1u ) + { + assert( fo_infos.front().relative_depth == 1u ); + return 0u; + } + } + + assert( fo_infos.size() > 1u ); + auto it = fo_infos.begin(); + uint32_t count = it->num_edges - it->fanouts.size() - it->extrefs.size(); + uint32_t rd = it->relative_depth; + for ( ++it; it != fo_infos.end(); ++it ) + { + count += it->num_edges - it->fanouts.size() - it->extrefs.size() + it->relative_depth - rd - 1; + rd = it->relative_depth; + } + + return count; + } + + /* (Upper bound on) the additional depth caused by a balanced splitter tree at the output of node `n`. */ + uint32_t num_splitter_levels( node const& n ) const + { + assert( n < _ntk.size() ); + if ( _ntk.is_pi( n ) ) + { + if ( _ntk.fanout_size( n ) > _ps.assume.ci_capacity ) + return std::ceil( std::log( _ntk.fanout_size( n ) - _ps.assume.ci_capacity + 1 ) / std::log( _ps.assume.splitter_capacity ) ); + else + return 0u; + } + return std::ceil( std::log( _ntk.fanout_size( n ) ) / std::log( _ps.assume.splitter_capacity ) ); + } + + /* Update fanout_information of all nodes */ + void update_fanout_info() + { + _fanouts.reset(); + + _ntk.foreach_gate( [&]( auto const& n ) { + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + insert_fanout( ni, n ); + } ); + } ); + + _ntk.foreach_po( [&]( auto const& f, auto i ){ + insert_extref( _ntk.get_node( f ), i ); + } ); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + count_edges( n ); + } ); + + _outdated = false; + } + + /* Update the fanout_information of a node */ + template + bool update_fanout_info( node const& n ) + { + std::vector fos; + std::vector extrefs; + for ( auto it = _fanouts[n].begin(); it != _fanouts[n].end(); ++it ) + { + if ( it->fanouts.size() ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + fos.push_back( *it2 ); + } + if ( it->extrefs.size() ) + { + for ( auto it2 = it->extrefs.begin(); it2 != it->extrefs.end(); ++it2 ) + extrefs.push_back( *it2 ); + } + } + + _fanouts[n].clear(); + for ( auto& fo : fos ) + insert_fanout( n, fo ); + for ( auto& po : extrefs ) + insert_extref( n, po ); + + return count_edges( n ); + } + + void insert_fanout( node const& n, node const& fanout ) + { + assert( _levels[fanout] > _levels[n] ); + auto const rd = _levels[fanout] - _levels[n]; + auto& fo_infos = _fanouts[n]; + for ( auto it = fo_infos.begin(); it != fo_infos.end(); ++it ) + { + if ( it->relative_depth == rd ) + { + it->fanouts.push_back( fanout ); + ++it->num_edges; + return; + } + else if ( it->relative_depth > rd ) + { + fo_infos.insert( it, { rd, { fanout }, {}, 1u } ); + return; + } + } + fo_infos.push_back( { rd, { fanout }, {}, 1u } ); + } + + void insert_extref( node const& n, uint32_t idx ) + { + assert( _po_levels[idx] > _levels[n] ); + auto const rd = _po_levels[idx] - _levels[n]; + auto& fo_infos = _fanouts[n]; + for ( auto it = fo_infos.begin(); it != fo_infos.end(); ++it ) + { + if ( it->relative_depth == rd ) + { + it->extrefs.push_back( idx ); + ++it->num_edges; + return; + } + else if ( it->relative_depth > rd ) + { + fo_infos.insert( it, { rd, {}, {idx}, 1u } ); + return; + } + } + fo_infos.push_back( { rd, {}, {idx}, 1u } ); + } + + template + bool count_edges( node const& n ) + { + auto& fo_infos = _fanouts[n]; + + if ( fo_infos.size() == 0u || ( fo_infos.size() == 1u && fo_infos.front().num_edges == 1u ) ) + { + return true; + } + + if ( _ntk.is_pi( n ) && _ps.assume.ci_capacity > 1 ) + { + if ( fo_infos.front().relative_depth > 1u ) + fo_infos.push_front( { 1u, {}, {}, 0u } ); + } + else + { + assert( fo_infos.front().relative_depth > 1u ); + fo_infos.push_front( { 1u, {}, {}, 0u } ); + } + + auto it = fo_infos.end(); + --it; + uint32_t splitters; + while ( it != fo_infos.begin() ) + { + splitters = num_splitters( it->num_edges ); + auto rd = it->relative_depth; + --it; + if ( it->relative_depth < rd - 1 && splitters > 1 ) + { + it = fo_infos.insert( ++it, { rd - 1, {}, {}, splitters } ); + } + else + { + it->num_edges += splitters; + } + } + + assert( fo_infos.front().relative_depth == 1u ); + if constexpr ( verify ) + { + return _ntk.is_pi( n ) ? fo_infos.front().num_edges <= _ps.assume.ci_capacity : fo_infos.front().num_edges == 1u; + } + else + { + assert( _ntk.is_pi( n ) ? fo_infos.front().num_edges <= _ps.assume.ci_capacity : fo_infos.front().num_edges == 1u ); + return true; + } + } + + /* Return the number of splitters needed in one level lower */ + uint32_t num_splitters( uint32_t const& num_fanouts ) const + { + return std::ceil( float( num_fanouts ) / float( _ps.assume.splitter_capacity ) ); + } +#pragma endregion + +#pragma region Initial level assignment +public: + void set_scheduling_policy( buffer_insertion_params::scheduling_policy p ) + { + _ps.scheduling = p; + } + + /*! \brief Obtain the initial level assignment using the specified scheduling policy */ + void schedule() + { + if ( _ps.scheduling == buffer_insertion_params::provided ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + assert( _po_levels[i] > _levels[f] ); + _depth = std::max( _depth, _po_levels[i] - 1 ); + } ); + assert( _depth % _ps.assume.num_phases == 0 ); + return; + } + + if ( _ps.scheduling == buffer_insertion_params::better_depth || _ps.scheduling == buffer_insertion_params::ASAP_depth || _ps.scheduling == buffer_insertion_params::ALAP_depth ) + { + fanout_view f_ntk{ _ntk }; + /* Optimum-depth ALAP scheduling */ + ALAP_depth( f_ntk ); + count_buffers(); + auto const num_buf_ALAP_depth = num_buffers(); + + if ( _ps.scheduling == buffer_insertion_params::ALAP_depth ) + return; + + /* Optimum-depth ALAP scheduling: no balanced trees */ + ASAP_depth( f_ntk, false ); + count_buffers(); + auto const num_buf_ASAP_depth = num_buffers(); + + if ( _ps.scheduling == buffer_insertion_params::ASAP_depth ) + return; + + /* Revert to optimum-depth ALAP scheduling if better */ + if ( num_buf_ALAP_depth < num_buf_ASAP_depth ) + { + ALAP_depth( f_ntk ); + } + return; + } + + ASAP(); + if ( _ps.scheduling == buffer_insertion_params::ALAP ) + { + ALAP(); + } + else if ( _ps.scheduling == buffer_insertion_params::better ) + { + count_buffers(); + auto num_buf_ASAP = num_buffers(); + ALAP(); + count_buffers(); + if ( num_buffers() > num_buf_ASAP ) + { + ASAP(); + } + } + } + + /*! \brief ASAP scheduling */ + void ASAP() + { + _depth = 0; + _levels.reset( 0 ); + _ntk.incr_trav_id(); + + _ntk.foreach_po( [&]( auto const& f, auto i ) { + auto const no = _ntk.get_node( f ); + _po_levels[i] = compute_levels_ASAP( no ) + num_splitter_levels( no ) + 1; + if ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases != 0 ) // phase alignment + { + _po_levels[i] += _ps.assume.num_phases - ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases ); + } + _depth = std::max( _depth, _po_levels[i] - 1 ); + } ); + assert( _depth % _ps.assume.num_phases == 0 ); + + if ( _ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + (void)f; + _po_levels[i] = _depth + 1; + } ); + } + + /* dangling PIs */ + _ntk.foreach_pi( [&]( auto const& n ){ + if ( _ntk.visited( n ) != _ntk.trav_id() ) + _levels[n] = _ps.assume.ci_phases[0]; + } ); + + _outdated = true; + _is_scheduled_ASAP = true; + } + + /*! \brief ASAP optimal-depth scheduling + * + * ASAP_depth should follow right after ALAP_depth (i.e., initialization). + * + * \param try_regular tries to insert balanced trees when sufficient slack. + */ + void ASAP_depth( fanout_view const& f_ntk, bool try_regular ) + { + node_map mobility( _ntk, std::numeric_limits::max() ); + + if ( !_ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + (void)f; + _po_levels[i] = 0; + } ); + } + + _ntk.foreach_pi( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + { + mobility[n] = _levels[n] - _ps.assume.ci_phases[0]; + compute_mobility_ASAP( f_ntk, n, mobility, try_regular ); + } + } ); + + _ntk.foreach_gate( [&]( auto const& n ) { + compute_mobility_ASAP( f_ntk, n, mobility, try_regular ); + } ); + + if ( !_ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( _po_levels[i] == 0 ) + { + assert( _ntk.is_constant( _ntk.get_node( f ) ) ); + _po_levels[i] = 1; + } + else if ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases != 0 ) // phase alignment + { + _po_levels[i] += _ps.assume.num_phases - ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases ); + } + _depth = std::max( _depth, _po_levels[i] - 1 ); + } ); + assert( _depth % _ps.assume.num_phases == 0 ); + } + + _outdated = true; + _is_scheduled_ASAP = true; + } + + /*! \brief ALAP scheduling. + * + * ALAP should follow right after ASAP (i.e., initialization) without other optimization in between. + */ + void ALAP() + { + assert( _depth % _ps.assume.num_phases == 0 ); + _levels.reset( 0 ); + _ntk.incr_trav_id(); + + _ntk.foreach_po( [&]( auto const& f, auto i ) { + _po_levels[i] = _depth + 1; + const auto n = _ntk.get_node( f ); + + if ( !_ntk.is_constant( n ) && _ntk.visited( n ) != _ntk.trav_id() ) + { + _levels[n] = _depth - num_splitter_levels( n ); + compute_levels_ALAP( n ); + } + } ); + + /* dangling PIs */ + _ntk.foreach_pi( [&]( auto const& n ){ + if ( _ntk.visited( n ) != _ntk.trav_id() ) + _levels[n] = _ps.assume.ci_phases[0]; + } ); + + _outdated = true; + _is_scheduled_ASAP = false; + } + + /*! \brief ALAP depth-optimal sheduling */ + void ALAP_depth( fanout_view const& f_ntk ) + { + _levels.reset( 0 ); + topo_view topo_ntk{ _ntk }; + + /* compute ALAP */ + _depth = std::numeric_limits::max() - 1; + uint32_t min_level = std::numeric_limits::max() - 1; + topo_ntk.foreach_node_reverse( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) && _ntk.fanout_size( n ) > 0 ) + { + compute_levels_ALAP_depth( f_ntk, n ); + min_level = std::min( min_level, _levels[n] ); + } + } ); + + /* move everything down by `delta` */ + uint32_t delta = min_level; + /* phase alignment for PO: depth % num_phases = 0 */ + if ( ( _depth - delta ) % _ps.assume.num_phases != 0 ) + { + delta -= _ps.assume.num_phases - ( ( _depth - delta ) % _ps.assume.num_phases ); + } + + /* level of the lowest PI >= ci_phases[0] */ + while ( min_level - delta < _ps.assume.ci_phases[0] ) + { + delta -= _ps.assume.num_phases; + } + /* move PIs down to an acceptable level */ + if ( _ps.assume.balance_cios ) + { + _ntk.foreach_pi( [&]( auto const& n ) { + if ( _ntk.fanout_size( n ) == 0 ) + { + _levels[n] = _ps.assume.ci_phases[0]; + } + else if ( !_ntk.is_constant( n ) ) + { + _levels[n] = _levels[n] - delta; + for ( auto rit = _ps.assume.ci_phases.rbegin(); rit != _ps.assume.ci_phases.rend(); ++rit ) + { + if ( *rit <= _levels[n] ) + { + _levels[n] = *rit; + return; + } + } + assert( false ); + } + } ); + } + else + { + _ntk.foreach_pi( [&]( auto const& n ) { + if ( _ntk.fanout_size( n ) == 0 ) + { + _levels[n] = _ps.assume.ci_phases[0]; + } + else if ( !_ntk.is_constant( n ) ) + { + _levels[n] = _levels[n] - delta; + while ( !is_acceptable_ci_lvl( _levels[n] ) ) + { + assert( _levels[n] > 0 ); + --_levels[n]; + } + } + } ); + } + + _ntk.foreach_gate( [&]( auto const& n ) { + _levels[n] = _levels[n] - delta; + } ); + _depth -= delta; + assert( _depth % _ps.assume.num_phases == 0 ); + if ( _ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + (void)f; + _po_levels[i] = _depth + 1; + } ); + } + else + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + _po_levels[i] = 1; + else + { + _po_levels[i] = _levels[f] + num_splitter_levels( _ntk.get_node( f ) ); + if ( _po_levels[i] % _ps.assume.num_phases > 0 ) + _po_levels[i] += _ps.assume.num_phases - ( _po_levels[i] % _ps.assume.num_phases ); + ++_po_levels[i]; + } + } ); + } + + _outdated = true; + _is_scheduled_ASAP = false; + } + +private: + uint32_t compute_levels_ASAP( node const& n ) + { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + { + return _levels[n]; + } + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.is_constant( n ) ) + { + return _levels[n] = 0; + } + else if ( _ntk.is_pi( n ) ) + { + return _levels[n] = _ps.assume.ci_phases[0]; + } + + uint32_t level{ 0 }; + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + auto fi_level = compute_levels_ASAP( ni ) + num_splitter_levels( ni ); + level = std::max( level, fi_level ); + } + } ); + + return _levels[n] = level + 1; + } + + bool is_acceptable_ci_lvl( uint32_t lvl ) const + { + if ( _ps.assume.balance_cios ) + { + for ( auto const& p : _ps.assume.ci_phases ) + { + if ( lvl == p ) + return true; + } + return false; + } + else + { + for ( auto const& p : _ps.assume.ci_phases ) + { + // for example, if num_phases = 4, ci_phases = {5}, + // then lvl = 1 will not be acceptable, but lvl = 5 or lvl = 9 will + if ( lvl % _ps.assume.num_phases == p % _ps.assume.num_phases && lvl >= p ) + return true; + } + return false; + } + } + + void compute_levels_ALAP( node const& n ) + { + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.is_pi( n ) ) + { + if ( _ps.assume.balance_cios ) + { + for ( auto rit = _ps.assume.ci_phases.rbegin(); rit != _ps.assume.ci_phases.rend(); ++rit ) + { + if ( *rit <= _levels[n] ) + { + _levels[n] = *rit; + return; + } + } + assert( false ); + } + else + { + while ( !is_acceptable_ci_lvl( _levels[n] ) ) + { + assert( _levels[n] > 0 ); + --_levels[n]; + } + } + return; + } + + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + assert( _levels[n] > num_splitter_levels( ni ) ); + auto fi_level = _levels[n] - num_splitter_levels( ni ) - 1; + if ( _ntk.visited( ni ) != _ntk.trav_id() || _levels[ni] > fi_level ) + { + _levels[ni] = fi_level; + compute_levels_ALAP( ni ); + } + } + } ); + } + + void compute_levels_ALAP_depth( fanout_view const& ntk, node const& n ) + { + std::vector level_assignment; + level_assignment.reserve( ntk.fanout_size( n ) ); + + /* if node is a PO, add levels */ + for ( auto i = ntk.fanout( n ).size(); i < ntk.fanout_size( n ); ++i ) + level_assignment.push_back( _depth + 1 ); + + /* get fanout levels */ + ntk.foreach_fanout( n, [&]( auto const& f ) { + level_assignment.push_back( _levels[f] ); + } ); + + /* sort by descending order of levels */ + std::stable_sort( level_assignment.begin(), level_assignment.end(), std::greater() ); + + /* simulate splitter tree reconstruction */ + uint32_t nodes_in_level = 0; + uint32_t last_level = level_assignment.front(); + for ( int const l : level_assignment ) + { + if ( l == last_level ) + { + ++nodes_in_level; + } + else + { + /* update splitters */ + for ( auto i = 0; ( i < last_level - l ) && ( nodes_in_level != 1 ); ++i ) + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + + ++nodes_in_level; + last_level = l; + } + } + + /* search for a feasible level for node n */ + --last_level; + if ( _ntk.is_pi( n ) ) + { + while ( nodes_in_level > _ps.assume.ci_capacity ) + { + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + --last_level; + } + } + else + { + while ( nodes_in_level > 1 ) + { + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + --last_level; + } + } + + _levels[n] = last_level; + } + + void compute_mobility_ASAP( fanout_view const& ntk, node const& n, node_map& mobility, bool try_regular ) + { + assert( mobility[n] <= _levels[n] ); + /* commit ASAP scheduling */ + uint32_t level_n = _levels[n] - mobility[n]; + _levels[n] = level_n; + + /* try to fit a balanced tree */ + if ( try_regular ) + { + uint32_t fo_level = num_splitter_levels( n ); + bool valid = true; + ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( level_n + fo_level + 1 > _levels[f] ) + valid = false; + return valid; + } ); + + if ( valid ) + { + ntk.foreach_fanout( n, [&]( auto const& f ) { + mobility[f] = std::min( mobility[f], _levels[f] - level_n - fo_level - 1 ); + } ); + return; + } + } + + /* keep current splitter structure, selecting the mobility based on the buffers */ + std::vector> level_assignment; + level_assignment.reserve( _ntk.fanout_size( n ) ); + + /* if node is a PO, add levels */ + if ( ntk.fanout( n ).size() < ntk.fanout_size( n ) ) + { + ntk.foreach_po( [&]( auto const& f, auto i ){ + if ( ntk.get_node( f ) == n ) + level_assignment.push_back( { i, _depth + 1, 0 } ); + } ); + assert( level_assignment.size() == ntk.fanout_size( n ) - ntk.fanout( n ).size() ); + } + + /* get fanout levels */ + ntk.foreach_fanout( n, [&]( auto const& f ) { + level_assignment.push_back( { ntk.node_to_index( f ), _levels[f], 0 } ); + } ); + + /* dangling PI */ + if ( level_assignment.empty() ) + { + return; + } + + /* sort by descending order of levels */ + std::stable_sort( level_assignment.begin(), level_assignment.end(), []( auto const& a, auto const& b ) { + return a[1] > b[1]; + } ); + + /* simulate splitter tree reconstruction */ + uint32_t nodes_in_level = 0; + uint32_t nodes_in_last_level = _ps.assume.splitter_capacity; + uint32_t last_level = level_assignment.front()[1]; + for ( auto i = 0; i < level_assignment.size(); ++i ) + { + uint32_t l = level_assignment[i][1]; + if ( l == last_level ) + { + ++nodes_in_level; + } + else + { + /* update splitters */ + uint32_t mobility_update = 0; + for ( auto j = 0; j < last_level - l; ++j ) + { + if ( nodes_in_level == 1 ) + { + ++mobility_update; + } + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + } + + if ( mobility_update ) + { + for ( auto j = 0; j < i; ++j ) + level_assignment[j][2] += mobility_update; + } + + ++nodes_in_level; + last_level = l; + } + } + + /* search a feasible level for node n */ + uint32_t mobility_update = 0; + for ( auto i = level_n + 1; i < last_level; ++i ) + { + if ( nodes_in_level == 1 || ( _ntk.is_pi( n ) && nodes_in_level <= _ps.assume.ci_capacity ) ) + ++mobility_update; + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + } + + /* update mobilities */ + for ( auto const& v : level_assignment ) + { + if ( v[1] != _depth + 1 ) + { + mobility[v[0]] = std::min( mobility[v[0]], v[2] + mobility_update ); + } + } + + /* update po_level, if possible */ + if ( !_ps.assume.balance_cios ) + { + for ( auto const& v : level_assignment ) + { + if ( v[1] == _depth + 1 ) + { + _po_levels[v[0]] = std::max( _po_levels[v[0]], _depth + 1 - v[2] - mobility_update ); + } + else + { + break; + } + } + } + } +#pragma endregion + +#pragma region Dump buffered network +public: + /*! \brief Dump buffered network + * + * After level assignment, (optimization), and buffer counting, this method + * can be called to dump the resulting buffered network. + */ + template + void dump_buffered_network( BufNtk& bufntk ) const + { + static_assert( is_buffered_network_type_v, "BufNtk is not a buffered network type" ); + static_assert( has_is_buf_v, "BufNtk does not implement the is_buf method" ); + static_assert( has_create_buf_v, "BufNtk does not implement the create_buf method" ); + assert( !_outdated && "Please call `count_buffers()` first." ); + + using buf_signal = typename BufNtk::signal; + using fanout_tree = std::vector>; + + node_map node_to_signal( _ntk ); + node_map buffers( _ntk ); + + /* constants */ + node_to_signal[_ntk.get_constant( false )] = bufntk.get_constant( false ); + buffers[_ntk.get_constant( false )].emplace_back( 1, bufntk.get_constant( false ) ); + if ( _ntk.get_node( _ntk.get_constant( false ) ) != _ntk.get_node( _ntk.get_constant( true ) ) ) + { + node_to_signal[_ntk.get_constant( true )] = bufntk.get_constant( true ); + buffers[_ntk.get_constant( true )].emplace_back( 1, bufntk.get_constant( true ) ); + } + + /* PIs */ + _ntk.foreach_pi( [&]( auto const& n ) { + node_to_signal[n] = bufntk.create_pi(); + create_buffer_chain( bufntk, buffers, n, node_to_signal[n] ); + } ); + + /* gates: assume topological order */ + _ntk.foreach_gate( [&]( auto const& n ) { + std::vector children; + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + buf_signal s; + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + s = node_to_signal[fi]; + else + s = get_buffer_at_relative_depth( bufntk, buffers[fi], _levels[n] - _levels[fi] - 1 ); + children.push_back( _ntk.is_complemented( fi ) ? !s : s ); + } ); + node_to_signal[n] = bufntk.clone_node( _ntk, n, children ); + create_buffer_chain( bufntk, buffers, n, node_to_signal[n] ); + } ); + + /* POs */ + _ntk.foreach_po( [&]( auto const& f, auto i ) { + buf_signal s; + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + s = node_to_signal[f]; + else + s = get_buffer_at_relative_depth( bufntk, buffers[f], _po_levels[i] - _levels[f] - 1 ); + assert( _ps.assume.ignore_co_negation ); + bufntk.create_po( _ntk.is_complemented( f ) ? !s : s ); + } ); + + assert( bufntk.size() - bufntk.num_pis() - bufntk.num_gates() - 1 == num_buffers() ); + } + +private: + template + void create_buffer_chain( BufNtk& bufntk, Buffers& buffers, node const& n, typename BufNtk::signal const& s ) const + { + if ( _ntk.fanout_size( n ) == 0 ) + return; /* dangling */ + + assert( _fanouts[n].size() > 0u ); + buffers[n].resize( _fanouts[n].back().relative_depth ); + auto& fot = buffers[n]; + fot[0].push_back( s ); + for ( auto i = 1u; i < fot.size(); ++i ) + { + fot[i].push_back( bufntk.create_buf( fot[i-1].back() ) ); + } + } + + template + typename BufNtk::signal get_buffer_at_relative_depth( BufNtk& bufntk, FOT& fot, uint32_t rd ) const + { + typename BufNtk::signal b = fot[rd].back(); + if ( rd == 0 && bufntk.is_pi( bufntk.get_node( b ) ) ) + { + assert( bufntk.fanout_size( bufntk.get_node( b ) ) < _ps.assume.ci_capacity ); + return b; + } + if ( bufntk.fanout_size( bufntk.get_node( b ) ) == _ps.assume.splitter_capacity ) + { + assert( rd > 0 ); + typename BufNtk::signal b_lower = get_buffer_at_relative_depth( bufntk, fot, rd - 1 ); + b = bufntk.create_buf( b_lower ); + fot[rd].push_back( b ); + } + return b; + } +#pragma endregion + +#pragma region Post-dump optimization +public: +template +uint32_t remove_buffer_chains( BufNtk& ntk ) const +{ + static_assert( is_buffered_network_type_v, "BufNtk is not a buffered network" ); + + uint32_t max_chain = 0; + ntk.incr_trav_id(); + ntk.foreach_po( [&]( auto f ){ + remove_buffer_chains_rec( ntk, ntk.get_node( f ), 0, max_chain ); + } ); + return max_chain; +} + +private: +template +std::pair remove_buffer_chains_rec( BufNtk& ntk, typename BufNtk::node n, typename BufNtk::node parent, uint32_t& max_chain ) const +{ + if ( ntk.visited( n ) == ntk.trav_id() ) + return std::make_pair( 0, n ); + ntk.set_visited( n, ntk.trav_id() ); + if ( ntk.is_pi( n ) ) + return std::make_pair( 0, n ); + + if ( ntk.is_buf( n ) ) + { + // splitter + if ( ntk.fanout_size( n ) > 1 ) + { + ntk.foreach_fanin( n, [&]( auto f ){ + remove_buffer_chains_rec( ntk, ntk.get_node( f ), n, max_chain ); + } ); + return std::make_pair( 0, n ); + } + + // single-output buffer: can be part of a chain to be removed + std::pair ret; + ntk.foreach_fanin( n, [&]( auto f ){ + auto [count, origin] = remove_buffer_chains_rec( ntk, ntk.get_node( f ), n, max_chain ); + if ( count % _ps.assume.num_phases == _ps.assume.num_phases - 1 ) + { + // TODO: take care of complementation + if ( parent != 0 ) + { + ntk.replace_in_node( parent, n, ntk.make_signal( origin ) ); + ntk.take_out_node( n ); + } + else + { + ntk.replace_in_outputs( n, ntk.make_signal( origin ) ); + ntk.take_out_node( n ); + } + max_chain = std::max( count + 1, max_chain ); + } + ret = std::make_pair( count + 1, origin ); + } ); + return ret; + } + + // gate + ntk.foreach_fanin( n, [&]( auto f ){ + remove_buffer_chains_rec( ntk, ntk.get_node( f ), n, max_chain ); + } ); + return std::make_pair( 0, n ); +} +#pragma endregion + +public: + /*! \brief Optimize with chunked movement using the specified optimization policy. */ + void optimize() + { + if ( _ps.optimization_effort == buffer_insertion_params::none ) + { + return; + } + //else if ( _ps.optimization_effort == buffer_insertion_params::optimal ) + //{ + // if constexpr ( has_get_network_name_v ) + // optimize_with_smt( _ntk.get_network_name() ); + // else + // optimize_with_smt( "" ); + // return; + //} + + if ( _outdated ) + { + update_fanout_info(); + } + + bool updated; + do + { + updated = find_and_move_chunks(); + } while ( updated && _ps.optimization_effort == buffer_insertion_params::until_sat ); + single_gate_movement(); + } + +#pragma region Chunked movement +private: + struct io_interface + { + node c; // chunk node + node o; // outside node + }; + + struct po_interface + { + node c; // chunk node + uint32_t o; // PO index + }; + + struct chunk + { + uint32_t id; + std::vector members{}; + std::vector input_interfaces{}; + std::vector output_interfaces{}; + std::vector po_interfaces{}; + int32_t slack{ std::numeric_limits::max() }; + int32_t benefits{ 0 }; + }; + + bool is_ignored( node const& n ) const + { + return _ntk.is_constant( n ); + } + + bool is_fixed( node const& n ) const + { + return _ps.assume.balance_cios && _ps.assume.ci_phases.size() == 1 && _ntk.is_pi( n ); + } + + bool find_and_move_chunks() + { + bool updated = false; + count_buffers(); + uint32_t num_buffers_before = num_buffers(); + _start_id = _ntk.trav_id(); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( is_ignored( n ) || is_fixed( n ) || _ntk.visited( n ) > _start_id /* belongs to a chunk */ ) + { + return true; + } + + _ntk.incr_trav_id(); + chunk c{ _ntk.trav_id() }; + recruit( n, c ); + if ( c.members.size() > _ps.max_chunk_size ) + { + return true; /* skip */ + } + cleanup_interfaces( c ); + + auto moved = analyze_chunk_down( c ); + if ( !moved ) + moved = analyze_chunk_up( c ); + updated |= moved; + return true; + } ); + + count_buffers(); + assert( num_buffers() <= num_buffers_before ); + return updated && num_buffers() < num_buffers_before; + } + + void single_gate_movement() + { + _ntk.foreach_node( [&]( auto const& n ) { + if ( is_ignored( n ) || is_fixed( n ) ) + return; + + _ntk.incr_trav_id(); + chunk c{ _ntk.trav_id() }; + c.members.emplace_back( n ); + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !is_ignored( ni ) ) + c.input_interfaces.push_back( { n, ni } ); + } ); + auto const& fanout_info = _fanouts[n]; + for ( auto it = fanout_info.begin(); it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + c.output_interfaces.push_back( { n, *it2 } ); + for ( auto it2 = it->extrefs.begin(); it2 != it->extrefs.end(); ++it2 ) + c.po_interfaces.push_back( { n, *it2 } ); + } + + if ( !analyze_chunk_down( c ) ) + analyze_chunk_up( c ); + } ); + } + + void recruit( node const& n, chunk& c ) + { + if ( _ntk.visited( n ) == c.id ) + return; + + assert( _ntk.visited( n ) <= _start_id ); + assert( !is_fixed( n ) ); + assert( !is_ignored( n ) ); + + _ntk.set_visited( n, c.id ); + c.members.emplace_back( n ); + recruit_fanins( n, c ); + recruit_fanouts( n, c ); + } + + void recruit_fanins( node const& n, chunk& c ) + { + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !is_ignored( ni ) && _ntk.visited( ni ) != c.id ) + { + if ( is_fixed( ni ) ) + c.input_interfaces.push_back( { n, ni } ); + else if ( are_close( ni, n ) ) + recruit( ni, c ); + else + c.input_interfaces.push_back( { n, ni } ); + } + } ); + } + + void recruit_fanouts( node const& n, chunk& c ) + { + auto const& fanout_info = _fanouts[n]; + if ( fanout_info.size() == 0 ) /* dangling */ + return; + + auto it = fanout_info.begin(); + if ( _ntk.fanout_size( n ) == 1 ) /* single fanout */ + { + assert( fanout_info.size() == 1 ); + if ( it->fanouts.size() == 1 ) /* single gate fanout */ + { + if ( it->relative_depth == 1 ) + recruit( it->fanouts.front(), c ); + else + c.output_interfaces.push_back( { n, it->fanouts.front() } ); + } + else /* single PO fanout */ + { + assert( it->extrefs.size() == 1 ); + c.po_interfaces.push_back( { n, it->extrefs.front() } ); + } + return; + } + + for ( ; it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->extrefs.begin(); it2 != it->extrefs.end(); ++it2 ) + c.po_interfaces.push_back( { n, *it2 } ); + } + it = fanout_info.begin(); + + if ( _ps.assume.ci_capacity > 1 && _ntk.is_pi( n ) ) + { + if ( it->relative_depth == 1 ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + recruit( *it2, c ); + it++; + } + if ( it->relative_depth == 2 && fanout_info.front().num_edges == _ps.assume.ci_capacity ) + { + assert( fanout_info.front().relative_depth == 1 ); + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + recruit( *it2, c ); + it++; + } + for ( ; it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( _ntk.visited( *it2 ) != c.id ) + c.output_interfaces.push_back( { n, *it2 } ); + } + } + return; + } + + for ( ; it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( it->relative_depth == 2 ) + recruit( *it2, c ); + else if ( _ntk.visited( *it2 ) != c.id ) + c.output_interfaces.push_back( { n, *it2 } ); + } + } + } + + bool are_close( node const& ni, node const& n ) + { + auto const& fanout_info = _fanouts[ni]; + + if ( _ps.assume.ci_capacity > 1 && _ntk.is_pi( ni ) ) + { + auto const& front_fanouts = fanout_info.front().fanouts; + if ( fanout_info.front().relative_depth == 1 ) + { + if ( std::find( front_fanouts.begin(), front_fanouts.end(), n ) != front_fanouts.end() ) + return true; + if ( fanout_info.front().num_edges < _ps.assume.ci_capacity ) + return false; + } + else if ( _ntk.fanout_size( ni ) <= _ps.assume.ci_capacity ) + return false; + assert( fanout_info.size() > 1 ); + } + + if ( fanout_info.size() == 1 && fanout_info.front().relative_depth == 1 ) + { + assert( fanout_info.front().fanouts.front() == n ); + return true; + } + if ( fanout_info.size() > 1 ) + { + auto it = fanout_info.begin(); + it++; + if ( it->relative_depth > 2 ) + return false; + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( *it2 == n ) + return true; + } + } + return false; + } + + void cleanup_interfaces( chunk& c ) + { + for ( int i = 0; i < c.input_interfaces.size(); ++i ) + { + if ( _ntk.visited( c.input_interfaces[i].o ) == c.id ) + { + c.input_interfaces.erase( c.input_interfaces.begin() + i ); + --i; + } + } + for ( int i = 0; i < c.output_interfaces.size(); ++i ) + { + if ( _ntk.visited( c.output_interfaces[i].o ) == c.id ) + { + c.output_interfaces.erase( c.output_interfaces.begin() + i ); + --i; + } + } + } + + bool analyze_chunk_down( chunk c ) + { + count_buffers(); + auto buffers_before = num_buffers(); + + std::set marked_oi; + for ( auto oi : c.output_interfaces ) + { + if ( marked_oi.find( oi.c ) == marked_oi.end() ) + { + marked_oi.insert( oi.c ); + --c.benefits; + } + } + + for ( auto ii : c.input_interfaces ) + { + auto const rd = _levels[ii.c] - _levels[ii.o]; + auto const lowest = lowest_spot( ii.o ); + if ( rd <= lowest ) + { + c.slack = 0; + break; + } + c.slack = std::min( c.slack, int32_t( rd - lowest ) ); + pseudo_move( ii.o, ii.c, rd, lowest ); + if ( _fanouts[ii.o].back().relative_depth == rd && _fanouts[ii.o].back().num_edges == 0 ) // `ii.c` is the last highest fanout of `ii.o` + { + ++c.benefits; + } + } + + if ( c.po_interfaces.size() > 0 ) + { + if ( !_ps.assume.balance_cios && c.slack >= _ps.assume.num_phases ) + { + c.slack -= c.slack % _ps.assume.num_phases; + } + else + { + for ( auto poi : c.po_interfaces ) + { + if ( marked_oi.find( poi.c ) == marked_oi.end() ) + --c.benefits; + } + } + } + + std::vector pi_members; + for ( auto m : c.members ) + { + if ( _ntk.is_pi( m ) ) + { + pi_members.emplace_back( m ); + c.slack = std::min( c.slack, int32_t( _levels[m] ) ); + } + } + if ( pi_members.size() > 0 ) + { + while ( c.slack > 0 ) + { + bool ok = true; + for ( auto m : pi_members ) + { + if ( _levels[m] < c.slack || !is_acceptable_ci_lvl( _levels[m] - c.slack ) ) + { + ok = false; + break; + } + } + if ( !ok ) + --c.slack; + else + break; + } + } + + if ( c.benefits > 0 && c.slack > 0 ) + { + bool legal = true; + + for ( auto m : c.members ) + _levels[m] -= c.slack; + if ( !_ps.assume.balance_cios && c.slack >= _ps.assume.num_phases ) + { + for ( auto poi : c.po_interfaces ) + _po_levels[poi.o] -= c.slack; + } + for ( auto m : c.members ) + update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + legal &= update_fanout_info( ii.o ); + + _outdated = true; + if ( legal ) + count_buffers(); + if ( !legal || num_buffers() >= buffers_before ) + { + /* UNDO */ + for ( auto m : c.members ) + _levels[m] += c.slack; + if ( !_ps.assume.balance_cios && c.slack >= _ps.assume.num_phases ) + { + for ( auto poi : c.po_interfaces ) + _po_levels[poi.o] += c.slack; + } + for ( auto m : c.members ) + update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + update_fanout_info( ii.o ); + _outdated = true; + return false; + } + + _start_id = _ntk.trav_id(); + return true; + } + else + { + /* reset fanout_infos of input_interfaces because num_edges may be modified by pseudo_move */ + for ( auto ii : c.input_interfaces ) + update_fanout_info( ii.o ); + _outdated = true; + return false; + } + } + + /* relative_depth of the lowest available spot in the fanout tree of n */ + uint32_t lowest_spot( node const& n ) const + { + auto const& fanout_info = _fanouts[n]; + assert( fanout_info.size() ); + + auto it = fanout_info.begin(); + uint32_t rd_prev = 1; + uint32_t num_splitters_prev = 1; + if ( _ntk.is_pi( n ) && _ps.assume.ci_capacity > 1 ) + { + if ( it->num_edges <= _ps.assume.ci_capacity ) + return 1; + else + num_splitters_prev = _ps.assume.ci_capacity - it->fanouts.size() - it->extrefs.size(); + } + else if ( fanout_info.size() == 1 ) // single fanout + { + return 1; + } + + ++it; // skip the first splitter at rd=1 + for ( ; it != fanout_info.end(); ++it ) + { + if ( it->relative_depth > rd_prev + 1 ) // level skip => must not full + { + return rd_prev + 1; + } + else if ( it->num_edges == _ps.assume.splitter_capacity * num_splitters_prev ) // full layer + { + num_splitters_prev = it->num_edges - it->fanouts.size() - it->extrefs.size(); + rd_prev = it->relative_depth; + } + else + { + return it->relative_depth; + } + } + // all full + return fanout_info.back().relative_depth + 1; + } + + /* move `no`, which is a fanout of `n`, from `from_rd` to `to_rd` */ + void pseudo_move( node const& n, node const& no, uint32_t from_rd, uint32_t to_rd ) + { + assert( from_rd > to_rd ); + auto& fanout_info = _fanouts[n]; + auto it = fanout_info.begin(); + for ( ; it != fanout_info.end(); ++it ) + { + if ( it->relative_depth == to_rd ) + { + ++it->num_edges; + it->fanouts.push_back( no ); + break; + } + else if ( it->relative_depth > to_rd ) + { + fanout_info.insert( it, {to_rd, {no}, {}, 2} ); + break; + } + } + for ( ; it != fanout_info.end(); ++it ) + { + if ( it->relative_depth == from_rd ) + { + --it->num_edges; + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( *it2 == no ) + { + it->fanouts.erase( it2 ); + return; + } + } + assert( false ); + } + } + assert( false ); + } + + bool analyze_chunk_up( chunk c ) + { + for ( auto ii : c.input_interfaces ) + { + if ( _fanouts[ii.o].back().relative_depth == _levels[ii.c] - _levels[ii.o] ) // is highest fanout + --c.benefits; + } + + std::set marked_oi; + for ( auto oi : c.output_interfaces ) + { + if ( marked_oi.find( oi.c ) == marked_oi.end() ) + { + marked_oi.insert( oi.c ); + ++c.benefits; + } + auto const& fanout_info = _fanouts[oi.c]; + if ( fanout_info.size() == 1 ) /* single fanout */ + c.slack = std::min( c.slack, int32_t( fanout_info.front().relative_depth - 1 ) ); + else + c.slack = std::min( c.slack, int32_t( _levels[oi.o] - _levels[oi.c] - 2 ) ); + } + + std::vector po_to_move; + if ( c.po_interfaces.size() > 0 ) + { + for ( auto poi : c.po_interfaces ) + { + if ( _levels[poi.c] + num_splitter_levels( poi.c ) + c.slack >= _po_levels[poi.o] ) + { + if ( _ps.assume.balance_cios ) + c.slack = std::min( c.slack, int32_t( _po_levels[poi.o] - _levels[poi.c] - num_splitter_levels( poi.c ) - 1 ) ); + else + { + c.slack = std::min( c.slack, int32_t( _depth + 1 - _po_levels[poi.o] ) ); + po_to_move.emplace_back( poi.o ); + } + } + else + { + if ( marked_oi.find( poi.c ) == marked_oi.end() ) + ++c.benefits; + } + } + } + + if ( c.benefits <= 0 || c.slack <= 0 ) + return false; + + std::vector pi_members; + for ( auto m : c.members ) + { + if ( _ntk.is_pi( m ) ) + pi_members.emplace_back( m ); + } + if ( pi_members.size() > 0 ) + { + while ( c.slack > 0 ) + { + bool ok = true; + for ( auto m : pi_members ) + { + if ( !is_acceptable_ci_lvl( _levels[m] + c.slack ) ) + { + ok = false; + break; + } + } + if ( !ok ) + --c.slack; + else + break; + } + } + if ( po_to_move.size() > 0 ) + c.slack -= c.slack % _ps.assume.num_phases; + + if ( c.benefits > 0 && c.slack > 0 ) + { + count_buffers(); + bool legal = true; + auto buffers_before = num_buffers(); + + for ( auto m : c.members ) + _levels[m] += c.slack; + for ( auto po : po_to_move ) + _po_levels[po] += c.slack; + for ( auto m : c.members ) + legal &= update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + legal &= update_fanout_info( ii.o ); + + _outdated = true; + if ( legal ) + count_buffers(); + if ( !legal || num_buffers() >= buffers_before ) + { + /* UNDO */ + for ( auto m : c.members ) + _levels[m] -= c.slack; + for ( auto po : po_to_move ) + _po_levels[po] -= c.slack; + for ( auto m : c.members ) + update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + update_fanout_info( ii.o ); + _outdated = true; + return false; + } + + _start_id = _ntk.trav_id(); + return true; + } + else + { + return false; + } + } +#pragma endregion + +#pragma region Global optimal by SMT +private: +//#include "optimal_buffer_insertion.hpp" +#pragma endregion + +private: + struct fanout_information + { + uint32_t relative_depth{ 0u }; + std::list fanouts; + std::list extrefs; // IDs of POs (as in `_ntk.foreach_po`) + uint32_t num_edges{ 0u }; + }; + using fanouts_by_level = std::list; + + Ntk const& _ntk; + buffer_insertion_params _ps; + bool _outdated{ true }; + bool _is_scheduled_ASAP{ true }; + + /* The following data structures uniquely define the state (i.e. schedule) of the algorithm/flow. + The rest (`_fanouts` and `_num_buffers`) are computed from these by calling `count_buffers()`. */ + node_map _levels; + std::vector _po_levels; // imaginary node, must be at `num_phases * k + 1` + uint32_t _depth{ 0u }; + + /* Guarantees on `_fanouts` (when not `_outdated`): + * - Sum of `_fanouts[n][l].fanouts.length() + _fanouts[n][l].extrefs.length()` over all `l`s + * should be equal to `ntk.fanout_size( n )`. + * - If having only one fanout: `_fanouts[n].size() == 1`. + * - If having multiple fanouts: `_fanouts[n]` must have at least two elements, + * and the first element must have `relative_depth == 1` and `num_edges == 1`. + * - If `ci_capacity > 1`, `_fanouts[PI].size()` may be 1. + */ + node_map _fanouts; + node_map _num_buffers; + + node_map, Ntk> _timeframes; // only for SMT; the most extreme min/max + uint32_t _start_id; // for chunked movement +}; /* buffer_insertion */ + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_verification.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_verification.hpp new file mode 100644 index 00000000000..ca899c88c72 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/buffer_verification.hpp @@ -0,0 +1,292 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file buffer_verification.hpp + \brief Verify buffered networks according to AQFP constraints + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/depth_view.hpp" +#include "aqfp_assumptions.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +template +void schedule_fanin_cone( Ntk& ntk, typename Ntk::node const& n, uint32_t l ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + ntk.set_visited( n, ntk.trav_id() ); + ntk.set_level( n, l ); + + ntk.foreach_fanin( n, [&]( auto const& fi ) { + schedule_fanin_cone( ntk, ntk.get_node( fi ), l - 1 ); + } ); +} + +template +uint32_t recompute_level( Ntk& ntk, typename Ntk::node const& n ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) + return ntk.level( n ); + ntk.set_visited( n, ntk.trav_id() ); + + uint32_t max_fi_level{ 0u }; + ntk.foreach_fanin( n, [&]( auto const& fi ) { + max_fi_level = std::max( max_fi_level, recompute_level( ntk, ntk.get_node( fi ) ) ); + } ); + ntk.set_level( n, max_fi_level + 1 ); + return max_fi_level + 1; +} + +} // namespace detail + +/*! \brief Find a reasonable level assignment for a buffered network given PI levels. + * + * \param ntk Buffered network + * \param pi_levels Levels of PIs + * \return Level assignment to all nodes + */ +template +node_map schedule_buffered_network_with_PI_levels( Ntk const& ntk, std::vector const& pi_levels ) +{ + assert( pi_levels.size() == ntk.num_pis() ); + + using node = typename Ntk::node; + node_map levels( ntk ); + depth_view dv{ ntk }; + + ntk.incr_trav_id(); + ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); + ntk.foreach_pi( [&]( auto const& n, auto i ) { + ntk.set_visited( n, ntk.trav_id() ); + dv.set_level( n, pi_levels[i] ); + } ); + + ntk.foreach_po( [&]( auto const& f ){ + detail::recompute_level( dv, ntk.get_node( f ) ); + }); + + ntk.foreach_node( [&]( auto const& n ) { + levels[n] = dv.level( n ); + } ); + + return levels; +} + +/*! \brief Verify a buffered network according to AQFP assumptions with provided level assignment. + * + * \param ntk Buffered network + * \param ps AQFP assumptions + * \param levels Level assignment for all nodes + * \return Whether `ntk` is path-balanced and properly-branched + */ +template +bool verify_aqfp_buffer( Ntk const& ntk, aqfp_assumptions_legacy const& ps, node_map const& levels ) +{ + static_assert( is_buffered_network_type_v, "Ntk is not a buffered network" ); + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + bool legal = true; + + /* fanout branching */ + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) ) + return true; + if ( !ps.branch_pis && ntk.is_pi( n ) ) + return true; + + if ( ntk.is_buf( n ) ) + legal &= ( ntk.fanout_size( n ) <= ps.splitter_capacity ); + else /* logic gate */ + legal &= ( ntk.fanout_size( n ) <= 1 ); + + return true; + } ); + + /* path balancing */ + ntk.foreach_node( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto ni = ntk.get_node( fi ); + if ( !ntk.is_constant( ni ) && ( ps.balance_pis || !ntk.is_pi( ni ) ) ) + legal &= ( levels[ni] == levels[n] - 1 ); + assert( legal ); + } ); + } ); + + if ( ps.balance_pis ) + { + ntk.foreach_pi( [&]( auto const& n ) { + legal &= ( levels[n] == 0 ); + } ); + } + + if ( ps.balance_pos ) + { + uint32_t depth{ 0u }; + ntk.foreach_po( [&]( auto const& f ) { + auto n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) && ( ps.balance_pis || !ntk.is_pi( n ) ) ) + { + if ( depth == 0u ) + depth = levels[n]; + else + legal &= ( levels[n] == depth ); + } + } ); + } + + return legal; +} + +/*! \brief Verify a buffered network according to AQFP assumptions with provided level assignment. + * + * \param ntk Buffered network + * \param ps AQFP assumptions + * \param levels Level assignment for all nodes + * \return Whether `ntk` is path-balanced and properly-branched + */ +template +bool verify_aqfp_buffer( Ntk const& ntk, aqfp_assumptions_realistic const& ps, node_map const& levels ) +{ + static_assert( is_buffered_network_type_v, "Ntk is not a buffered network" ); + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + bool legal = true; + + /* fanout branching */ + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) ) + return; + if ( ntk.is_pi( n ) ) + { + legal &= ( ntk.fanout_size( n ) <= ps.ci_capacity ); + } + else if ( ntk.is_buf( n ) ) + { + legal &= ( ntk.fanout_size( n ) <= ps.splitter_capacity ); + } + else /* logic gate */ + { + legal &= ( ntk.fanout_size( n ) <= 1 ); + } + assert( legal ); + } ); + + /* path balancing */ + ntk.foreach_node( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto ni = ntk.get_node( fi ); + if ( !ntk.is_constant( ni ) ) + legal &= ( levels[ni] == levels[n] - 1 ); + assert( legal ); + } ); + } ); + + if ( ps.balance_cios ) + { + auto const check_pi_fn = [&]( uint32_t level ){ + for ( auto const& p : ps.ci_phases ) + { + if ( level == p ) + return true; + } + return false; + }; + + ntk.foreach_pi( [&]( auto const& n ) { + legal &= check_pi_fn( levels[n] ); + assert( legal ); + } ); + + uint32_t depth{ 0u }; + ntk.foreach_po( [&]( auto const& f ) { + auto n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) ) + { + if ( depth == 0u ) + depth = levels[n]; + else + legal &= ( levels[n] == depth ); + assert( legal ); + } + } ); + legal &= ( depth % ps.num_phases == 0 ); + assert( legal ); + } + else + { + auto const check_pi_fn = [&]( uint32_t level ){ + for ( auto const& p : ps.ci_phases ) + { + if ( level >= p && ( level - p ) % ps.num_phases == 0 ) + return true; + } + return false; + }; + + ntk.foreach_pi( [&]( auto const& n ) { + legal &= check_pi_fn( levels[n] ); + assert( legal ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + auto n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) ) + { + legal &= ( levels[n] % ps.num_phases == 0 ); + assert( legal ); + } + } ); + } + + // TODO: max_phase_skip + + return legal; +} + +/*! \brief Verify a buffered network according to AQFP assumptions with provided PI level assignment. + * + * \param ntk Buffered network + * \param ps AQFP assumptions + * \param pi_levels Levels of PIs + * \return Whether `ntk` is path-balanced, phase-aligned, and properly-branched + */ +template +bool verify_aqfp_buffer( Ntk const& ntk, Asmp const& ps, std::vector const& pi_levels ) +{ + auto const levels = schedule_buffered_network_with_PI_levels( ntk, pi_levels ); + return verify_aqfp_buffer( ntk, ps, levels ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag.hpp new file mode 100644 index 00000000000..e35bc9d445e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag.hpp @@ -0,0 +1,159 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dag.hpp + \brief AQFP DAG data structure + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Represents a single-output connected Partial DAG or DAG. + * + * A partial DAG is a network with majority gates where some gates may have unconnected fanin slots. + * A DAG is a network with majority gates obtained from a partial DAG by specifying which + * unconnected fanin slots connect to the same primary input. + * Optionally, a DAG may designate which fanin slots are connected the constant 0. + * Unlike logic networks elsewhere in mockturtle, gates are numbered from 0 starting from the top gate. + */ +template +struct aqfp_dag +{ + using node_type = NodeT; + + std::vector> nodes; // fanins of nodes + std::vector input_slots; // identifiers of the input slots (bundles of fanins where the inputs will be connected) + NodeT zero_input = 0; // id of the input slot that is connected to constant 0 + + aqfp_dag( const std::vector>& nodes = {}, const std::vector& input_slots = {}, node_type zero_input = {} ) + : nodes( nodes ), input_slots( input_slots ), zero_input( zero_input ) {} + + aqfp_dag( const std::string& str ) + { + decode_from_string( str ); + } + + /*! \brief Compare to logical networks for equality. */ + bool operator==( const aqfp_dag& rhs ) const + { + if ( nodes.size() != rhs.nodes.size() ) + return false; + + for ( auto i = 0u; i < nodes.size(); i++ ) + { + auto x1 = nodes[i]; + auto x2 = rhs.nodes[i]; + std::stable_sort( x1.begin(), x1.end() ); + std::stable_sort( x2.begin(), x2.end() ); + if ( x1 != x2 ) + return false; + } + + auto y1 = input_slots; + auto y2 = rhs.input_slots; + std::stable_sort( y1.begin(), y1.end() ); + std::stable_sort( y2.begin(), y2.end() ); + if ( y1 != y2 ) + return false; + + if ( zero_input != rhs.zero_input ) + return false; + + return true; + } + + /*! \brief Return the number of majority gates. */ + uint32_t num_gates() const + { + return nodes.size() - input_slots.size(); + } + + /*! \brief Decode a string representation of a DAG into a DAG. + * + * Format: ng ni zi k0 g0f0 g0f1 .. g0fk0 k1 g1f0 g1f1 .. g1fk1 .... + * ng := num gates, ni := num inputs, zi := zero input, ki := num fanin of i-th gate, gifj = j-th fanin of i-th gate + */ + void decode_from_string( const std::string& str ) + { + std::istringstream iss( str ); + auto ng = 0u; + auto ni = 0u; + auto zi = 0u; + + iss >> ng >> ni >> zi; + zero_input = zi; + + std::vector level( ng + ni, 0u ); + for ( auto i = 0u; i < ng; i++ ) + { + auto nf = 0u; + iss >> nf; + + nodes.push_back( {} ); + + for ( auto j = 0u; j < nf; j++ ) + { + auto t = 0u; + iss >> t; + nodes[i].push_back( t ); + } + } + + for ( auto i = 0u; i < ni; i++ ) + { + nodes.push_back( {} ); + input_slots.push_back( nodes.size() - 1 ); + } + } + + /*! \brief Encode a DAG as a string. + * + * Format: ng ni zi k0 g0f0 g0f1 .. g0fk0 k1 g1f0 g1f1 .. g1fk1 .... + * ng := num gates, ni := num inputs, zi := zero input, ki := num fanin of i-th gate, gifj = j-th fanin of i-th gate + */ + std::string encode_as_string() const + { + std::stringstream ss; + ss << num_gates() << " " << input_slots.size() << " " << zero_input; + for ( auto i = 0u; i < num_gates(); i++ ) + { + ss << fmt::format( " {} {}", nodes[i].size(), fmt::join( nodes[i], " " ) ); + } + + return ss.str(); + } +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_cost.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_cost.hpp new file mode 100644 index 00000000000..7c9de614c6b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_cost.hpp @@ -0,0 +1,508 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dag_cost.hpp + \brief Cost computing functions for AQFP DAG structures + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include "../../../properties/aqfpcost.hpp" + +namespace mockturtle +{ + +/*! \brief Cost function for dag structures that compute the gate cost. */ +template +class dag_gate_cost +{ +public: + dag_gate_cost( const std::unordered_map& gate_costs ) : gate_costs( gate_costs ) {} + + double operator()( const Ntk& net ) + { + double res = 0.0; + + for ( const auto& node : net.nodes ) + { + if ( !node.empty() ) + res += gate_costs.at( node.size() ); + } + + return res; + } + +private: + std::unordered_map gate_costs; +}; + +/*! \brief Cost function for dag structures that compute the gate and path balancing cost. */ +template +class dag_aqfp_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + using depth_config_t = uint64_t; + + dag_aqfp_cost( const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : simp_cc( gate_costs ), fanout_cc( splitters ) {} + + /*! \brief Compute cost assuming all primary inputs are at the same depth. */ + double operator()( const Ntk& orig_net ) + { + net = orig_net; + fanout = std::vector>( net.nodes.size() ); + minlev = std::vector( net.nodes.size() ); + maxlev = std::vector( net.nodes.size() ); + curlev = std::vector( net.nodes.size() ); + + compute_fanouts( net, fanout ); + compute_min_levels( net, fanout, minlev ); + + // perform depth bounded search + double cost = IMPOSSIBLE; + + auto lastlev = *( std::max_element( minlev.begin(), minlev.end() ) ); + while ( true ) + { + compute_max_levels( net, fanout, maxlev, lastlev ); + + // fix levels for the root and the inputs + maxlev[0] = 0; + for ( auto f : net.input_slots ) + { + if ( f != net.zero_input ) + { + minlev[f] = lastlev; + } + } + + // check different level configurations for the other gates and compute the cost for buffers and splitters + cost = compute_best_cost( 1u, 0.0 ); + + if ( cost < IMPOSSIBLE ) + { + break; + } + + lastlev++; + } + + // add the gate costs + cost += simp_cc( net ); + + return cost; + } + +protected: + dag_gate_cost simp_cc; + fanout_net_cost fanout_cc; + + Ntk net; + std::vector> fanout; + std::vector minlev; + std::vector maxlev; + std::vector curlev; + + template + void compute_fanouts( const Ntk& net, FanOutT& fanout ) + { + for ( auto i = 0u; i < net.nodes.size(); i++ ) + { + for ( auto&& f : net.nodes[i] ) + { + if ( net.zero_input != f ) + { + fanout[f].push_back( i ); + } + } + } + } + + template + void compute_min_levels( const Ntk& net, const FanOutT& fanout, MinLevT& minlev ) + { + for ( auto i = 0u; i < net.nodes.size(); i++ ) + { + if ( fanout[i].size() == 0u ) + { + minlev[i] = 0u; + } + else + { + auto critical_fo = *( std::max_element( fanout[i].begin(), fanout[i].end(), + [&]( auto x, auto y ) { return ( minlev[x] < minlev[y] ); } ) ); + minlev[i] = 1 + minlev[critical_fo]; + if ( fanout[i].size() > 1 ) + { + minlev[i]++; + } + } + } + } + + template + void compute_max_levels( const Ntk& net, const FanOutT& fanout, MaxLevT& maxlev, uint32_t lastlev ) + { + for ( auto& f : net.input_slots ) + { + if ( f != net.zero_input ) + { + maxlev[f] = lastlev; + } + else + { + maxlev[f] = std::numeric_limits::max(); + } + } + + for ( auto i = net.num_gates(); i > 0u; i-- ) + { + maxlev[i - 1] = std::numeric_limits::max(); + for ( auto f : net.nodes[i - 1] ) + { + auto t = maxlev[f] - 1; + if ( fanout[f].size() > 1 ) + { + t--; + } + if ( t < maxlev[i - 1] ) + { + maxlev[i - 1] = t; + } + } + } + } + + double cost_for_node_if_in_level( uint32_t lev, std::vector fanouts ) + { + std::vector rellev; + for ( auto fo : fanouts ) + { + rellev.push_back( lev - curlev[fo] ); + } + std::stable_sort( rellev.begin(), rellev.end() ); + + return fanout_cc( rellev ); + } + + double compute_best_cost( uint32_t current_gid, double cost_so_far ) + { + if ( net.num_gates() == current_gid ) + { + for ( auto f : net.input_slots ) + { + if ( ( f == net.zero_input ) || fanout[f].empty() ) + { + continue; + } + + cost_so_far += cost_for_node_if_in_level( maxlev[f], fanout[f] ); + + if ( cost_so_far >= IMPOSSIBLE ) + { + return IMPOSSIBLE; + } + } + + return cost_so_far; + } + + auto result = IMPOSSIBLE; + for ( auto lev = minlev[current_gid]; lev <= maxlev[current_gid]; lev++ ) + { + auto cost = cost_for_node_if_in_level( lev, fanout[current_gid] ); + if ( cost >= IMPOSSIBLE ) + { + continue; + } + curlev[current_gid] = lev; + auto temp = compute_best_cost( current_gid + 1, cost_so_far + cost ); + if ( temp < result ) + { + result = temp; + } + } + + return result; + } +}; + +/*! \brief Compute cost together with the depth assignment to nodes for a given input depth configuration. */ +template +class dag_aqfp_cost_and_depths : public dag_aqfp_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + using dag_aqfp_cost::simp_cc; + using dag_aqfp_cost::fanout_cc; + using dag_aqfp_cost::net; + using dag_aqfp_cost::fanout; + using dag_aqfp_cost::minlev; + using dag_aqfp_cost::maxlev; + using dag_aqfp_cost::curlev; + using dag_aqfp_cost::compute_fanouts; + using dag_aqfp_cost::compute_min_levels; + using dag_aqfp_cost::compute_max_levels; + using dag_aqfp_cost::cost_for_node_if_in_level; + + dag_aqfp_cost_and_depths( const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : dag_aqfp_cost( gate_costs, splitters ) {} + + std::pair> operator()( const Ntk& orig_net, const std::vector& input_depths ) + { + net = orig_net; + fanout = std::vector>( net.nodes.size() ); + minlev = std::vector( net.nodes.size() ); + maxlev = std::vector( net.nodes.size() ); + curlev = std::vector( net.nodes.size() ); + + compute_fanouts( net, fanout ); + compute_min_levels( net, fanout, minlev ); + compute_max_levels( net, fanout, maxlev, input_depths ); + + uint32_t ind = 0u; + + // fix levels for the root and the inputs + maxlev[0] = 0; + ind = 0u; + for ( auto f : net.input_slots ) + { + if ( f != net.zero_input ) + { + minlev[f] = input_depths[ind++]; + } + } + + // check different level configurations for the other gates and compute the cost for buffers and splitters + auto [cost, levels] = compute_best_cost_and_levels( 1u, 0.0 ); + + // add the gate costs + cost += simp_cc( net ); + + return { cost, levels }; + } + +private: + template + void compute_max_levels( const Ntk& net, const FanOutT& fanout, MaxLevT& maxlev, std::vector input_depths ) + { + uint32_t ind = 0u; + for ( auto& f : net.input_slots ) + { + if ( f != net.zero_input ) + { + maxlev[f] = input_depths[ind++]; + } + else + { + maxlev[f] = std::numeric_limits::max(); + } + } + + for ( auto i = net.num_gates(); i > 0u; i-- ) + { + maxlev[i - 1] = std::numeric_limits::max(); + for ( auto f : net.nodes[i - 1] ) + { + auto t = maxlev[f] - 1; + if ( fanout[f].size() > 1 ) + { + t--; + } + if ( t < maxlev[i - 1] ) + { + maxlev[i - 1] = t; + } + } + } + } + + std::tuple> compute_best_cost_and_levels( uint32_t current_gid, double cost_so_far ) + { + if ( net.num_gates() == current_gid ) + { + for ( auto f : net.input_slots ) + { + if ( ( f == net.zero_input ) || fanout[f].empty() ) + { + continue; + } + + curlev[f] = maxlev[f]; + cost_so_far += cost_for_node_if_in_level( maxlev[f], fanout[f] ); + + if ( cost_so_far >= IMPOSSIBLE ) + { + return { IMPOSSIBLE, {} }; + } + } + + return { cost_so_far, curlev }; + } + + double res_cost = IMPOSSIBLE; + std::vector res_lev = {}; + for ( auto lev = minlev[current_gid]; lev <= maxlev[current_gid]; lev++ ) + { + auto cost = cost_for_node_if_in_level( lev, fanout[current_gid] ); + if ( cost >= IMPOSSIBLE ) + { + continue; + } + curlev[current_gid] = lev; + auto [temp_cost, temp_lev] = compute_best_cost_and_levels( current_gid + 1, cost_so_far + cost ); + if ( temp_cost < res_cost ) + { + res_cost = temp_cost; + res_lev = temp_lev; + } + } + + return { res_cost, res_lev }; + } +}; + +/*! \brief Compute costs for different input depth configurations. */ +template +class dag_aqfp_cost_all_configs : public dag_aqfp_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + using depth_config_t = uint64_t; // each byte encodes a depth of a primary input + + using dag_aqfp_cost::simp_cc; + using dag_aqfp_cost::fanout_cc; + using dag_aqfp_cost::net; + using dag_aqfp_cost::fanout; + using dag_aqfp_cost::minlev; + using dag_aqfp_cost::maxlev; + using dag_aqfp_cost::curlev; + using dag_aqfp_cost::compute_fanouts; + using dag_aqfp_cost::compute_min_levels; + using dag_aqfp_cost::compute_max_levels; + using dag_aqfp_cost::cost_for_node_if_in_level; + + dag_aqfp_cost_all_configs( const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : dag_aqfp_cost( gate_costs, splitters ) {} + + std::unordered_map operator()( const Ntk& orig_net ) + { + std::unordered_map config_cost; + net = orig_net; + fanout = std::vector>( net.nodes.size() ); + minlev = std::vector( net.nodes.size() ); + maxlev = std::vector( net.nodes.size() ); + curlev = std::vector( net.nodes.size() ); + + compute_fanouts( net, fanout ); + compute_min_levels( net, fanout, minlev ); + + auto lastlev = *( std::max_element( minlev.begin(), minlev.end() ) ); + while ( true ) + { + compute_max_levels( net, fanout, maxlev, lastlev ); + + // fix levels for the root and the inputs + maxlev[0] = 0; + + // check different level configurations for the other gates and compute the cost for buffers and splitters + compute_best_costs_for_all_configs( 1u, 0.0, config_cost ); + + if ( config_cost.size() > 0 ) + { + break; + } + + lastlev++; + } + + double cost_for_gates = simp_cc( net ); + + for ( auto it = config_cost.begin(); it != config_cost.end(); it++ ) + { + it->second += cost_for_gates; + } + + return config_cost; + } + +private: + void compute_best_costs_for_all_configs( uint32_t current_gid, double cost_so_far, std::unordered_map& config_cost ) + { + if ( net.nodes.size() == current_gid ) + { + depth_config_t depth_config = 0u; + uint32_t ind = 0; + for ( auto f : net.input_slots ) + { + if ( f == net.zero_input ) + { + continue; + } + + depth_config |= ( curlev[f] << ( 8 * ind ) ); + ind++; + } + + if ( !config_cost.count( depth_config ) ) + { + config_cost[depth_config] = IMPOSSIBLE; + } + + config_cost[depth_config] = std::min( config_cost[depth_config], cost_so_far ); + return; + } + + if ( net.zero_input == (typename Ntk::node_type)current_gid ) + { + compute_best_costs_for_all_configs( current_gid + 1, cost_so_far, config_cost ); + return; + } + + for ( auto lev = minlev[current_gid]; lev <= maxlev[current_gid]; lev++ ) + { + auto cost = cost_for_node_if_in_level( lev, fanout[current_gid] ); + if ( cost >= IMPOSSIBLE ) + { + continue; + } + curlev[current_gid] = lev; + compute_best_costs_for_all_configs( current_gid + 1, cost_so_far + cost, config_cost ); + } + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_gen.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_gen.hpp new file mode 100644 index 00000000000..4ad384e6640 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_gen.hpp @@ -0,0 +1,389 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dag_gen.hpp + \brief AQFP DAG generation + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dag.hpp" +#include "dag_util.hpp" +#include "partial_dag.hpp" + +namespace mockturtle +{ + +using part = std::multiset; +using partition = std::multiset; +using partition_set = std::set; + +struct dag_generator_params +{ + + uint32_t max_gates; // max number of gates allowed + uint32_t max_levels; // max number of gate levels + uint32_t max_num_in; // max number of primary input slots (including the constant) + uint32_t max_num_fanout; // max number of fanouts per gate + uint32_t max_width; // max number of gates in any given level + + std::vector allowed_num_fanins; // the types of allowed majority gates + std::unordered_map max_gates_of_fanin; // max number of gates allowed for each type + + uint32_t verbose; + + dag_generator_params() : max_gates( std::numeric_limits::max() ), + max_levels( std::numeric_limits::max() ), + max_num_in( std::numeric_limits::max() ), + max_num_fanout( std::numeric_limits::max() ), + max_width( std::numeric_limits::max() ), + allowed_num_fanins( { 3u } ), + max_gates_of_fanin( { { 3u, std::numeric_limits::max() } } ), + verbose( 0u ) {} +}; + +/*! \brief Generate all DAGs derived from a given partial DAG. */ +template +class dags_from_partial_dag +{ + using PartialNtk = aqfp_partial_dag; + using Ntk = aqfp_dag; + +public: + dags_from_partial_dag( uint32_t max_num_in, uint32_t max_num_fanout ) : max_num_in( max_num_in ), max_num_fanout( max_num_fanout ) {} + + std::vector operator()( const PartialNtk& net ) + { + std::vector leaves = net.last_layer_leaves; + leaves.insert( leaves.end(), net.other_leaves.begin(), net.other_leaves.end() ); + std::stable_sort( leaves.begin(), leaves.end() ); + + auto max_counts = net.max_equal_fanins(); + + auto partitions = partition_gen( leaves, max_counts, max_num_in + 1, max_num_fanout ); + + std::vector result; + for ( auto p : partitions ) + { + auto new_net = get_dag_for_partition( net, p ); + + if ( net.input_slots.size() <= max_num_in ) + { + result.push_back( new_net ); + } + + int i = 0; + for ( auto it = p.begin(); it != p.end(); ) + { + auto temp_net = new_net; + temp_net.zero_input = new_net.input_slots[i]; + result.push_back( temp_net ); + auto c = p.count( *it ); + i += c; + std::advance( it, c ); + } + } + + return result; + } + +private: + uint32_t max_num_in; + uint32_t max_num_fanout; + detail::partition_generator partition_gen; + + /** + * \brief Compute the DAG obtained from partial DAG `orig` by combining slots + * according to partitions `p`. + */ + Ntk get_dag_for_partition( const PartialNtk& orig, const partition& p ) + { + auto net = orig.copy_without_leaves(); + std::for_each( p.begin(), p.end(), [&net]( const auto& q ) { net.add_leaf_node( q ); } ); + return std::move( net ); + } +}; + +#if !__clang__ || __clang_major__ > 10 + +/*! \brief Generate all DAGs satisfying the parameters. */ +template +class dag_generator +{ + using PartialNtk = aqfp_partial_dag; + +public: + dag_generator( const dag_generator_params& params, uint32_t num_threads = 1u ) : params( params ), num_threads( num_threads ) {} + + template + void for_each_dag( Fn&& callback ) + { + if ( params.verbose > 0u ) + { + std::cerr << fmt::format( "Generating partial dags.\n" ); + } + + generate_all_partial_dags(); + + if ( params.verbose > 0u ) + { + std::cerr << fmt::format( "Generated {} partial dags.\n", partial_dags.size() ); + std::cerr << fmt::format( "Generating dags in {} threads...\n", num_threads ); + } + + std::vector threads; + std::mutex mu; + for ( auto i = 0u; i < num_threads; i++ ) + { + threads.emplace_back( + [&]( auto id ) { + dags_from_partial_dag dag_from_pdag( params.max_num_in, params.max_num_fanout ); + while ( true ) + { + mu.lock(); + if ( partial_dags.empty() ) + { + mu.unlock(); + return; + } + auto p = partial_dags.front(); + partial_dags.pop(); + mu.unlock(); + auto dags = dag_from_pdag( p ); + for ( const auto& dag : dags ) + { + callback( dag, id ); + } + } + }, + i ); + } + + for ( auto i = 0u; i < num_threads; i++ ) + { + threads[i].join(); + } + } + +private: + dag_generator_params params; + uint32_t num_threads; + + std::queue partial_dags; + + detail::partition_generator partition_gen; + detail::partition_extender partition_ext; + detail::sublist_generator sublist_gen; + + void generate_all_partial_dags() + { + std::stack stk; + + for ( auto&& fin : params.allowed_num_fanins ) + { + if ( params.max_gates_of_fanin.at( fin ) > 0 ) + { + auto root = PartialNtk::get_root( fin ); + stk.push( root ); + } + } + + while ( !stk.empty() ) + { + auto res = stk.top(); + stk.pop(); + + partial_dags.push( res ); + + if ( params.max_levels > res.num_levels ) + { + auto ext = get_layer_extension( res ); + for ( const auto& e : ext ) + { + stk.push( e ); + } + } + } + } + + /*! \brief Extend the current aqfp logical network by one more level. */ + std::vector get_layer_extension( const PartialNtk& net ) + { + std::vector result; + + auto max_counts = net.max_equal_fanins(); + + auto last_options = sublist_gen( net.last_layer_leaves ); + auto other_options = sublist_gen( net.other_leaves ); + + auto last_counts = detail::get_frequencies( net.last_layer_leaves ); + auto other_counts = detail::get_frequencies( net.other_leaves ); + + auto net_without_leaves = net.copy_without_leaves(); + + /* Consider all different ways of choosing a non-empty subset of last layer slots */ + for ( auto&& last : last_options ) + { + if ( last.empty() ) + continue; + + /* Remaining slots in the last layer */ + auto last_counts_cpy = last_counts; + for ( auto&& e : last ) + { + last_counts_cpy[e]--; + } + + /* Consider all different ways of choosing a subset of other layer slots */ + for ( auto&& other : other_options ) + { + + /* Remaining slots in the other layers */ + auto other_counts_cpy = other_counts; + for ( auto&& e : other ) + { + other_counts_cpy[e]--; + } + + /* Compute the new set of other leaves for all resulting partial DAGs */ + std::vector other_leaves_new; + for ( auto it = last_counts_cpy.begin(); it != last_counts_cpy.end(); it++ ) + { + for ( auto i = 0u; i < it->second; i++ ) + { + other_leaves_new.push_back( it->first ); + } + } + for ( auto it = other_counts_cpy.begin(); it != other_counts_cpy.end(); it++ ) + { + for ( auto i = 0u; i < it->second; i++ ) + { + other_leaves_new.push_back( it->first ); + } + } + + if ( params.max_gates == 0 || params.max_gates > net_without_leaves.num_gates() ) + { + auto max_gates = params.max_gates > 0u ? params.max_gates - net_without_leaves.num_gates() : 0u; + auto last_layers_partitions = partition_gen( last, max_counts, max_gates, params.max_num_fanout ); + + for ( auto p : last_layers_partitions ) + { + auto extensions = partition_ext( other, p, max_counts, params.max_num_fanout ); + for ( auto q : extensions ) + { + auto temp = get_next_partial_dags( net_without_leaves, q, other_leaves_new ); + for ( auto&& r : temp ) + { + r.num_levels++; + result.push_back( r ); + } + } + } + } + } + } + + return result; + } + + /*! \brief Compute the partial DAGs obtained by combining the slots of `orig` as indicated by + * partitioning 'p'. + */ + std::vector get_next_partial_dags( const PartialNtk& orig, const partition& p, const std::vector& other_leaves ) + { + auto max_allowed_of_fanin = params.max_gates_of_fanin; + for ( auto it = orig.num_gates_of_fanin.begin(); it != orig.num_gates_of_fanin.end(); it++ ) + { + assert( max_allowed_of_fanin[it->first] >= it->second ); + max_allowed_of_fanin[it->first] -= it->second; + } + + std::vector q( p.begin(), p.end() ); + + /* For each part in partition 'p', consider gates of different number of fanins to connect. */ + auto res = add_node_recur( orig, q, 0, max_allowed_of_fanin ); + + for ( auto&& net : res ) + { + net.other_leaves = other_leaves; + } + + return res; + } + + /*! \brief Recursively consider gates with different number of fanins for different parts in partition `p`. */ + std::vector add_node_recur( const PartialNtk& orig, const std::vector& p, uint32_t ind, std::unordered_map& max_allowed_of_fanin ) + { + if ( ind == p.size() ) + { + return { orig }; + } + + std::vector res; + + /* Decide what fanin gate to use for part in partition 'p' at index 'ind'. */ + for ( auto&& fin : params.allowed_num_fanins ) + { + if ( max_allowed_of_fanin[fin] == 0 ) + { + continue; + } + max_allowed_of_fanin[fin]--; + + auto temp = add_node_recur( orig, p, ind + 1, max_allowed_of_fanin ); + + for ( const auto& t : temp ) + { + auto net = t.copy_with_last_layer_leaves(); + net.add_internal_node( fin, p[ind], true ); + res.push_back( net ); + } + + max_allowed_of_fanin[fin]++; + } + + return res; + } +}; + +#endif + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_util.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_util.hpp new file mode 100644 index 00000000000..ec4b95c80ff --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/dag_util.hpp @@ -0,0 +1,316 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dag_util.hpp + \brief Utilities for DAG generation + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../../../utils/hash_functions.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +/*! \brief Computes and returns the frequency map for a given collection of elements. + * Use std::map instead of std::unordered_map because we use it as a key in a hash-table so the order is important to compute the hash + */ +template +inline std::map get_frequencies( const std::vector& elems ) +{ + std::map elem_counts; + std::for_each( elems.begin(), elems.end(), [&elem_counts]( auto e ) { elem_counts[e]++; } ); + return elem_counts; +} + +template +class partition_generator +{ + using part = std::multiset; + using partition = std::multiset; + using partition_set = std::set; + + using inner_cache_key_t = std::vector; + using inner_cache_t = std::unordered_map>; + + using outer_cache_key_t = std::tuple, uint32_t, uint32_t>; + using outer_cache_t = std::unordered_map>; + +public: + /*! \brief Computes and returns a set of partitions for a given list of elements + * such that no part contains any element `e` more than `max_counts[e]` times. + */ + partition_set operator()( + std::vector elems, + const std::vector& max_counts = {}, + uint32_t max_parts = 0, + uint32_t max_part_size = 0 ) + { + _elems = elems; + _max_counts = max_counts; + _max_parts = max_parts; + _max_part_size = max_part_size; + + const outer_cache_key_t key = { _max_counts, _max_parts, _max_part_size }; + partition_cache = outer_cache.insert( { key, inner_cache_t() } ).first; + + return get_all_partitions(); + } + +private: + outer_cache_t outer_cache; + + typename outer_cache_t::iterator partition_cache; + std::vector _elems; + std::vector _max_counts; + uint32_t _max_parts; + uint32_t _max_part_size; + + partition_set get_all_partitions() + { + if ( _elems.size() == 0 ) + { + return { {} }; // return the empty partition. + } + + inner_cache_key_t key = _elems; + if ( partition_cache->second.count( key ) ) + { + return partition_cache->second.at( key ); + } + + partition_set result; + + auto last = _elems.back(); + _elems.pop_back(); + + auto temp = get_all_partitions(); + + for ( auto&& t : temp ) + { + partition cpy; + + // take 'last' in its own partition + cpy = t; + + if ( _max_parts == 0u || _max_parts > cpy.size() ) + { + cpy.insert( { last } ); + result.insert( cpy ); + } + + // add 'last' to one of the existing partitions + for ( auto it = t.begin(); it != t.end(); ) + { + if ( _max_counts.empty() || it->count( last ) < _max_counts[last] ) + { + + if ( _max_part_size == 0 || _max_part_size > it->size() ) + { + cpy = t; + auto elem_it = cpy.find( *it ); + auto cpy_elem = *elem_it; + cpy_elem.insert( last ); + cpy.erase( elem_it ); + cpy.insert( cpy_elem ); + result.insert( cpy ); + } + } + + std::advance( it, t.count( *it ) ); + } + } + + return ( partition_cache->second[key] = result ); + } +}; + +template +class partition_extender +{ + using part = std::multiset; + using partition = std::multiset; + using partition_set = std::set; + + using inner_cache_key_t = std::vector; + using inner_cache_t = std::unordered_map>; + + using outer_cache_key_t = std::tuple, uint32_t>; + using outer_cache_t = std::map; + +public: + /*! \brief Compute a list of different partitions that can be obtained by adding elements + * in `elems` to the parts of `base` such that no part contains any element `e` more than + * `max_counts[e]` times + */ + partition_set operator()( std::vector elems, partition base, const std::vector& max_counts, uint32_t max_part_size = 0 ) + { + _elems = elems; + _base = base; + _max_counts = max_counts; + _max_part_size = max_part_size; + + const outer_cache_key_t key = { _base, _max_counts, _max_part_size }; + partition_cache = outer_cache.insert( { key, inner_cache_t() } ).first; + + return extend_partitions(); + } + +private: + outer_cache_t outer_cache; + + typename outer_cache_t::iterator partition_cache; + std::vector _elems; + partition _base; + std::vector _max_counts; + uint32_t _max_part_size; + + partition_set extend_partitions() + { + if ( _elems.size() == 0 ) + { + return { _base }; + } + + inner_cache_key_t key = _elems; + if ( partition_cache->second.count( key ) ) + { + return partition_cache->second.at( key ); + } + + partition_set result; + + auto last = _elems.back(); + _elems.pop_back(); + + auto temp = extend_partitions(); + for ( auto&& t : temp ) + { + partition cpy; + + for ( auto it = t.begin(); it != t.end(); ) + { + if ( it->count( last ) < _max_counts.at( last ) ) + { + + if ( _max_part_size == 0 || _max_part_size > it->size() ) + { + cpy = t; + auto elem_it = cpy.find( *it ); + auto cpy_elem = *elem_it; + cpy_elem.insert( last ); + cpy.erase( elem_it ); + cpy.insert( cpy_elem ); + result.insert( cpy ); + } + } + + std::advance( it, t.count( *it ) ); + } + } + + return ( partition_cache->second[key] = result ); + } +}; + +template +struct sublist_generator +{ + using sub_list_cache_key_t = std::map; + +public: + /** + * \brief Given a list of elements `elems`, generate all sub lists of those elements. + * Ex: if `elems` = [1, 2, 2, 3], this will generate the following lists: + * [0], [1], [1, 2], [1, 2, 2], [1, 2, 2, 3], [1, 2, 3], [1, 3], [2], [2, 2], [2, 2, 3], [2, 3], and [3]. + */ + std::set> operator()( std::vector elems ) + { + elem_counts = get_frequencies( elems ); + return get_sub_lists_recur(); + } + +private: + std::unordered_map>, hash> sub_list_cache; + std::map elem_counts; + + std::set> get_sub_lists_recur() + { + if ( elem_counts.size() == 0u ) + { + return { {} }; + } + + sub_list_cache_key_t key = elem_counts; + if ( !sub_list_cache.count( key ) ) + { + auto last = std::prev( elem_counts.end() ); + auto last_elem = last->first; + auto last_count = last->second; + elem_counts.erase( last ); + + std::set> result; + + std::vector t; + for ( auto i = last_count; i > 0; --i ) + { + t.push_back( last_elem ); + result.insert( t ); // insert a copy of t, and note that t is already sorted. + } + + auto temp = get_sub_lists_recur(); + + for ( std::vector t : temp ) + { + result.insert( t ); + for ( auto i = last_count; i > 0; --i ) + { + t.push_back( last_elem ); + std::stable_sort( t.begin(), t.end() ); + result.insert( t ); + } + } + + sub_list_cache[key] = result; + } + + return sub_list_cache[key]; + } +}; + +} // namespace detail + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_builder.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_builder.hpp new file mode 100644 index 00000000000..7676d1fb6c8 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_builder.hpp @@ -0,0 +1,319 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file db_builder.hpp + \brief Builder class for AQFP DAG database + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../aqfp_db.hpp" +#include "dag.hpp" +#include "dag_cost.hpp" +#include "npn_cache.hpp" + +namespace mockturtle +{ + +/*! \brief A class to help the generation of AQFP database in an incremental manner. */ +template> +class aqfp_db_builder +{ + using db_type = aqfp_db; + using replacement = typename db_type::replacement; + +public: + aqfp_db_builder( + const std::unordered_map& gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }, + const std::unordered_map& splitters = { { 1u, 2.0 }, { 4u, 2.0 } } ) + : gate_costs( gate_costs ), splitters( splitters ), cc( gate_costs, splitters ) {} + + db_type build() + { + return db_type( db, gate_costs, splitters ); + } + + /*! \brief Update the database with a rusult for network `net`. */ + void update( const Ntk& ntk, const std::unordered_map& cost_config, uint32_t N = 4u ) + { + std::vector input_tt = { 0x0000UL, 0xaaaaUL, 0xccccUL, 0xf0f0UL, 0xff00UL }; + auto fs = all_functions_from_dag( input_tt, ntk ); + + std::unordered_set npn; + for ( auto f : fs ) + { + auto tmp = npndb( f ); + + auto& npntt = std::get<0>( tmp ); + auto& npnperm = std::get<2>( tmp ); + + if ( npn.count( npntt ) ) // already processed + { + continue; + } + npn.insert( npntt ); + + std::vector revperm( N ); + for ( uint32_t i = 0; i < N; i++ ) + { + revperm[npnperm[i]] = i; + } + + for ( auto it = cost_config.begin(); it != cost_config.end(); it++ ) + { + auto& lvl_cfg = it->first; + auto& cost = it->second; + std::vector new_levels = { level_of_input( lvl_cfg, npnperm[0] ), + level_of_input( lvl_cfg, npnperm[1] ), + level_of_input( lvl_cfg, npnperm[2] ), + level_of_input( lvl_cfg, npnperm[3] ) }; + assert( new_levels.size() == N ); + auto new_lvl_cfg = lvl_cfg_from_vec( new_levels ); + + if ( !db[npntt].count( new_lvl_cfg ) || db[npntt][new_lvl_cfg].cost > cost ) + { + db[npntt][new_lvl_cfg] = { cost, ntk, new_levels, revperm }; + } + } + } + } + + /*! Filter database configurations that are "covered" by other configurations. */ + void remove_redundant( bool verbose = false ) + { + for ( auto i = db.begin(); i != db.end(); i++ ) + { + auto& npn = i->first; + auto& configs = i->second; + + std::map good_configs; + for ( auto it = configs.begin(); it != configs.end(); it++ ) + { + bool ok = true; + + uint32_t N = it->second.input_levels.size(); + + for ( auto jt = configs.begin(); jt != configs.end(); jt++ ) + { + if ( jt->first == it->first ) + { + continue; + } + + /* check whether there exist input-wise smaller level config */ + bool jt_smaller_to_it = true; + for ( auto j = 0; j < N; j++ ) + { + if ( level_of_input( jt->first, j ) > level_of_input( it->first, j ) ) + { + jt_smaller_to_it = false; + break; + } + } + if ( jt_smaller_to_it ) + { + double extra_buff_cost = 0; + for ( auto j = 0; j < N; j++ ) + { + extra_buff_cost += splitters.at( 1u ) * ( level_of_input( it->first, j ) - level_of_input( jt->first, j ) ); + } + if ( extra_buff_cost + jt->second.cost <= it->second.cost ) + { + if ( verbose ) + { + std::cerr << fmt::format( "[aqfp_db] configuration {:04x} already covered by {:04x} [{} {} {}].\n", + it->first, jt->first, it->second.cost, jt->second.cost, extra_buff_cost ); + } + ok = false; + break; + } + } + } + if ( ok ) + { + good_configs[it->first] = it->second; + } + } + db[npn] = good_configs; + } + } + + /*! \brief Save database to the output stream `os`. */ + void save_db_to_file( std::ostream& os, bool hardcode = false ) + { + if ( !hardcode ) + { + /* output the database in plain-text encoding */ + + /* number of functions */ + os << fmt::format( "{}\n", db.size() ); + for ( auto i = db.begin(); i != db.end(); i++ ) + { + /* npn class */ + auto& k = i->first; + + auto& m = i->second; + os << fmt::format( "{:04x}\n", k ); + + /* number of entries for the npn class */ + os << fmt::format( "{}\n", m.size() ); + for ( auto it = m.begin(); it != m.end(); it++ ) + { + auto& lvl_cfg = it->first; + auto& r = it->second; + + os << fmt::format( "{:08x}\n", lvl_cfg ); + os << fmt::format( "{}\n", r.cost ); + os << fmt::format( "{}\n", r.ntk.encode_as_string() ); + os << fmt::format( "{}\n", fmt::join( r.input_perm, " " ) ); + } + } + + return; + } + + /* output the database encoded as an initializer list */ + + os << "{\n"; + for ( auto i = db.begin(); i != db.end(); i++ ) + { + os << fmt::format( "\t{{ 0x{:04x}, {{\n", i->first ); + + for ( auto j = i->second.begin(); j != i->second.end(); j++ ) + { + os << fmt::format( "\t\t\t\t{{ 0x{:08x}, ", j->first ); + os << fmt::format( "{{ {}, {{ \"{}\" }}, {{ {} }}, {{ {} }} }} }},\n", + j->second.cost, + j->second.ntk.encode_as_string(), + fmt::join( j->second.input_levels, ", " ), + fmt::join( j->second.input_perm, ", " ) ); + } + + os << "\t\t}\n\t},\n"; + } + os << "}\n"; + } + + /*! \brief Load database from input stream `is`. */ + void load_db( std::istream& is ) + { + aqfp_db::load_db( is, db ); + } + +private: + std::unordered_map gate_costs; + std::unordered_map splitters; + std::unordered_map> db; + dag_aqfp_cost_and_depths cc; + npn_cache npndb; + + /*! \brief Compute all functions synthesizable from `net` if input slots are assigned the truthtables in `input_tt`. */ + std::unordered_set all_functions_from_dag( const std::vector& input_tt, const Ntk& net ) + { + // static_assert( N == 4u, "Template parameter N must be equal to 4 in the current implementation" ); + + uint32_t num_inputs = net.input_slots.size(); + if ( net.zero_input != 0 ) + { + num_inputs--; + } + + std::unordered_set res; + + std::vector tt( net.nodes.size(), input_tt[0] ); + auto input_ind = 1u; + + auto tmp_input_slots = net.input_slots; + std::stable_sort( tmp_input_slots.begin(), tmp_input_slots.end() ); + assert( tmp_input_slots == net.input_slots ); + + for ( auto i : net.input_slots ) + { + if ( i != (int)net.zero_input ) + { + tt[i] = input_tt[input_ind++]; + } + } + + auto shift = 0u; + for ( auto i = 0u; i < net.num_gates(); i++ ) + { + shift += ( net.nodes[i].size() - 1 ); + } + + const auto n_gates = net.num_gates(); + + for ( auto inv_config_itr = 0ul; inv_config_itr < ( 1ul << shift ); inv_config_itr++ ) + { + auto inv_config = inv_config_itr; + + for ( auto i = n_gates; i > 0; i-- ) + { + const auto& n = net.nodes[i - 1]; + + const auto n_fanin = n.size(); + const auto shift = n_fanin - 1; + const auto mask = ( 1 << shift ) - 1; + const auto ith_gate_config = ( inv_config & mask ); + + // only consider half the inverter configurations, the other half is covered by output inversion + if ( n_fanin == 3u ) + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + tt[n[2]] ); + } + else + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + ( ith_gate_config & 4 ) ? ~tt[n[2]] : tt[n[2]], + ( ith_gate_config & 8 ) ? ~tt[n[3]] : tt[n[3]], + tt[n[4]] ); + } + inv_config >>= shift; + } + + res.insert( tt[0] & 0xffff ); + } + + return res; + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_utils.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_utils.hpp new file mode 100644 index 00000000000..adfa9b390fc --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/db_utils.hpp @@ -0,0 +1,278 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file db_utils.hpp + \brief Utility functions for creating the DAGs, costs, and final AQFP databases + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "dag.hpp" +#include "dag_cost.hpp" +#include "dag_gen.hpp" +#include "db_builder.hpp" + +namespace mockturtle +{ + +inline void generate_aqfp_dags( const mockturtle::dag_generator_params& params, const std::string& file_prefix, uint32_t num_threads ) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + + std::vector os; + for ( auto i = 0u; i < num_threads; i++ ) + { + auto file_path = fmt::format( "{}_{:02d}.txt", file_prefix, i ); + os.emplace_back( file_path ); + assert( os[i].is_open() ); + } + + auto gen = mockturtle::dag_generator( params, num_threads ); + + std::atomic count = 0u; + std::vector> counts_inp( 6u ); + + gen.for_each_dag( [&]( const auto& net, uint32_t thread_id ) { + counts_inp[net.input_slots.size()]++; + + os[thread_id] << fmt::format( "{}\n", net.encode_as_string() ); + + if ( (++count) % 100000 == 0u ) + { + auto t1 = std::chrono::high_resolution_clock::now(); + auto d1 = std::chrono::duration_cast( t1 - t0 ); + + std::cerr << fmt::format( "Number of DAGs generated {:10d}\nTime so far in seconds {:9.3f}\n", count, d1.count() / 1000.0 ); + } } ); + + for ( auto& file : os ) + { + file.close(); + } + + auto t2 = std::chrono::high_resolution_clock::now(); + auto d2 = std::chrono::duration_cast( t2 - t0 ); + std::cerr << fmt::format( "Number of DAGs generated {:10d}\nTime elapsed in seconds {:9.3f}\n", count, d2.count() / 1000.0 ); + + std::cerr << fmt::format( "Number of DAGs of different input counts: [3 -> {}, 4 -> {}, 5 -> {}]\n", counts_inp[3u], counts_inp[4u], counts_inp[5u] ); +} + +inline void compute_aqfp_dag_costs( const std::unordered_map& gate_costs, const std::unordered_map& splitters, + const std::string& dag_file_prefix, const std::string& cost_file_prefix, uint32_t num_threads ) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + + std::vector threads; + + std::atomic count = 0u; + + for ( auto i = 0u; i < num_threads; i++ ) + { + threads.emplace_back( + [&]( auto id ) { + std::ifstream is( fmt::format( "{}_{:02d}.txt", dag_file_prefix, id ) ); + assert( is.is_open() ); + + std::ofstream os( fmt::format( "{}_{:02d}.txt", cost_file_prefix, id ) ); + assert( os.is_open() ); + mockturtle::dag_aqfp_cost_all_configs> cc( gate_costs, splitters ); + + std::string temp; + while ( getline( is, temp ) ) + { + if ( temp.length() > 0 ) + { + mockturtle::aqfp_dag<> net( temp ); + auto costs = cc( net ); + + os << costs.size() << std::endl; + for ( auto it = costs.begin(); it != costs.end(); it++ ) + { + os << fmt::format( "{:08x} {}\n", it->first, it->second ); + } + + if ( ( ++count ) % 100000u == 0u ) + { + auto t1 = std::chrono::high_resolution_clock::now(); + auto d1 = std::chrono::duration_cast( t1 - t0 ); + + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime so far in seconds {:9.3f}\n", count, d1.count() / 1000.0 ); + } + } + } + + is.close(); + os.close(); + }, + i ); + } + + for ( auto& t : threads ) + { + t.join(); + } + + auto t2 = std::chrono::high_resolution_clock::now(); + auto d2 = std::chrono::duration_cast( t2 - t0 ); + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime elapsed in seconds {:9.3f}\n", count, d2.count() / 1000.0 ); +} + +inline void generate_aqfp_db( const std::unordered_map& gate_costs, const std::unordered_map& splitters, + const std::string& dag_file_prefix, const std::string& cost_file_prefix, const std::string& db_file_prefix, uint32_t num_threads ) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + + std::vector threads; + + std::atomic count = 0u; + + for ( auto i = 0u; i < num_threads; i++ ) + { + threads.emplace_back( + [&]( auto id ) { + std::ifstream ds( fmt::format( "{}_{:02d}.txt", dag_file_prefix, id ) ); + std::ifstream cs( fmt::format( "{}_{:02d}.txt", cost_file_prefix, id ) ); + + mockturtle::aqfp_db_builder<> db( gate_costs, splitters ); + uint64_t local_count = 0u; + + std::string dag; + while ( std::getline( ds, dag ) ) + { + uint32_t num_configs; + cs >> num_configs; + + std::unordered_map configs; + + std::string config_str; + uint64_t config; + double cost; + + for ( auto j = 0u; j < num_configs; j++ ) + { + cs >> config_str; + cs >> cost; + config = std::stoul( config_str, 0, 16 ); + configs[config] = cost; + } + + mockturtle::aqfp_dag<> ntk( dag ); + if ( ntk.input_slots.size() < 5u || ( ntk.input_slots.size() == 5u && ntk.zero_input != 0 ) ) + { + db.update( ntk, configs ); + } + + if ( ( ++count ) % 10000 == 0u ) + { + auto t1 = std::chrono::high_resolution_clock::now(); + auto d1 = std::chrono::duration_cast( t1 - t0 ); + + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime so far in seconds {:9.3f}\n", count, d1.count() / 1000.0 ); + } + + if ( ( ++local_count ) % 10000 == 0u ) + { + db.remove_redundant(); + + std::ofstream os_tmp( fmt::format( "{}_{:02d}.txt", db_file_prefix, id ) ); + assert( os_tmp.is_open() ); + db.save_db_to_file( os_tmp ); + os_tmp.close(); + } + } + + db.remove_redundant(); + + std::ofstream os( fmt::format( "{}_{:02d}.txt", db_file_prefix, id ) ); + assert( os.is_open() ); + db.save_db_to_file( os ); + os.close(); + }, + i ); + } + + for ( auto& t : threads ) + { + t.join(); + } + + mockturtle::aqfp_db_builder<> db( gate_costs, splitters ); + for ( auto i = 0u; i < num_threads; i++ ) + { + std::ifstream is( fmt::format( "{}_{:02d}.txt", db_file_prefix, i ) ); + assert( is.is_open() ); + db.load_db( is ); + is.close(); + } + + db.remove_redundant(); + + std::ofstream os_final( fmt::format( "{}.txt", db_file_prefix ) ); + assert( os_final.is_open() ); + db.save_db_to_file( os_final ); + os_final.close(); + + std::ofstream os_final_init_list( fmt::format( "{}_as_initializer_list.txt", db_file_prefix ) ); + assert( os_final_init_list.is_open() ); + db.save_db_to_file( os_final_init_list, true ); + os_final_init_list.close(); + + auto t2 = std::chrono::high_resolution_clock::now(); + auto d2 = std::chrono::duration_cast( t2 - t0 ); + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime elapsed in seconds {:9.3f}\n", count, d2.count() / 1000.0 ); +} + +inline void generate_aqfp_db( + const mockturtle::dag_generator_params& params, + const std::unordered_map& gate_costs, + const std::unordered_map& splitters, + const std::string& file_prefix, + uint32_t num_threads ) +{ + std::cerr << "Generating DAGs ...\n"; + auto dag_file_prefix = fmt::format( "{}_dags", file_prefix ); + generate_aqfp_dags( params, dag_file_prefix, num_threads ); + + std::cerr << "Computing costs ...\n"; + auto cost_file_prefix = fmt::format( "{}_costs", file_prefix ); + compute_aqfp_dag_costs( gate_costs, splitters, dag_file_prefix, cost_file_prefix, num_threads ); + + std::cerr << "Generating the database ...\n"; + auto db_file_prefix = fmt::format( "{}_db", file_prefix ); + generate_aqfp_db( gate_costs, splitters, dag_file_prefix, cost_file_prefix, db_file_prefix, num_threads ); + + std::cerr << "Generation completed!"; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/npn_cache.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/npn_cache.hpp new file mode 100644 index 00000000000..612fd961cc0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/npn_cache.hpp @@ -0,0 +1,89 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file npn_cache.hpp + \brief Cached NPN class computation + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Cache for mapping an N-input truthtable to the corresponding NPN class and the associated NPN transformation. */ +class npn_cache +{ + using npn_info = std::tuple>; + +public: + npn_cache() : arr( 1ul << ( 1ul << 4u ) ), has( 1ul << ( 1ul << 4u ), false ) + { + } + + npn_info operator()( uint64_t tt, uint32_t num_inputs = 4u ) + { + if ( num_inputs == 4u ) + { + if ( has[tt] ) + { + return arr[tt]; + } + + kitty::dynamic_truth_table dtt( num_inputs ); + dtt._bits[0] = tt; + auto tmp = kitty::exact_npn_canonization( dtt ); + + has[tt] = true; + return ( arr[tt] = { std::get<0>( tmp )._bits[0] & 0xffff, std::get<1>( tmp ), std::get<2>( tmp ) } ); + } + + if ( cache.count( tt ) ) + { + return cache[tt]; + } + + kitty::dynamic_truth_table dtt( num_inputs ); + dtt._bits[0] = tt; + auto tmp = kitty::exact_npn_canonization( dtt ); + + return ( arr[tt] = { std::get<0>( tmp )._bits[0] & 0xffff, std::get<1>( tmp ), std::get<2>( tmp ) } ); + } + +private: + std::vector arr; + std::vector has; + + std::unordered_map cache; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/partial_dag.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/partial_dag.hpp new file mode 100644 index 00000000000..6c714ed5dee --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/detail/partial_dag.hpp @@ -0,0 +1,243 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file partial_dag.hpp + \brief AQFP partial DAG data structure + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include + +#include "dag.hpp" + +namespace mockturtle +{ + +/*! \brief A class for constructing DAG structures in an incremental manner. */ +template +struct aqfp_partial_dag : public aqfp_dag +{ + using node_type = NodeT; + + using aqfp_dag::nodes; + using aqfp_dag::input_slots; + using aqfp_dag::zero_input; + using aqfp_dag::num_gates; + + uint32_t num_levels = 0u; // current number of levels + + std::unordered_map num_gates_of_fanin; + std::vector node_num_fanin; // number of fanins of each node + std::vector last_layer_leaves; // remaining fanin slots in the last layer + std::vector other_leaves; // remaining fanin slots of the other layers + + aqfp_partial_dag( + const std::vector>& nodes = {}, + const std::vector& input_slots = {}, + node_type zero_input = {}, + uint32_t num_levels = 0u, + const std::unordered_map& num_gates_of_fanin = {}, + const std::vector& node_num_fanin = {}, + const std::vector& last_layer_leaves = {}, + const std::vector& other_leaves = {} ) + : aqfp_dag( nodes, input_slots, zero_input ), + num_levels( num_levels ), + num_gates_of_fanin( num_gates_of_fanin ), + node_num_fanin( node_num_fanin ), + last_layer_leaves( last_layer_leaves ), + other_leaves( other_leaves ) {} + + aqfp_partial_dag( const std::string& str ) + { + decode_from_string( str ); + } + + /*! \brief Decode a string representation of a DAG into a DAG. + * + * Format: ng ni zi k0 g0f0 g0f1 .. g0fk0 k1 g1f0 g1f1 .. g1fk1 .... + * ng := num gates, ni := num inputs, zi := zero input, ki := num fanin of i-th gate, gifj = j-th fanin of i-th gate + */ + void decode_from_string( const std::string& str ) + { + num_levels = 0; + + std::istringstream iss( str ); + auto ng = 0u; + auto ni = 0u; + auto zi = 0u; + + iss >> ng >> ni >> zi; + zero_input = zi; + + std::vector level( ng + ni, 0u ); + level[0u] = 1u; + for ( auto i = 0u; i < ng; i++ ) + { + auto nf = 0u; + iss >> nf; + + num_gates_of_fanin[nf]++; + node_num_fanin.push_back( nf ); + nodes.push_back( {} ); + + for ( auto j = 0u; j < nf; j++ ) + { + auto t = 0u; + iss >> t; + nodes[i].push_back( t ); + level[t] = std::max( level[t], level[i] + 1 ); + } + } + + num_levels = *std::max_element( level.begin(), level.end() ); + + for ( auto i = 0u; i < ni; i++ ) + { + nodes.push_back( {} ); + node_num_fanin.push_back( 0 ); + input_slots.push_back( nodes.size() - 1 ); + } + } + + /*! \brief Returns a map with maximum number of equal fanins that a gate may have without that gate being redundant. + * + * A 3-input majority gate may not have any equal fanins as it would simplify otherwise. + * A 5-input majoirty gate may have up to 2 equal fanins but if it had more, then it would simplify. + */ + std::vector max_equal_fanins() const + { + std::vector res( num_gates() ); + for ( auto i = 0u; i < num_gates(); i++ ) + { + res[i] = node_num_fanin[i] / 2; + } + return res; + } + + /*! \brief Add "fanin" as a fanin of "node". */ + void add_fanin( NodeT node, NodeT fanin ) + { + nodes[node].push_back( fanin ); + } + + /*! \brief Adds a new node with "num_fanin" fanins that is connected to "fanouts". + * + * Optionally, it can be specified as a last layer node if at least one of its fanouts previously belonged to + * the last layer. + */ + uint32_t add_internal_node( uint32_t num_fanin = 3u, const std::multiset& fanouts = {}, bool is_in_last_layer = true ) + { + uint32_t node_id = nodes.size(); + nodes.push_back( {} ); + + assert( node_id == node_num_fanin.size() ); + + num_gates_of_fanin[num_fanin]++; + node_num_fanin.push_back( num_fanin ); + + for ( auto&& fo : fanouts ) + { + add_fanin( fo, node_id ); + } + + if ( is_in_last_layer ) + { + for ( auto slot = 0u; slot < num_fanin; slot++ ) + { + last_layer_leaves.push_back( node_id ); + } + } + + return node_id; + } + + /*! \brief Adds a new input node connected to "fanouts". */ + uint32_t add_leaf_node( const std::multiset& fanouts = {} ) + { + auto input_slot = add_internal_node( 0u, fanouts, false ); + input_slots.push_back( input_slot ); + return input_slot; + } + + /*! \brief Make a copy with empty last_layer_leaves and other_leaves. */ + aqfp_partial_dag copy_without_leaves() const + { + aqfp_partial_dag res{ + nodes, + input_slots, + zero_input, + + num_levels, + + num_gates_of_fanin, + node_num_fanin, + {}, + {}, + }; + + return res; + } + + /*! \brief Make a copy with empty other_leaves. */ + aqfp_partial_dag copy_with_last_layer_leaves() const + { + aqfp_partial_dag res{ + nodes, + input_slots, + zero_input, + + num_levels, + + num_gates_of_fanin, + node_num_fanin, + last_layer_leaves, + {}, + }; + + return res; + } + + /*! \brief Create a aqfp_partial_dag with a single gate of a given number of fanins. */ + static aqfp_partial_dag get_root( uint32_t num_fanin ) + { + aqfp_partial_dag net; + + net.num_levels = 1u; + + std::multiset fanouts = {}; + net.add_internal_node( num_fanin, fanouts, true ); + + return net; + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp new file mode 100644 index 00000000000..cb4ca494817 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp @@ -0,0 +1,407 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_algebraic_rewriting_splitters.hpp + \brief MIG algebraric rewriting with fanout size limitation + + \author Mathias Soeken + \author Eleonora Testa + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/stopwatch.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "../mig_algebraic_rewriting.hpp" + +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class mig_algebraic_depth_rewriting_splitter_impl +{ +public: + mig_algebraic_depth_rewriting_splitter_impl( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps, mig_algebraic_depth_rewriting_stats& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + switch ( ps.strategy ) + { + case mig_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case mig_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case mig_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [this]( auto n ) { + mark_critical_paths(); + reduce_depth( n ); + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + if ( reduce_depth( n ) ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + if ( !reduce_depth( n ) ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + bool reduce_depth( node const& n ) + { + + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_maj( ntk.get_node( ocs[2] ) ) ) + return false; + + if ( ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + ocs2[0] = !ocs2[0]; + ocs2[1] = !ocs2[1]; + ocs2[2] = !ocs2[2]; + } + + auto index = 0; + + if ( auto cand = associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + + signal third; + if ( assoc ) + { + third = u; + } + else + third = x; + + auto h = ntk.create_maj( x, y, u ); + auto opt = ntk.create_maj( z, third, h ); + + if ( ntk.fanout_size( ntk.get_node( opt ) ) < 1 ) // opt does not exists + { + if ( ( ntk.fanout_size( ntk.get_node( h ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( h ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( h ) ) == 2 ) ) // it would mean the depth is actually increased + { + ntk.take_out_node( ntk.get_node( opt ) ); + return true; + } + } + + ntk.substitute_node( n, opt ); + ntk.update_levels(); + return true; + } + + /* distributivity */ + if ( ps.allow_area_increase ) + { + auto s1 = ntk.create_maj( ocs[0], ocs[1], ocs2[0] ); + + auto s2 = ntk.create_maj( ocs[0], ocs[1], ocs2[1] ); + if ( ntk.fanout_size( ntk.get_node( s2 ) ) < 1 ) + { + index = ntk.node_to_index( ntk.get_node( ocs[0] ) ); + if ( ( !ntk.is_pi( ntk.get_node( ocs[0] ) ) ) && ( index != 0 ) ) //&& ( ntk.is_on_critical_path( ntk.get_node( ocs[0] ) ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) < 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + ntk.take_out_node( ntk.get_node( s2 ) ); + return true; + } + } + + index = ntk.node_to_index( ntk.get_node( ocs[1] ) ); + if ( ( !ntk.is_pi( ntk.get_node( ocs[1] ) ) ) && ( index != 0 ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( ocs[1] ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( ocs[1] ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( ocs[1] ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) < 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + ntk.take_out_node( ntk.get_node( s2 ) ); + return true; + } + } + } + + auto opt = ntk.create_maj( ocs2[2], s1, s2 ); + + if ( ( ntk.fanout_size( ntk.get_node( s1 ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s2 ) ); + if ( ntk.fanout_size( ntk.get_node( opt ) ) < 1 ) + ntk.take_out_node( ntk.get_node( opt ) ); + return true; + } + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + if ( ntk.fanout_size( ntk.get_node( opt ) ) < 1 ) + ntk.take_out_node( ntk.get_node( opt ) ); + return true; + } + if ( ( ntk.fanout_size( ntk.get_node( opt ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( opt ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( opt ) ) == 1 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + if ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s2 ) ); + return true; + } + ntk.substitute_node( n, opt ); + ntk.update_levels(); + } + return true; + } + + using candidate_t = std::tuple, signal, signal, signal, bool>; + std::optional associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + + return std::nullopt; + } + + std::array, 3> ordered_children( node const& n ) const + { + std::array, 3> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } ); + std::stable_sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) { + return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) ); + } ); + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + mig_algebraic_depth_rewriting_params const& ps; + mig_algebraic_depth_rewriting_stats& st; +}; // namespace detail + +} // namespace detail + +/*! \brief Majority algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with majority gates for depth + * optimization using the associativity and distributivity rule in + * majority-of-3 logic. It can be applied to networks other than MIGs, but + * only considers pairs of nodes which both implement the majority-of-3 + * function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_maj` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_maj` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired by an + implementation from Luca Amarù. + \endverbatim + */ +template +void mig_algebraic_depth_rewriting_splitters( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps = {}, mig_algebraic_depth_rewriting_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + mig_algebraic_depth_rewriting_stats st; + detail::mig_algebraic_depth_rewriting_splitter_impl p( ntk, ps, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_resub_splitters.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_resub_splitters.hpp new file mode 100644 index 00000000000..5cf722451a1 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/mig_resub_splitters.hpp @@ -0,0 +1,581 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_resub_splitters.hpp + \brief Modified MIG resubstitution to consider splitters in AQFP + + \author Heinz Riener + \author Eleonora Testa + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../networks/mig.hpp" +#include "../../utils/truth_table_utils.hpp" +#include "../resubstitution.hpp" + +#include + +namespace mockturtle +{ + +struct mig_resub_splitters_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for relevance resub */ + stopwatch<>::duration time_resubR{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted relevance resubsitutions */ + uint32_t num_divR_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: mig_resub_splitters_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] R-resub {:6d} ({:>5.2f} secs)\n", + num_divR_accepts, to_seconds( time_resubR ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} = {:6d} MAJ ({:>5.2f} secs)\n", + num_div1_accepts, num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_divR_accepts + num_div1_accepts ) ); + } +}; /* mig_resub_splitters_stats */ + +template +struct mig_resub_splitters_functor +{ +public: + using node = mig_network::node; + using signal = mig_network::signal; + using stats = mig_resub_splitters_stats; + + struct unate_divisors + { + std::vector positive_divisors0; + std::vector positive_divisors1; + std::vector negative_divisors0; + std::vector negative_divisors1; + std::vector next_candidates; + + void clear() + { + positive_divisors0.clear(); + positive_divisors1.clear(); + negative_divisors0.clear(); + negative_divisors1.clear(); + next_candidates.clear(); + } + }; + +public: + explicit mig_resub_splitters_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider relevance optimization */ + g = call_with_stopwatch( st.time_resubR, [&]() { + return resub_divR( root, required ); + } ); + if ( g ) + { + ++st.num_divR_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, required ); + } ); + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + if ( !ntk.is_pi( d ) ) + { + if ( ( ntk.fanout_size( d ) == 4 ) || ( ntk.fanout_size( d ) >= 16 ) || ( ntk.fanout_size( d ) == 1 ) ) // it would mean the depth is actually increased + continue; + auto fanout = ntk.fanout_size( root ); + if ( ( ntk.fanout_size( d ) < 4 ) && ( ntk.fanout_size( d ) + fanout > 4 ) ) + continue; + else if ( ( ntk.fanout_size( d ) < 16 ) && ( ntk.fanout_size( d ) > 4 ) && ( ntk.fanout_size( d ) + fanout > 16 ) ) + continue; + } + + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + continue; /* next */ + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + std::optional resub_divR( node const& root, uint32_t required ) + { + (void)required; + + std::vector fs; + ntk.foreach_fanin( root, [&]( const auto& f ) { + fs.emplace_back( f ); + } ); + + for ( auto i = 0u; i < divs.size(); ++i ) + { + auto const& d0 = divs.at( i ); + + if ( !ntk.is_pi( d0 ) ) + { + if ( ( ntk.fanout_size( d0 ) == 4 ) || ( ntk.fanout_size( d0 ) >= 16 ) || ( ntk.fanout_size( d0 ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + auto const& s = ntk.make_signal( d0 ); + auto const& tt = sim.get_tt( s ); + + if ( d0 == root ) + break; + + auto const tt0 = sim.get_tt( fs[0] ); + auto const tt1 = sim.get_tt( fs[1] ); + auto const tt2 = sim.get_tt( fs[2] ); + + if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ); + } + else if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( ~tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( ~tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( ~tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ); + } + } + + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t required ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d0 = divs.at( i ); + if ( ntk.level( d0 ) > required - 1 ) + continue; + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const d1 = divs.at( j ); + if ( ntk.level( d1 ) > required - 1 ) + continue; + + auto const& tt_s0 = sim.get_tt( ntk.make_signal( d0 ) ); + auto const& tt_s1 = sim.get_tt( ntk.make_signal( d1 ) ); + + /* Boolean filtering rule for MAJ-3 */ + if ( kitty::ternary_majority( tt_s0, tt_s1, tt ) == tt ) + { + udivs.positive_divisors0.emplace_back( ntk.make_signal( d0 ) ); + udivs.positive_divisors1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt ) == tt ) + { + udivs.negative_divisors0.emplace_back( ntk.make_signal( d0 ) ); + udivs.negative_divisors1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d1 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d1 ) ); + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d0 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d0 ) ); + } + } + + std::optional resub_div1( node const& root, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + /* check for positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors0.size(); ++i ) + { + auto const s0 = udivs.positive_divisors0.at( i ); + auto const s1 = udivs.positive_divisors1.at( i ); + + if ( !ntk.is_pi( ntk.get_node( s0 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s0 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + if ( !ntk.is_pi( ntk.get_node( s1 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s1 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + for ( auto j = i + 1; j < udivs.positive_divisors0.size(); ++j ) + { + auto s2 = udivs.positive_divisors0.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + { + continue; + } + else + return sim.get_phase( root ) ? !e : e; + } + + s2 = udivs.positive_divisors1.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) + continue; + } + tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + continue; + else + return sim.get_phase( root ) ? !e : e; + } + } + } + + /* check for negative unate divisors */ + for ( auto i = 0u; i < udivs.negative_divisors0.size(); ++i ) + { + auto const s0 = udivs.negative_divisors0.at( i ); + if ( !ntk.is_pi( ntk.get_node( s0 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s0 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + auto const s1 = udivs.negative_divisors1.at( i ); + if ( !ntk.is_pi( ntk.get_node( s1 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s1 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + for ( auto j = i + 1; j < udivs.negative_divisors0.size(); ++j ) + { + auto s2 = udivs.negative_divisors0.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) + continue; + } + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( !a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + continue; + else + return sim.get_phase( root ) ? !e : e; + } + + s2 = udivs.negative_divisors1.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) + continue; + } + tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( !a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + continue; + else + return sim.get_phase( root ) ? !e : e; + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; +}; /* mig_resub_functor */ + +template +bool substitute_and_update_fn( Ntk& ntk, typename Ntk::node const& n, typename Ntk::signal const& g ) +{ + ntk.substitute_node( n, g ); + ntk.update_levels(); + ntk.update_fanout(); + return true; +}; + +template +void mig_resubstitution_splitters( depth_view& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not mig_network" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + resub_view_t resub_view{ ntk }; + + if ( ps.max_pis == 8 ) + { + using truthtable_t = kitty::static_truth_table<8>; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_resub_splitters_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run( substitute_and_update_fn ); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_resub_splitters_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run( substitute_and_update_fn ); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/aqfp/optimal_buffer_insertion.hpp b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/optimal_buffer_insertion.hpp new file mode 100644 index 00000000000..859f681fb04 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/aqfp/optimal_buffer_insertion.hpp @@ -0,0 +1,563 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file optimal_buffer_insertion.hpp + \brief Global-optimal buffer insertion for AQFP using SMT + + \author Siang-Yun (Sonia) Lee +*/ + +// NOTE: This file is included inside the class `mockturtle::buffer_insertion` +// It should not be included anywhere else. + +#pragma region Compute timeframe for SMT solving + /*! \brief Compute the earliest and latest possible timeframe by eager ASAP and ALAP */ + uint32_t compute_timeframe( uint32_t max_depth ) + { + // TODO: Consider max_depth % _ps.assume.num_phases == 0 constraint + _timeframes.reset( std::make_pair( 0, 0 ) ); + uint32_t min_depth{ 0 }; + + _ntk.incr_trav_id(); + _ntk.foreach_po( [&]( auto const& f ) { + auto const no = _ntk.get_node( f ); + auto clevel = compute_levels_ASAP_eager( no ) + ( _ntk.fanout_size( no ) > 1 ? 1 : 0 ); + min_depth = std::max( min_depth, clevel ); + } ); + + _ntk.incr_trav_id(); + _ntk.foreach_po( [&]( auto const& f ) { + const auto n = _ntk.get_node( f ); + if ( !_ntk.is_constant( n ) && _ntk.visited( n ) != _ntk.trav_id() ) + { + _timeframes[n].second = max_depth - ( _ntk.fanout_size( n ) > 1 ? 1 : 0 ); + compute_levels_ALAP_eager( n ); + } + } ); + + return min_depth; + } + + uint32_t compute_levels_ASAP_eager( node const& n ) + { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + { + return _timeframes[n].first; + } + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.is_constant( n ) ) + { + return _timeframes[n].first = 0; + } + if ( _ntk.is_pi( n ) ) + { + return _timeframes[n].first = _ps.assume.ci_phases[0]; + } + + uint32_t level{ 0 }; + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + level = std::max( level, compute_levels_ASAP_eager( ni ) + ( _ntk.fanout_size( ni ) > 1 ? 1 : 0 ) ); + } + } ); + + return _timeframes[n].first = level + 1; + } + + void compute_levels_ALAP_eager( node const& n ) + { + _ntk.set_visited( n, _ntk.trav_id() ); + + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + if ( _ps.assume.balance_cios && _ntk.is_pi( ni ) ) + { + assert( _timeframes[n].second > _ps.assume.ci_phases[0] ); + _timeframes[ni].second = _ps.assume.ci_phases[0]; + } + else + { + assert( _timeframes[n].second > num_splitter_levels( ni ) ); + auto fi_level = _timeframes[n].second - ( _ntk.fanout_size( ni ) > 1 ? 2 : 1 ); + if ( _ntk.visited( ni ) != _ntk.trav_id() || _timeframes[ni].second > fi_level ) + { + _timeframes[ni].second = fi_level; + compute_levels_ALAP_eager( ni ); + } + } + } + } ); + } +#pragma + +#if __GNUC__ == 7 + +void optimize_with_smt( std::string name = "" ) +{} + +#else + +void optimize_with_smt( std::string name = "" ) +{ + std::ofstream os( "model_" + name + ".smt2", std::ofstream::out ); + dump_smt_model( os ); + os.close(); + std::string command = "z3 -v:1 model_" + name + ".smt2 &> sol_" + name + ".txt"; + std::system( command.c_str() ); + parse_z3_result( "sol_" + name + ".txt" ); + return; +} + +// ILP formulation +void dump_smt_model( std::ostream& os = std::cout ) +{ + os << "(set-logic QF_LIA)\n"; + /* hard assumptions to bound the number of variables */ + uint32_t const max_depth = depth(); // + 3; + uint32_t const max_relative_depth = max_depth; + count_buffers(); + uint32_t const upper_bound = num_buffers(); + uint32_t const min_depth = compute_timeframe( max_depth ); + + /* depth variable */ + if ( _ps.assume.balance_pos ) /* depth is a variable */ + { + os << fmt::format( "(declare-const depth Int)\n" ); + assert( min_depth <= max_depth ); + os << fmt::format( "(assert (<= depth {}))\n", max_depth ); + os << fmt::format( "(assert (>= depth {}))\n", min_depth ); + } + + /* declare variables for the level of each node */ + std::vector level_vars; + if ( _ps.assume.branch_pis && !_ps.assume.balance_pis ) + { + /* Only if we branch but not balance PIs, they are free variables as gates */ + _ntk.foreach_pi( [&]( auto const& n ) { + level_vars.emplace_back( fmt::format( "l{}", n ) ); + os << fmt::format( "(declare-const l{} Int)\n", n ); + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= l{} depth))\n", n ); + assert( _timeframes[n].second <= max_depth ); + os << fmt::format( "(assert (<= l{} {}))\n", n, _timeframes[n].second ); + os << fmt::format( "(assert (>= l{} 0))\n", n ); + } ); + } + + _ntk.foreach_gate( [&]( auto const& n ) { + level_vars.emplace_back( fmt::format( "l{}", n ) ); + os << fmt::format( "(declare-const l{} Int)\n", n ); + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= l{} depth))\n", n ); + assert( _timeframes[n].first >= 1 && _timeframes[n].second <= max_depth && _timeframes[n].first <= _timeframes[n].second ); + os << fmt::format( "(assert (>= l{} {}))\n", n, _timeframes[n].first ); + os << fmt::format( "(assert (<= l{} {}))\n", n, _timeframes[n].second ); + } ); + + /* constraints for each gate's fanout */ + std::vector bufs; + if ( _ps.assume.branch_pis ) + { + /* If PIs are balanced, they are always at level 0 so we don't have variables for them */ + if ( _ps.assume.balance_pis ) + { + _ntk.foreach_pi( [&]( auto const& n ) { + smt_constraints_balanced_pis( os, n, max_depth ); + bufs.emplace_back( fmt::format( "bufs{}", n ) ); + } ); + } + else + { + _ntk.foreach_pi( [&]( auto const& n ) { + smt_constraints( os, n, max_depth ); + bufs.emplace_back( fmt::format( "bufs{}", n ) ); + } ); + } + } + _ntk.foreach_gate( [&]( auto const& n ) { + smt_constraints( os, n, max_depth ); + bufs.emplace_back( fmt::format( "bufs{}", n ) ); + } ); + + os << "\n(declare-const total Int)\n"; + os << fmt::format( "(assert (= total (+ {})))\n", fmt::join( bufs, " " ) ); + os << fmt::format( "(assert (<= total {}))\n", upper_bound ); + os << "(minimize total)\n(check-sat)\n"; + if ( _ps.assume.balance_pos ) + os << "(get-value (total depth))\n"; + else + os << "(get-value (total))\n"; + os << fmt::format( "(get-value ({}))\n(exit)\n", fmt::join( level_vars, " " ) ); +} + +void smt_constraints_balanced_pis( std::ostream& os, node const& n, uint32_t const& max_depth ) +{ + os << fmt::format( "\n;constraints for node {}\n", n ); + os << fmt::format( "(declare-const bufs{} Int)\n", n ); + /* special cases */ + if ( _ntk.fanout_size( n ) == 0 ) /* dangling */ + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + return; + } + else if ( _ntk.fanout_size( n ) == 1 ) + { + /* single fanout: only sequencing constraint */ + if ( _external_ref_count[n] > 0 ) + { + if ( _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (= bufs{} depth))\n", n ); + } + else + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + } + } + else + { + node const& no = _fanouts[n].front().fanouts.front(); + os << fmt::format( "(assert (= bufs{} (- l{} 1)))\n", n, no ); + } + return; + } + else if ( _ntk.fanout_size( n ) <= _ps.assume.splitter_capacity ) + { + /* only one splitter is needed */ + + /* sequencing */ + std::vector fos; + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (>= l{} 2))\n", no ); + fos.emplace_back( no ); + } ); + + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (= bufs{} depth))\n", n ); + } + else + { + if ( fos.size() == 0 ) /* not balance PO; have multiple PO refs */ + { + os << fmt::format( "(assert (= bufs{} 1))\n", n ); + } + else if ( fos.size() == 1 ) /* not balance PO; have one gate fanout and PO ref(s) */ + { + os << fmt::format( "(assert (= bufs{} (- l{} 1)))\n", n, fos[0] ); + } + else if ( fos.size() >= 2 ) + { + os << fmt::format( "(declare-const g{}maxfo Int)\n", n ); /* the max level of fanouts */ + + /* bound it -- just to hint the solver; should not matter if we have optimization objective */ + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= g{}maxfo depth))\n", n ); + else + os << fmt::format( "(assert (<= g{}maxfo {}))\n", n, max_depth ); + + /* taking the max (lower bounded by the max) */ + for ( auto const& no : fos ) + os << fmt::format( "(assert (>= g{}maxfo l{}))\n", n, no ); + + os << fmt::format( "(assert (= bufs{} (- g{}maxfo 1)))\n", n, n ); + } + } + return; + } + + /* sequencing & range constraints */ + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (>= l{} 2))\n", no ); + } ); + + /* PO branching */ + if ( _external_ref_count[n] > 0 ) + { + os << fmt::format( "(assert (>= depth {}))\n", num_splitter_levels( n ) ); + } + + /* max possible relative depth */ + uint32_t highest_fanout = 0; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + highest_fanout = max_depth + 1; + } + else + { + foreach_fanout( n, [&]( auto const& no ) { + highest_fanout = std::max( highest_fanout, _timeframes[no].second ); + } ); + } + uint32_t max_relative_depth = highest_fanout; + + uint32_t l = max_relative_depth; + bool add_po_refs = false; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + add_po_refs = true; + os << fmt::format( "(assert (<= depth {}))\n", l - 1 ); + } + + /* initial case */ + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= l{} {}) 1 0)", no, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= depth {}) {} 0)", l - 1, _external_ref_count[n] ); + os << ")))\n"; + + /* general case */ + for ( --l; l > 0; --l ) + { + os << fmt::format( "(declare-const g{}b{} Int)\n", n, l ); // buffers at relative depth l + /* g{n}b{l} = ceil( g{n}e{l+1} / s_b ) --> s_b * ( g{n}b{l} - 1 ) < g{n}e{l+1} <= s_b * g{n}b{l} */ + os << fmt::format( "(assert (< (* {} (- g{}b{} 1)) g{}e{}))\n", _ps.assume.splitter_capacity, n, l, n, l + 1 ); + os << fmt::format( "(assert (<= g{}e{} (* {} g{}b{})))\n", n, l + 1, _ps.assume.splitter_capacity, n, l ); + + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= l{} {}) 1 0)", no, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= depth {}) {} 0)", l - 1, _external_ref_count[n] ); + os << fmt::format( " g{}b{})))\n", n, l ); + } + + /* end of loop */ + os << fmt::format( "(assert (= g{}e1 1))\n", n ); // legal + os << fmt::format( "(assert (= bufs{} (+", n ); + for ( l = max_relative_depth - 1; l > 0; --l ) + { + os << fmt::format( " g{}b{}", n, l ); + } + os << ")))\n"; +} + +void smt_constraints( std::ostream& os, node const& n, uint32_t const& max_depth ) +{ + os << fmt::format( "\n;constraints for node {}\n", n ); + os << fmt::format( "(declare-const bufs{} Int)\n", n ); + /* special cases */ + if ( _ntk.fanout_size( n ) == 0 ) /* dangling */ + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + return; + } + else if ( _ntk.fanout_size( n ) == 1 ) + { + /* single fanout: only sequencing constraint */ + if ( _external_ref_count[n] > 0 ) + { + if ( _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (= bufs{} (- depth l{})))\n", n, n ); + } + else + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + } + } + else + { + node const& no = _fanouts[n].front().fanouts.front(); + os << fmt::format( "(assert (> l{} l{}))\n", no, n ); + os << fmt::format( "(assert (= bufs{} (- l{} l{} 1)))\n", n, no, n ); + } + return; + } + else if ( _ntk.fanout_size( n ) <= _ps.assume.splitter_capacity ) + { + /* only one splitter is needed */ + + /* sequencing */ + std::vector fos; + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (>= l{} (+ l{} 2)))\n", no, n ); + fos.emplace_back( no ); + } ); + + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (> depth l{}))\n", n ); + os << fmt::format( "(assert (= bufs{} (- depth l{})))\n", n, n ); + } + else + { + if ( fos.size() == 0 ) /* not balance PO; have multiple PO refs */ + { + os << fmt::format( "(assert (= bufs{} 1))\n", n ); + } + else if ( fos.size() == 1 ) /* not balance PO; have one gate fanout and PO ref(s) */ + { + os << fmt::format( "(assert (= bufs{} (- l{} l{} 1)))\n", n, fos[0], n ); + } + else if ( fos.size() >= 2 ) + { + os << fmt::format( "(declare-const g{}maxfo Int)\n", n ); /* the max level of fanouts */ + + /* bound it -- just to hint the solver; should not matter if we have optimization objective */ + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= g{}maxfo depth))\n", n ); + else + os << fmt::format( "(assert (<= g{}maxfo {}))\n", n, max_depth ); + + /* taking the max (lower bounded by the max) */ + for ( auto const& no : fos ) + os << fmt::format( "(assert (>= g{}maxfo l{}))\n", n, no ); + + os << fmt::format( "(assert (= bufs{} (- g{}maxfo l{} 1)))\n", n, n, n ); + } + } + return; + } + + /* max possible relative depth */ + uint32_t highest_fanout = 0; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + highest_fanout = max_depth + 1; + } + else + { + foreach_fanout( n, [&]( auto const& no ) { + highest_fanout = std::max( highest_fanout, _timeframes[no].second ); + } ); + } + uint32_t max_relative_depth = highest_fanout - _timeframes[n].first; + + /* sequencing & range constraints */ + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (<= l{} (+ l{} {})))\n", no, n, max_relative_depth ); + os << fmt::format( "(assert (>= l{} (+ l{} 2)))\n", no, n ); + } ); + + /* PO branching */ + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (>= depth (+ l{} {})))\n", n, num_splitter_levels( n ) ); + } + + uint32_t l = max_relative_depth; + bool add_po_refs = false; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + add_po_refs = true; + os << fmt::format( "(assert (<= depth (+ l{} {})))\n", n, l - 1 ); + } + + /* initial case */ + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= (- l{} l{}) {}) 1 0)", no, n, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= (+ l{} {}) depth) {} 0)", n, l - 1, _external_ref_count[n] ); + os << ")))\n"; + + /* general case */ + for ( --l; l > 0; --l ) + { + os << fmt::format( "(declare-const g{}b{} Int)\n", n, l ); // buffers at relative depth l + /* g{n}b{l} = ceil( g{n}e{l+1} / s_b ) --> s_b * ( g{n}b{l} - 1 ) < g{n}e{l+1} <= s_b * g{n}b{l} */ + os << fmt::format( "(assert (< (* {} (- g{}b{} 1)) g{}e{}))\n", _ps.assume.splitter_capacity, n, l, n, l + 1 ); + os << fmt::format( "(assert (<= g{}e{} (* {} g{}b{})))\n", n, l + 1, _ps.assume.splitter_capacity, n, l ); + + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= (- l{} l{}) {}) 1 0)", no, n, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= (+ l{} {}) depth) {} 0)", n, l - 1, _external_ref_count[n] ); + os << fmt::format( " g{}b{})))\n", n, l ); + } + + /* end of loop */ + os << fmt::format( "(assert (= g{}e1 1))\n", n ); // legal + os << fmt::format( "(assert (= bufs{} (+", n ); + for ( l = max_relative_depth - 1; l > 0; --l ) + { + os << fmt::format( " g{}b{}", n, l ); + } + os << ")))\n"; +} + +template +void foreach_fanout( node const& n, Fn&& fn ) +{ + for ( auto it1 = _fanouts[n].begin(); it1 != _fanouts[n].end(); ++it1 ) + { + for ( auto it2 = it1->fanouts.begin(); it2 != it1->fanouts.end(); ++it2 ) + { + fn( *it2 ); + } + } +} + +void parse_z3_result( std::string filename ) +{ + std::ifstream fin( filename, std::ifstream::in ); + assert( fin.is_open() ); + + /* parsing */ + std::string line; + do + { + std::getline( fin, line ); /* first line: "sat" */ + } while ( line != "sat" ); + assert( line == "sat" ); + std::getline( fin, line ); /* second line: "((total <>)" */ + uint32_t total = std::stoi( line.substr( 8, line.find_first_of( ')' ) - 8 ) ); + std::cout << "[i] total = " << total; + if ( _ps.assume.balance_pos ) + { + std::getline( fin, line ); /* third line: " (depth <>))" */ + uint32_t depth = std::stoi( line.substr( 8, line.find_first_of( ')' ) - 8 ) ); + std::cout << ", depth = " << depth; + } + std::cout << "\n"; + + while ( std::getline( fin, line ) ) /* remaining lines: "((l<> <>)" or " (l<> <>)" or " (l<> <>))" */ + { + line = line.substr( line.find( 'l' ) + 1 ); + uint32_t n = std::stoi( line.substr( 0, line.find( ' ' ) ) ); + line = line.substr( line.find( ' ' ) + 1 ); + _levels[n] = std::stoi( line.substr( 0, line.find_first_of( ')' ) ) ); + } + adjust_depth(); +} + +#endif \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/balancing.hpp b/third-party/mockturtle/include/mockturtle/algorithms/balancing.hpp new file mode 100644 index 00000000000..0ba4c929779 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/balancing.hpp @@ -0,0 +1,446 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file balancing.hpp + \brief Cut-based depth-optimization + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/depth_view.hpp" +#include "../views/mapping_view.hpp" +#include "../views/topo_view.hpp" +#include "balancing/esop_balancing.hpp" +#include "balancing/sop_balancing.hpp" +#include "balancing/utils.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "lut_mapper.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for balancing. + */ +struct balancing_params +{ + /*! \brief Cut enumeration params. */ + cut_enumeration_params cut_enumeration_ps; + + /*! \brief Optimize only on critical path. */ + bool only_on_critical_path{ false }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for balancing. + */ +struct balancing_stats +{ + /*! \brief Total run-time. */ + stopwatch<>::duration time_total{}; + + /*! \brief Cut enumeration run-time. */ + cut_enumeration_stats cut_enumeration_st; + + /*! \brief Prints report. */ + void report() const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] Cut enumeration stats\n" ); + cut_enumeration_st.report(); + } +}; + +namespace detail +{ + +template +struct balancing_impl +{ + balancing_impl( Ntk const& ntk, rebalancing_function_t const& rebalancing_fn, balancing_params const& ps, balancing_stats& st ) + : ntk_( ntk ), + rebalancing_fn_( rebalancing_fn ), + ps_( ps ), + st_( st ) + { + } + + Ntk run() + { + Ntk dest; + node_map, Ntk> old_to_new( ntk_ ); + + /* input arrival times and mapping */ + old_to_new[ntk_.get_constant( false )] = { dest.get_constant( false ), 0u }; + if ( ntk_.get_node( ntk_.get_constant( false ) ) != ntk_.get_node( ntk_.get_constant( true ) ) ) + { + old_to_new[ntk_.get_constant( true )] = { dest.get_constant( true ), 0u }; + } + ntk_.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = { dest.create_pi(), 0u }; + } ); + + std::shared_ptr> depth_ntk; + if ( ps_.only_on_critical_path ) + { + depth_ntk = std::make_shared>( ntk_ ); + } + + stopwatch<> t( st_.time_total ); + const auto cuts = cut_enumeration( ntk_, ps_.cut_enumeration_ps, &st_.cut_enumeration_st ); + + uint32_t current_level{}; + const auto size = ntk_.size(); + progress_bar pbar{ ntk_.size(), "balancing |{0}| node = {1:>4} / " + std::to_string( size ) + " current level = {2}", ps_.progress }; + topo_view{ ntk_ }.foreach_node( [&]( auto const& n, auto index ) { + pbar( index, index, current_level ); + + if ( ntk_.is_constant( n ) || ntk_.is_pi( n ) ) + { + return; + } + + if ( ps_.only_on_critical_path && !depth_ntk->is_on_critical_path( n ) ) + { + std::vector> children; + ntk_.foreach_fanin( n, [&]( auto const& f ) { + const auto f_best = old_to_new[f].f; + children.push_back( ntk_.is_complemented( f ) ? dest.create_not( f_best ) : f_best ); + } ); + old_to_new[n] = { dest.clone_node( ntk_, n, children ), depth_ntk->level( n ) }; + return; + } + + arrival_time_pair best{ {}, std::numeric_limits::max() }; + uint32_t best_size{}; + for ( auto& cut : cuts.cuts( ntk_.node_to_index( n ) ) ) + { + if ( cut->size() == 1u || kitty::is_const0( cuts.truth_table( *cut ) ) ) + { + continue; + } + + std::vector> arrival_times( cut->size() ); + std::transform( cut->begin(), cut->end(), arrival_times.begin(), [&]( auto leaf ) { return old_to_new[ntk_.index_to_node( leaf )]; } ); + + rebalancing_fn_( dest, cuts.truth_table( *cut ), arrival_times, best.level, best_size, [&]( arrival_time_pair const& cand, uint32_t cand_size ) { + if ( cand.level < best.level || ( cand.level == best.level && cand_size < best_size ) ) + { + best = cand; + best_size = cand_size; + } + } ); + } + old_to_new[n] = best; + current_level = std::max( current_level, best.level ); + } ); + + ntk_.foreach_po( [&]( auto const& f ) { + const auto s = old_to_new[f].f; + dest.create_po( ntk_.is_complemented( f ) ? dest.create_not( s ) : s ); + } ); + + return cleanup_dangling( dest ); + } + +private: + Ntk const& ntk_; + rebalancing_function_t const& rebalancing_fn_; + balancing_params const& ps_; + balancing_stats& st_; +}; + +template +struct balancing_decomp_impl +{ + balancing_decomp_impl( mapping_view const& ntk, rebalancing_function_t const& rebalancing_fn ) + : ntk_( ntk ), + rebalancing_fn_( rebalancing_fn ) + { + } + + Ntk run() + { + Ntk dest; + node_map, Ntk> old_to_new( ntk_ ); + + /* input arrival times and mapping */ + old_to_new[ntk_.get_constant( false )] = { dest.get_constant( false ), 0u }; + if ( ntk_.get_node( ntk_.get_constant( false ) ) != ntk_.get_node( ntk_.get_constant( true ) ) ) + { + old_to_new[ntk_.get_constant( true )] = { dest.get_constant( true ), 0u }; + } + + ntk_.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = { dest.create_pi(), 0u }; + } ); + + if constexpr ( has_foreach_ro_v ) + { + ntk_.foreach_ro( [&]( auto const& n ) { + old_to_new[n] = { dest.create_ro(), 0u }; + } ); + } + + topo_view{ ntk_ }.foreach_node( [&]( auto const& n, auto index ) { + if ( ntk_.is_constant( n ) || ntk_.is_ci( n ) ) + { + return; + } + + if ( !ntk_.is_cell_root( n ) ) + { + return; + } + + std::vector> arrival_times; + ntk_.foreach_cell_fanin( n, [&]( auto const& leaf, uint32_t ctr ) { + arrival_times.push_back( old_to_new[ntk_.index_to_node( leaf )] ); + } ); + + kitty::dynamic_truth_table tt = ntk_.cell_function( n ); + + rebalancing_fn_( dest, tt, arrival_times, UINT32_MAX, UINT32_MAX, [&]( arrival_time_pair cand, uint32_t cand_size ) { + (void)cand_size; + old_to_new[n] = cand; + } ); + } ); + + ntk_.foreach_po( [&]( auto const& f ) { + const auto s = old_to_new[f].f; + dest.create_po( ntk_.is_complemented( f ) ? dest.create_not( s ) : s ); + } ); + + if constexpr ( has_foreach_ri_v ) + { + ntk_.foreach_ri( [&]( auto const& f ) { + const auto s = old_to_new[f].f; + dest.create_ri( ntk_.is_complemented( f ) ? dest.create_not( s ) : s ); + } ); + } + + return dest; + } + +private: + mapping_view const& ntk_; + rebalancing_function_t const& rebalancing_fn_; +}; + +} // namespace detail + +/*! Balancing of a logic network + * + * This function implements a dynamic-programming and cut-enumeration based + * balancing algorithm. It returns a new network of the same type and performs + * generic balancing by providing a rebalancing function. + * + * The template parameter `CostFn` is only used to compute the critical paths, + * when the `only_on_critical_path` parameter is assigned true. Note that the + * size for rewriting candidates is computed by the rebalancing function and + * may not correspond to the cost given by CostFn. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const auto aig = ...; + + sop_rebalancing balance_fn; + balancing_params ps; + ps.cut_enumeration_ps.cut_size = 6u; + const auto balanced_aig = balancing( aig, {balance_fn}, ps ); + \endverbatim + */ +template> +Ntk balancing( Ntk const& ntk, rebalancing_function_t const& rebalancing_fn = {}, balancing_params const& ps = {}, balancing_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + + balancing_stats st; + const auto dest = detail::balancing_impl{ ntk, rebalancing_fn, ps, st }.run(); + + if ( pst ) + { + *pst = st; + } + if ( ps.verbose ) + { + st.report(); + } + + return dest; +} + +/*! \brief SOP balancing of a logic network + * + * This function implements an LUT-based SOP balancing algorithm. + * It returns a new network of the same type and performs + * generic balancing by providing a rebalancing function. + * + */ +template +Ntk sop_balancing( Ntk const& ntk, lut_map_params const& ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + + lut_map_params mps; + mps = ps; + mps.sop_balancing = true; + mps.esop_balancing = false; + mps.verbose = false; + lut_map_stats st; + + /* perform SOP-driven mapping */ + mapping_view map_ntk{ ntk }; + lut_map_inplace( map_ntk, mps, &st ); + + /* decompose mapping */ + sop_rebalancing balance_fn; + balance_fn.both_phases_ = true; + const auto dest = call_with_stopwatch( st.time_total, [&]() { + return detail::balancing_decomp_impl{ map_ntk, balance_fn }.run(); + } ); + + if ( pst ) + { + *pst = st; + } + if ( ps.verbose ) + { + st.report(); + } + + return dest; +} + +/*! \brief ESOP balancing of a logic network + * + * This function implements an LUT-based ESOP balancing algorithm. + * It returns a new network of the same type and performs + * generic balancing by providing a rebalancing function. + * + */ +template +Ntk esop_balancing( Ntk const& ntk, lut_map_params const& ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + + lut_map_params mps; + mps = ps; + mps.esop_balancing = true; + mps.sop_balancing = false; + mps.verbose = false; + lut_map_stats st; + + /* perform ESOP-driven mapping */ + mapping_view map_ntk{ ntk }; + lut_map_inplace( map_ntk, mps, &st ); + + /* decompose mapping */ + esop_rebalancing balance_fn; + balance_fn.both_phases = true; + const auto dest = call_with_stopwatch( st.time_total, [&]() { + return detail::balancing_decomp_impl{ map_ntk, balance_fn }.run(); + } ); + + if ( pst ) + { + *pst = st; + } + if ( ps.verbose ) + { + st.report(); + } + + return dest; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/balancing/esop_balancing.hpp b/third-party/mockturtle/include/mockturtle/algorithms/balancing/esop_balancing.hpp new file mode 100644 index 00000000000..af717c8e09a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/balancing/esop_balancing.hpp @@ -0,0 +1,305 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file esop_balancing.hpp + \brief ESOP-based balancing engine for `balancing` algorithm + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../traits.hpp" +#include "../../utils/stopwatch.hpp" +#include "../balancing.hpp" +#include "../exorcism.hpp" +#include "utils.hpp" + +namespace mockturtle +{ + +template +struct esop_rebalancing +{ + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, std::vector> const& inputs, uint32_t best_level, uint32_t best_cost, rebalancing_function_callback_t const& callback ) const + { + bool inverted = false; + auto [and_terms, max_level, num_and_gates] = create_function( dest, function, inputs, inverted ); + + /* Try with MUX decomposition */ + if ( mux_optimization ) + { + const auto index = std::distance( inputs.begin(), std::max_element( inputs.begin(), inputs.end(), []( auto const& a1, auto const& a2 ) { return a1.level < a2.level; } ) ); + + bool inverted0 = false, inverted1 = false; + auto [and_terms0, max_level0, num_and_gates0] = create_function( dest, kitty::cofactor0( function, static_cast( index ) ), inputs, inverted0 ); + auto [and_terms1, max_level1, num_and_gates1] = create_function( dest, kitty::cofactor1( function, static_cast( index ) ), inputs, inverted1 ); + + const auto max_level_mux = std::max( max_level0, max_level1 ) + 1u; + + if ( max_level_mux < max_level && max_level_mux < best_level ) + { + callback( { dest.create_ite( inputs[index].f, + balanced_xor_tree( dest, and_terms1 ).f ^ inverted1, + balanced_xor_tree( dest, and_terms0 ).f ^ inverted0 ), + max_level_mux }, + num_and_gates0 + num_and_gates1 + 1u ); + return; + } + } + + arrival_time_pair cand = balanced_xor_tree( dest, and_terms ); + if ( cand.level < best_level || ( cand.level == best_level && num_and_gates < best_cost ) ) + { + cand.f = cand.f ^ inverted; + callback( cand, num_and_gates ); + } + } + +private: + std::tuple, uint32_t, uint32_t> create_function( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + if ( spp_optimization ) + { + return create_function_from_spp( dest, func, arrival_times, inverted ); + } + else + { + return create_function_from_esop( dest, func, arrival_times, inverted ); + } + } + + std::tuple, uint32_t, uint32_t> create_function_from_esop( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + const auto esop = create_sop_form( func, inverted ); + + stopwatch<> t_tree( time_tree_balancing ); + arrival_time_queue and_terms; + uint32_t max_level{}; + uint32_t num_and_gates{}; + for ( auto const& cube : esop ) + { + arrival_time_queue product_queue; + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( cube.get_mask( i ) ) + { + const auto [f, l] = arrival_times[i]; + product_queue.push( { cube.get_bit( i ) ? f : dest.create_not( f ), l } ); + } + } + if ( product_queue.size() ) + { + num_and_gates += static_cast( product_queue.size() ) - 1u; + } + arrival_time_pair and_res = balanced_and_tree( dest, product_queue ); + and_terms.push( and_res ); + max_level = std::max( max_level, and_res.level ); + } + return { and_terms, max_level, num_and_gates }; + } + + std::tuple, uint32_t, uint32_t> create_function_from_spp( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + const auto esop = create_sop_form( func, inverted ); + const auto [spp, sums] = kitty::simple_spp( esop, func.num_vars() ); + + stopwatch<> t_tree( time_tree_balancing ); + arrival_time_queue and_terms; + uint32_t max_level{}; + uint32_t num_and_gates{}; + for ( auto const& cube : spp ) + { + arrival_time_queue product_queue; + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( cube.get_mask( i ) ) + { + const auto [f, l] = arrival_times[i]; + product_queue.push( { cube.get_bit( i ) ? f : dest.create_not( f ), l } ); + } + } + for ( auto i = 0u; i < sums.size(); ++i ) + { + if ( cube.get_mask( func.num_vars() + i ) ) + { + std::vector> xor_terms; + uint32_t xor_level{}; + for ( auto j = 0u; j < func.num_vars(); ++j ) + { + if ( ( sums[i] >> j ) & 1 ) + { + const auto [f, l] = arrival_times[j]; + xor_terms.push_back( f ); + xor_level = std::max( xor_level, l ); + } + } + const auto f = dest.create_nary_xor( xor_terms ); + product_queue.push( { cube.get_bit( func.num_vars() + i ) ? f : dest.create_not( f ), xor_level } ); + } + } + if ( product_queue.size() ) + { + num_and_gates += static_cast( product_queue.size() ) - 1u; + } + arrival_time_pair and_res = balanced_and_tree( dest, product_queue ); + and_terms.push( and_res ); + max_level = std::max( max_level, and_res.level ); + } + return { and_terms, max_level, num_and_gates }; + } + + arrival_time_pair balanced_and_tree( Ntk& dest, arrival_time_queue& queue ) const + { + if ( queue.empty() ) + { + return { dest.get_constant( true ), 0u }; + } + + while ( queue.size() > 1u ) + { + auto [s1, l1] = queue.top(); + queue.pop(); + auto [s2, l2] = queue.top(); + queue.pop(); + const auto s = dest.create_and( s1, s2 ); + const auto l = std::max( l1, l2 ) + 1; + queue.push( { s, l } ); + } + return queue.top(); + } + + arrival_time_pair balanced_xor_tree( Ntk& dest, arrival_time_queue& queue ) const + { + if ( queue.empty() ) + { + return { dest.get_constant( true ), 0u }; + } + + while ( queue.size() > 1u ) + { + auto [s1, l1] = queue.top(); + queue.pop(); + auto [s2, l2] = queue.top(); + queue.pop(); + const auto s = dest.create_xor( s1, s2 ); + const auto l = std::max( l1, l2 ) + 1; + queue.push( { s, l } ); + } + return queue.top(); + } + + std::vector create_sop_form( kitty::dynamic_truth_table const& func, bool& inverted ) const + { + stopwatch<> t( time_sop ); + inverted = false; + + if ( auto it = sop_hash_.find( func ); it != sop_hash_.end() ) + { + sop_cache_hits++; + return it->second; + } + + if ( both_phases ) + { + if ( auto it = sop_hash_.find( ~func ); it != sop_hash_.end() ) + { + inverted = true; + sop_cache_hits++; + return it->second; + } + } + + sop_cache_misses++; + std::vector sop = mockturtle::exorcism( func ); + + if ( both_phases ) + { + std::vector n_sop = mockturtle::exorcism( ~func ); + + if ( n_sop.size() < sop.size() ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + else if ( n_sop.size() == sop.size() ) + { + /* compute literal cost */ + uint32_t lit = 0, n_lit = 0; + for ( auto const& c : sop ) + { + lit += c.num_literals(); + } + for ( auto const& c : n_sop ) + { + n_lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + } + } + + return sop_hash_[func] = sop; + } + +private: + mutable std::unordered_map, kitty::hash> sop_hash_; + +public: + bool both_phases{ false }; + bool spp_optimization{ false }; + bool mux_optimization{ false }; + +public: + mutable uint32_t sop_cache_hits{}; + mutable uint32_t sop_cache_misses{}; + + mutable stopwatch<>::duration time_sop{}; + mutable stopwatch<>::duration time_tree_balancing{}; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/balancing/sop_balancing.hpp b/third-party/mockturtle/include/mockturtle/algorithms/balancing/sop_balancing.hpp new file mode 100644 index 00000000000..0b808eae328 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/balancing/sop_balancing.hpp @@ -0,0 +1,201 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sop_balancing.hpp + \brief SOP-based balancing engine for `balancing` algorithm + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../../traits.hpp" +#include "../../utils/stopwatch.hpp" +#include "../balancing.hpp" +#include "utils.hpp" + +namespace mockturtle +{ + +/*! \brief SOP rebalancing function + * + * This class can be used together with the generic `balancing` function. It + * converts each cut function into an SOP and then performs weight-oriented + * tree balancing on the AND terms and the outer OR function. + */ +template +struct sop_rebalancing +{ + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, std::vector> const& inputs, uint32_t best_level, uint32_t best_cost, rebalancing_function_callback_t const& callback ) const + { + bool inverted = false; + auto [and_terms, num_and_gates] = create_function( dest, function, inputs, inverted ); + const auto num_gates = num_and_gates + ( and_terms.empty() ? 0u : static_cast( and_terms.size() ) - 1u ); + arrival_time_pair cand = balanced_tree( dest, and_terms, false ); + + if ( cand.level < best_level || ( cand.level == best_level && num_gates < best_cost ) ) + { + cand.f = cand.f ^ inverted; + callback( cand, num_gates ); + } + } + +private: + std::pair, uint32_t> create_function( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + const auto sop = create_sop_form( func, inverted ); + + stopwatch<> t_tree( time_tree_balancing ); + arrival_time_queue and_terms; + uint32_t num_and_gates{}; + for ( auto const& cube : sop ) + { + arrival_time_queue product_queue; + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( cube.get_mask( i ) ) + { + const auto [f, l] = arrival_times[i]; + product_queue.push( { cube.get_bit( i ) ? f : dest.create_not( f ), l } ); + } + } + if ( !product_queue.empty() ) + { + num_and_gates += static_cast( product_queue.size() ) - 1u; + } + and_terms.push( balanced_tree( dest, product_queue ) ); + } + return { and_terms, num_and_gates }; + } + + arrival_time_pair balanced_tree( Ntk& dest, arrival_time_queue& queue, bool _and = true ) const + { + if ( queue.empty() ) + { + return { dest.get_constant( true ), 0u }; + } + + while ( queue.size() > 1u ) + { + auto [s1, l1] = queue.top(); + queue.pop(); + auto [s2, l2] = queue.top(); + queue.pop(); + const auto s = _and ? dest.create_and( s1, s2 ) : dest.create_or( s1, s2 ); + const auto l = std::max( l1, l2 ) + 1; + queue.push( { s, l } ); + } + return queue.top(); + } + + std::vector create_sop_form( kitty::dynamic_truth_table const& func, bool& inverted ) const + { + stopwatch<> t( time_sop ); + inverted = false; + + if ( auto it = sop_hash_.find( func ); it != sop_hash_.end() ) + { + sop_cache_hits++; + return it->second; + } + + if ( both_phases_ ) + { + if ( auto it = sop_hash_.find( ~func ); it != sop_hash_.end() ) + { + inverted = true; + sop_cache_hits++; + return it->second; + } + } + + sop_cache_misses++; + std::vector sop = kitty::isop( func ); + + if ( both_phases_ ) + { + std::vector n_sop = kitty::isop( ~func ); + + if ( n_sop.size() < sop.size() ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + else if ( n_sop.size() == sop.size() ) + { + /* compute literal cost */ + uint32_t lit = 0, n_lit = 0; + for ( auto const& c : sop ) + { + lit += c.num_literals(); + } + for ( auto const& c : n_sop ) + { + n_lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + } + } + + return sop_hash_[func] = sop; + } + +private: + mutable std::unordered_map, kitty::hash> sop_hash_; + +public: + bool both_phases_{ false }; + +public: + mutable uint32_t sop_cache_hits{}; + mutable uint32_t sop_cache_misses{}; + + mutable stopwatch<>::duration time_sop{}; + mutable stopwatch<>::duration time_tree_balancing{}; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/balancing/utils.hpp b/third-party/mockturtle/include/mockturtle/algorithms/balancing/utils.hpp new file mode 100644 index 00000000000..57b569f513d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/balancing/utils.hpp @@ -0,0 +1,80 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file utils.hpp + \brief Balancing data types + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "../../traits.hpp" + +namespace mockturtle +{ + +template +struct arrival_time_pair +{ + signal f; + uint32_t level; +}; + +/*! \brief Callback function for `rebalancing_function_t`. + * + * This callback is used in the rebalancing function to announce a new candidate that + * could be used for replacement in the main balancing algorithm. Using a callback + * makes it possible to account for situations in which none, a single, or multiple + * candidates are generated. + * + * The callback returns a pair composed of the output signal of the replacement + * candidate and the level of the new candidate. Ideally, the rebalancing function + * should not call the callback with candidates that a worse level. + */ +template +using rebalancing_function_callback_t = std::function const&, uint32_t )>; + +template +using rebalancing_function_t = std::function> const&, uint32_t, uint32_t, rebalancing_function_callback_t const& )>; + +template +struct arrival_time_compare +{ + bool operator()( arrival_time_pair const& p1, arrival_time_pair const& p2 ) const + { + return p1.level > p2.level; + } +}; + +template +using arrival_time_queue = std::priority_queue, std::vector>, arrival_time_compare>; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/bi_decomposition.hpp b/third-party/mockturtle/include/mockturtle/algorithms/bi_decomposition.hpp new file mode 100644 index 00000000000..2e5b512ac4f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/bi_decomposition.hpp @@ -0,0 +1,171 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bi_decomposition.hpp + \brief BI decomposition + + \author Eleonora Testa + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class bi_decomposition_impl +{ +public: + bi_decomposition_impl( Ntk& ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& dc, std::vector> const& children ) + : _ntk( ntk ), + remainder( func ), + dc_remainder( dc ), + pis( children ) {} + + signal run() + { + /* bi_decomposition */ + if ( kitty::is_const0( binary_and( remainder, dc_remainder ) ) ) + { + return _ntk.get_constant( false ); + } + else if ( kitty::is_const0( binary_and( ~remainder, dc_remainder ) ) ) + { + return _ntk.get_constant( true ); + } + else + { + for ( auto h = 0u; h < remainder.num_vars(); h++ ) + { + auto var = remainder.construct(); + kitty::create_nth_var( var, h ); + if ( binary_and( remainder, dc_remainder ) == binary_and( var, dc_remainder ) ) + { + return pis[h]; + } + else if ( binary_and( remainder, dc_remainder ) == binary_and( ~var, dc_remainder ) ) + { + return _ntk.create_not( pis[h] ); + } + } + } + + auto bi_dec = kitty::is_bi_decomposable_mc( remainder, dc_remainder ); + auto res = std::get<1>( bi_dec ); + + remainder = std::get<2>( bi_dec )[0]; + dc_remainder = std::get<2>( bi_dec )[1]; + const auto right = run(); + + remainder = std::get<2>( bi_dec )[2]; + dc_remainder = std::get<2>( bi_dec )[3]; + const auto left = run(); + + if ( res == kitty::bi_decomposition::and_ ) + { + return _ntk.create_and( left, right ); + } + else if ( res == kitty::bi_decomposition::or_ ) + { + return _ntk.create_or( left, right ); + } + else if ( res == kitty::bi_decomposition::weak_and_ ) + { + return _ntk.create_and( left, right ); + } + else if ( res == kitty::bi_decomposition::weak_or_ ) + { + return _ntk.create_or( left, right ); + } + else if ( res == kitty::bi_decomposition::xor_ ) + { + return _ntk.create_xor( left, right ); + } + else + { + assert( false ); + return signal(); + } + } + +private: + Ntk& _ntk; + kitty::dynamic_truth_table remainder; + kitty::dynamic_truth_table dc_remainder; + std::vector> pis; +}; + +} // namespace detail + +/*! \brief Bi decomposition + * + * This function applies bi-decomposition on a truth table inside the network. + * + * Note that the number of variables in `func` and `care` must be the same. + * The function will create a network composed on two-input gates with as many primary inputs as the number of + * variables in `func` and a single output. + * + * **Required network functions:** + * - `create_and` + * - `create_or` + * - `create_xor` + * - `create_not` + * + * \param func Function as truth table + * \param care Care set of the function (as truth table) + * \return An internal signal of the network + */ + +template +signal bi_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care, std::vector> const& children ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + + detail::bi_decomposition_impl impl( ntk, func, care, children ); + return impl.run(); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cell_window.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cell_window.hpp new file mode 100644 index 00000000000..6e855abdb37 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cell_window.hpp @@ -0,0 +1,509 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cell_window.hpp + \brief Windowing in mapped network + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" + +template<> +struct std::hash> +{ + auto operator()( std::array const& key ) const + { + std::hash hasher; + return hasher( key[0] ) * 31 + hasher( key[1] ); + } +}; + +namespace mockturtle +{ + +namespace detail +{ + +template +class cell_window_storage +{ +public: + cell_window_storage( Ntk const& ntk ) : _cell_refs( ntk ), + _cell_parents( ntk ) + { + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + _num_constants++; + } + + _nodes.reserve( _max_gates >> 1 ); + _gates.reserve( _max_gates ); + } + + phmap::flat_hash_set> _nodes; /* cell roots in current window */ + phmap::flat_hash_set> _gates; /* gates in current window */ + phmap::flat_hash_set> _leaves; /* leaves of current window */ + phmap::flat_hash_set> _roots; /* roots of current window */ + + std::array _window_mask; + phmap::flat_hash_set> _window_hash; + + node_map _cell_refs; /* ref counts for cells */ + node_map>, Ntk> _cell_parents; /* parent cells */ + + std::vector> _index_to_node; + phmap::flat_hash_map, uint32_t> _node_to_index; + + uint32_t _num_constants{ 1u }; + uint32_t _max_gates{}; + bool _has_mapping{ true }; +}; + +} // namespace detail + +template +class cell_window : public Ntk +{ +public: + using storage = typename std::shared_ptr>; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + cell_window( Ntk const& ntk, uint32_t max_gates = 64 ) + : Ntk( ntk ), + _storage( std::make_shared>( ntk ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_cell_root_v, "Ntk does not implement the is_cell_root method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_cell_fanin_v, "Ntk does not implement the foreach_cell_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_has_mapping_v, "Ntk does not implement the has_mapping method" ); + + assert( Ntk::has_mapping() ); + _storage->_max_gates = max_gates; + } + + bool compute_window_for( node const& pivot ) + { + init_cell_refs(); + + // print_time<> pt; + assert( Ntk::is_cell_root( pivot ) ); + + // reset old window + _storage->_nodes.clear(); + _storage->_gates.clear(); + + std::vector gates; + gates.reserve( _storage->_max_gates ); + collect_mffc( pivot, gates ); + add_node( pivot, gates ); + + if ( gates.size() > _storage->_max_gates ) + { + assert( false ); + } + + std::optional next; + while ( ( next = find_next_pivot() ) ) + { + gates.clear(); + collect_mffc( *next, gates ); + + if ( _storage->_gates.size() + gates.size() > _storage->_max_gates ) + { + break; + } + add_node( *next, gates ); + } + + find_leaves_and_roots(); + set_indexes(); + + return _storage->_window_hash.insert( _storage->_window_mask ).second; + } + + uint32_t num_pis() const + { + return _storage->_leaves.size(); + } + + uint32_t num_pos() const + { + return _storage->_roots.size(); + } + + uint32_t num_gates() const + { + return _storage->_gates.size(); + } + + uint32_t num_cells() const + { + return _storage->_nodes.size(); + } + + uint32_t size() const + { + return _storage->_num_constants + _storage->_leaves.size() + _storage->_gates.size(); + } + + bool is_pi( node const& n ) const + { + return _storage->_leaves.count( n ); + } + + bool is_cell_root( node const& n ) const + { + return _storage->_nodes.count( n ); + } + + uint32_t node_to_index( node const& n ) const + { + return _storage->_node_to_index.at( n ); + } + + node index_to_node( uint32_t index ) const + { + return _storage->_index_to_node[index]; + } + + bool has_mapping() const + { + return _storage->_has_mapping; + } + + void clear_mapping() + { + _storage->_has_mapping = false; + for ( auto const& n : _storage->_nodes ) + { + Ntk::remove_from_mapping( n ); + } + _storage->_nodes.clear(); + } + + template + void add_to_mapping( node const& n, LeavesIterator begin, LeavesIterator end ) + { + _storage->_has_mapping = true; + _storage->_nodes.insert( n ); + Ntk::add_to_mapping( n, begin, end ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->_leaves.begin(), _storage->_leaves.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->_roots.begin(), _storage->_roots.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( _storage->_gates.begin(), _storage->_gates.end(), fn ); + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( _storage->_index_to_node.begin(), _storage->_index_to_node.end(), fn ); + } + +private: + void init_cell_refs() + { + _storage->_cell_refs.reset(); + _storage->_cell_parents.reset(); + + /* initial ref counts for cells */ + Ntk::foreach_gate( [&]( auto const& n ) { + if ( Ntk::is_cell_root( n ) ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]++; + _storage->_cell_parents[n2].push_back( n ); + } ); + } } ); + Ntk::foreach_po( [&]( auto const& f ) { + _storage->_cell_refs[f]++; + } ); + } + + void collect_mffc( node const& pivot, std::vector& gates ) + { + Ntk::incr_trav_id(); + collect_gates( pivot, gates ); + const auto it = std::remove_if( gates.begin(), gates.end(), [&]( auto const& g ) { return _storage->_gates.count( g ); } ); + gates.erase( it, gates.end() ); + } + + void collect_gates( node const& pivot, std::vector& gates ) + { + assert( !Ntk::is_pi( pivot ) ); + + Ntk::set_visited( Ntk::get_node( Ntk::get_constant( false ) ), Ntk::trav_id() ); + Ntk::set_visited( Ntk::get_node( Ntk::get_constant( true ) ), Ntk::trav_id() ); + + Ntk::foreach_cell_fanin( pivot, [this]( auto const& n ) { + Ntk::set_visited( n, Ntk::trav_id() ); + } ); + + collect_gates_rec( pivot, gates ); + } + + void collect_gates_rec( node const& n, std::vector& gates ) + { + if ( Ntk::visited( n ) == Ntk::trav_id() ) + return; + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + return; + + Ntk::set_visited( n, Ntk::trav_id() ); + Ntk::foreach_fanin( n, [&]( auto const& f ) { + collect_gates_rec( Ntk::get_node( f ), gates ); + } ); + gates.push_back( n ); + } + + void add_node( node const& pivot, std::vector const& gates ) + { + /*std::cout << "add_node(" << pivot << ", { "; + for ( auto const& g : gates ) { + std::cout << g << " "; + } + std::cout << "})\n";*/ + _storage->_nodes.insert( pivot ); + std::copy( gates.begin(), gates.end(), std::insert_iterator( _storage->_gates, _storage->_gates.begin() ) ); + } + + std::optional find_next_pivot() + { + /* deref */ + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]--; + } ); + } + + std::vector candidates; + std::unordered_set inputs; + + do + { + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + if ( !_storage->_nodes.count( n2 ) && !Ntk::is_pi( n2 ) && !_storage->_cell_refs[n2] ) + { + candidates.push_back( n2 ); + inputs.insert( n2 ); + } + } ); + } + + if ( !candidates.empty() ) + { + const auto best = max_element_unary( + candidates.begin(), candidates.end(), + [&]( auto const& cand ) { + auto cnt{ 0 }; + this->foreach_cell_fanin( cand, [&]( auto const& n2 ) { + cnt += inputs.count( n2 ); + } ); + return cnt; + }, + -1 ); + candidates[0] = *best; + break; + } + + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + if ( !_storage->_nodes.count( n2 ) && !Ntk::is_pi( n2 ) ) + { + candidates.push_back( n2 ); + inputs.insert( n2 ); + } + } ); + } + + for ( auto const& n : _storage->_nodes ) + { + if ( _storage->_cell_refs[n] == 0 ) + continue; + if ( _storage->_cell_refs[n] >= 5 ) + continue; + if ( _storage->_cell_refs[n] == 1 && _storage->_cell_parents[n].size() == 1 && !_storage->_nodes.count( _storage->_cell_parents[n].front() ) ) + { + candidates.clear(); + candidates.push_back( _storage->_cell_parents[n].front() ); + break; + } + std::copy_if( _storage->_cell_parents[n].begin(), _storage->_cell_parents[n].end(), + std::back_inserter( candidates ), + [&]( auto const& g ) { + return !_storage->_nodes.count( g ); + } ); + } + + if ( !candidates.empty() ) + { + const auto best = max_element_unary( + candidates.begin(), candidates.end(), + [&]( auto const& cand ) { + auto cnt{ 0 }; + this->foreach_cell_fanin( cand, [&]( auto const& n2 ) { + cnt += inputs.count( n2 ); + } ); + return cnt; + }, + -1 ); + candidates[0] = *best; + } + } while ( false ); + + /* ref */ + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]++; + } ); + } + + if ( candidates.empty() ) + { + return std::nullopt; + } + else + { + return candidates.front(); + } + } + + void find_leaves_and_roots() + { + _storage->_leaves.clear(); + _storage->_window_mask[0] = 0u; + for ( auto const& g : _storage->_gates ) + { + Ntk::foreach_fanin( g, [&]( auto const& f ) { + auto const child = Ntk::get_node( f ); + if ( !_storage->_gates.count( child ) ) + { + _storage->_leaves.insert( child ); + _storage->_window_mask[0] |= UINT64_C( 1 ) << ( Ntk::node_to_index( child ) % 64 ); + } + } ); + } + + _storage->_roots.clear(); + _storage->_window_mask[1] = 0u; + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]--; + } ); + } + for ( auto const& n : _storage->_nodes ) + { + if ( _storage->_cell_refs[n] ) + { + _storage->_roots.insert( Ntk::make_signal( n ) ); + _storage->_window_mask[1] |= UINT64_C( 1 ) << ( Ntk::node_to_index( n ) % 64 ); + } + } + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]++; + } ); + } + } + + void set_indexes() + { + _storage->_index_to_node.resize( _storage->_num_constants + _storage->_leaves.size() + _storage->_gates.size() ); + _storage->_node_to_index.clear(); + + _storage->_node_to_index[_storage->_index_to_node[0] = Ntk::get_node( Ntk::get_constant( false ) )] = 0; + + if ( _storage->_num_constants == 2u ) + { + _storage->_node_to_index[_storage->_index_to_node[1] = Ntk::get_node( Ntk::get_constant( true ) )] = 1; + } + + auto idx = _storage->_num_constants; + for ( auto const& n : _storage->_leaves ) + { + _storage->_node_to_index[_storage->_index_to_node[idx] = n] = idx; + ++idx; + } + for ( auto const& n : _storage->_gates ) + { + _storage->_node_to_index[_storage->_index_to_node[idx] = n] = idx; + ++idx; + } + + assert( _storage->_index_to_node.size() == idx ); + } + +private: + std::shared_ptr> _storage; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/circuit_validator.hpp b/third-party/mockturtle/include/mockturtle/algorithms/circuit_validator.hpp new file mode 100644 index 00000000000..e0fe50d4c97 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/circuit_validator.hpp @@ -0,0 +1,799 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file circuit_validator.hpp + \brief Validate potential circuit optimization choices with SAT. + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/events.hpp" +#include "../utils/index_list/index_list.hpp" +#include "../utils/node_map.hpp" +#include "cnf.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +struct validator_params +{ + /*! \brief Maximum number of clauses of the SAT solver. (incremental CNF construction) */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Whether to consider ODC, and how many levels. 0 = No consideration. -1 = Consider TFO until PO. */ + int32_t odc_levels{ 0 }; + + /*! \brief Conflict limit of the SAT solver. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Seed for randomized solving. */ + uint32_t random_seed{ 0 }; +}; + +template +class circuit_validator +{ +public: + static constexpr bool use_odc_ = use_odc; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using add_clause_fn_t = std::function const& )>; + + enum gate_type + { + AND, + XOR, + MAJ, + MUX + }; + + explicit circuit_validator( Ntk const& ntk, validator_params const& ps = {} ) + : ntk( ntk ), ps( ps ), literals( ntk ), constructed( ntk ), num_invoke( 0u ), cex( ntk.num_pis() ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_xor3 method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + + if constexpr ( use_pushpop ) + { +#if defined( BILL_HAS_Z3 ) + static_assert( Solver == bill::solvers::z3 || Solver == bill::solvers::bsat2, "Solver does not support push/pop" ); +#else + static_assert( Solver == bill::solvers::bsat2, "Solver does not support push/pop" ); +#endif + } + if constexpr ( randomize ) + { +#if defined( BILL_HAS_Z3 ) + static_assert( Solver == bill::solvers::z3 || Solver == bill::solvers::bsat2, "Solver does not support set_random" ); +#else + static_assert( Solver == bill::solvers::bsat2, "Solver does not support set_random" ); +#endif + } + if constexpr ( use_odc ) + { + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + } + + add_event = ntk.events().register_add_event( [&]( node const& n ) { + (void)n; + literals.resize(); + } ); + + /* constants are mapped to var 0 */ + literals[ntk.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + literals[ntk.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + + /* first indexes (starting from 1) are for PIs */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + literals[n] = bill::lit_type( i + 1, bill::lit_type::polarities::positive ); + } ); + + restart(); + } + + ~circuit_validator() + { + ntk.events().release_add_event( add_event ); + } + + /*! \brief Set ODC levels */ + void set_odc_levels( uint32_t odc_levels ) + { + ps.odc_levels = odc_levels; + } + + /*! \brief Validate functional equivalence of signals `f` and `d`. */ + std::optional validate( signal const& f, signal const& d ) + { + if ( !constructed.has( d ) && !ntk.is_pi( ntk.get_node( d ) ) && !ntk.is_constant( ntk.get_node( d ) ) ) + { + construct( ntk.get_node( d ) ); + } + auto const res = validate( ntk.get_node( f ), lit_not_cond( literals[d], ntk.is_complemented( f ) ^ ntk.is_complemented( d ) ) ); + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return res; + } + + /*! \brief Validate functional equivalence of node `root` and signal `d`. */ + std::optional validate( node const& root, signal const& d ) + { + if ( !constructed.has( d ) && !ntk.is_pi( ntk.get_node( d ) ) && !ntk.is_constant( ntk.get_node( d ) ) ) + { + construct( ntk.get_node( d ) ); + } + auto const res = validate( root, lit_not_cond( literals[d], ntk.is_complemented( d ) ) ); + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return res; + } + + /*! \brief Validate functional equivalence of signal `f` with a circuit represented by an index_list. + * + * \param id_list The index_list representing a circuit. + * \param divs Existing nodes in the network, serving as PIs of `id_list`. + * \param inverted Whether to validate equivalence or inverse equivalence. + */ + template + std::optional validate( signal const& f, std::vector const& divs, index_list_type const& id_list, bool inverted = false ) + { + return validate( ntk.get_node( f ), divs.begin(), divs.end(), id_list, inverted ^ ntk.is_complemented( f ) ); + } + + /*! \brief Validate functional equivalence of node `root` with an index_list. */ + template + std::optional validate( node const& root, std::vector const& divs, index_list_type const& id_list, bool inverted = false ) + { + return validate( root, divs.begin(), divs.end(), id_list, inverted ); + } + + /*! \brief Validate functional equivalence of signal `f` with an index_list. */ + template + std::optional validate( signal const& f, iterator_type divs_begin, iterator_type divs_end, index_list_type const& id_list, bool inverted = false ) + { + return validate( ntk.get_node( f ), divs_begin, divs_end, id_list, inverted ^ ntk.is_complemented( f ) ); + } + + /*! \brief Validate functional equivalence of node `root` with an index_list. */ + template + std::optional validate( node const& root, iterator_type divs_begin, iterator_type divs_end, index_list_type const& id_list, bool inverted = false ) + { + static_assert( std::is_same_v || + std::is_same_v> || + std::is_same_v> || + std::is_same_v, "Unknown type of index list" ); + assert( uint64_t( std::distance( divs_begin, divs_end ) ) == id_list.num_pis() && "Size of the provided divisor list does not match number of PIs of the index list" ); + assert( id_list.num_pos() == 1u && "Index list must have exactly one PO" ); + + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + std::vector lits; + lits.reserve( id_list.num_pis() + id_list.num_gates() + 1 ); + lits.emplace_back( literals[ntk.get_constant( false )] ); + for ( auto it = divs_begin; it != divs_end; ++it ) + { + if ( !constructed.has( *it ) && !ntk.is_pi( *it ) && !ntk.is_constant( *it ) ) + { + construct( *it ); + } + lits.emplace_back( literals[*it] ); + } + + if constexpr ( use_pushpop ) + { + push(); + } + + if constexpr ( std::is_same_v> || std::is_same_v> ) + { + id_list.foreach_gate( [&]( uint32_t id_lit0, uint32_t id_lit1 ) { + uint32_t const node_pos0 = id_lit0 >> 1; + uint32_t const node_pos1 = id_lit1 >> 1; + assert( node_pos0 < lits.size() ); + assert( node_pos1 < lits.size() ); + lits.emplace_back( add_clauses_for_2input_gate( lit_not_cond( lits[node_pos0], id_lit0 & 0x1 ), lit_not_cond( lits[node_pos1], id_lit1 & 0x1 ), std::nullopt, id_lit0 < id_lit1 ? AND : XOR ) ); + } ); + } + if constexpr ( std::is_same_v ) + { + id_list.foreach_gate( [&]( uint32_t id_lit0, uint32_t id_lit1, uint32_t id_lit2 ) { + uint32_t const node_pos0 = id_lit0 >> 1; + uint32_t const node_pos1 = id_lit1 >> 1; + uint32_t const node_pos2 = id_lit2 >> 1; + assert( node_pos0 < lits.size() ); + assert( node_pos1 < lits.size() ); + assert( node_pos2 < lits.size() ); + lits.emplace_back( add_clauses_for_3input_gate( lit_not_cond( lits[node_pos0], id_lit0 & 0x1 ), lit_not_cond( lits[node_pos1], id_lit1 & 0x1 ), lit_not_cond( lits[node_pos2], id_lit2 & 0x1 ), std::nullopt, MAJ ) ); + } ); + } + if constexpr ( std::is_same_v ) + { + id_list.foreach_gate( [&]( uint32_t id_lit0, uint32_t id_lit1, uint32_t id_lit2 ) { + uint32_t const node_pos0 = id_lit0 >> 1; + uint32_t const node_pos1 = id_lit1 >> 1; + uint32_t const node_pos2 = id_lit2 >> 1; + assert( node_pos0 < lits.size() ); + assert( node_pos1 < lits.size() ); + assert( node_pos2 < lits.size() ); + lits.emplace_back( add_clauses_for_3input_gate( lit_not_cond( lits[node_pos0], id_lit0 & 0x1 ), lit_not_cond( lits[node_pos1], id_lit1 & 0x1 ), lit_not_cond( lits[node_pos2], id_lit2 & 0x1 ), std::nullopt, MUX ) ); + } ); + } + + bill::lit_type lit_out; + id_list.foreach_po( [&]( uint32_t id_lit ) { + lit_out = lit_not_cond( lits[id_lit >> 1], ( id_lit & 0x1 ) ^ inverted ); + } ); + + auto const res = validate( root, lit_out ); + + if constexpr ( use_pushpop ) + { + pop(); + } + + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + + return res; + } + + /*! \brief Validate whether signal `f` is a constant of `value`. */ + std::optional validate( signal const& f, bool value ) + { + return validate( ntk.get_node( f ), value ^ ntk.is_complemented( f ) ); + } + + /*! \brief Validate whether node `root` is a constant of `value`. */ + std::optional validate( node const& root, bool value ) + { + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + std::optional res; + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + if constexpr ( use_pushpop ) + { + push(); + } + res = solve( { build_odc_window( root, ~literals[root] ), lit_not_cond( literals[root], value ) } ); + if constexpr ( use_pushpop ) + { + pop(); + } + } + else + { + res = solve( { lit_not_cond( literals[root], value ) } ); + } + } + else + { + res = solve( { lit_not_cond( literals[root], value ) } ); + } + + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return res; + } + + /*! \brief Generate pattern(s) for signal `f` to be `value`, optionally blocking several known patterns. + * + * Requires `use_pushpop = true`, which is only supported for `bsat2` and `z3`. If `bsat2` is used, + * and if the network has more than 2048 PIs, the `BUFFER_SIZE` in `lib/bill/sat/interface/abc_bsat2.hpp` + * has to be increased to at least `ntk.num_pis()`. + * + * If `block_patterns` and the returned vector are both empty, `f` is validated to be a constant of `!value`. + * + * \param block_patterns Patterns to be blocked in the solver. (Will not generate any of them.) + * \param num_patterns Number of patterns to be generated, if possible. (The size of the result may be smaller than this number, but never larger.) + */ + template> + std::vector> generate_pattern( signal const& f, bool value, std::vector> const& block_patterns = {}, uint32_t num_patterns = 1u ) + { + return generate_pattern( ntk.get_node( f ), value ^ ntk.is_complemented( f ), block_patterns, num_patterns ); + } + + /*! \brief Generate pattern(s) for node `root` to be `value`, optionally blocking several known patterns. */ + template> + std::vector> generate_pattern( node const& root, bool value, std::vector> const& block_patterns = {}, uint32_t num_patterns = 1u ) + { + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + push(); + + for ( auto const& pattern : block_patterns ) + { + block_pattern( pattern ); + } + + std::vector assumptions( { lit_not_cond( literals[root], !value ) } ); + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + assumptions.emplace_back( build_odc_window( root, ~literals[root] ) ); + } + } + + std::optional res; + std::vector> generated; + for ( auto i = 0u; i < num_patterns; ++i ) + { + res = solve( assumptions ); + + if ( !res || *res ) /* timeout or UNSAT */ + { + break; + } + else /* SAT */ + { + generated.emplace_back( cex ); + block_pattern( cex ); + } + } + + pop(); + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return generated; + } + + /*! \brief Update CNF clauses. + * + * This function should be called when the function of one or more nodes + * has been modified (typically when utilizing ODCs). + */ + void update() + { + restart(); + } + +private: + void restart() + { + num_invoke = 0u; + solver.restart(); + if constexpr ( randomize ) + { + solver.set_random_phase( ps.random_seed ); + } + + constructed.reset(); + + solver.add_variables( ntk.num_pis() + 1 ); + solver.add_clause( { ~literals[ntk.get_constant( false )] } ); + + if constexpr ( has_EXCDC_interface_v ) + { + ntk.add_EXCDC_clauses( solver ); + } + + if constexpr ( has_EXODC_interface_v ) + { + if ( ps.odc_levels == -1 ) + { + po_lits_link.clear(); + typename Ntk::base_type oec_ntk; + ntk.build_oe_miter( oec_ntk ); + + std::vector po_lits; + ntk.foreach_po( [&]( auto const& f ) { + if ( !ntk.is_pi( ntk.get_node( f ) ) && !constructed.has( f ) ) + { + construct( ntk.get_node( f ) ); + } + po_lits.emplace_back( lit_not_cond( literals[f], ntk.is_complemented( f ) ) ); + po_lits_link.emplace_back( solver.add_variable(), bill::lit_type::polarities::positive ); + }); + + + /* OEC */ + assert( oec_ntk.num_pis() == ntk.num_pos() * 2 && oec_ntk.num_pos() == 1 ); + node_map oe_lits( oec_ntk ); + oe_lits[oec_ntk.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( oec_ntk.get_node( oec_ntk.get_constant( false ) ) != oec_ntk.get_node( oec_ntk.get_constant( true ) ) ) + { + oe_lits[oec_ntk.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + oec_ntk.foreach_pi( [&]( auto const& n, auto i ) { + oe_lits[n] = i < ntk.num_pos() ? po_lits[i] : po_lits_link[i - ntk.num_pos()]; + } ); + + oec_ntk.foreach_gate( [&]( auto const& n ){ + oe_lits[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + }); + + auto out_lits = generate_cnf( oec_ntk, add_clause_fn, oe_lits ); + solver.add_clause( {out_lits[0]} ); + } + } + } + + bill::lit_type construct( node const& n ) + { + assert( !constructed.has( n ) && !ntk.is_pi( n ) && !ntk.is_constant( n ) ); + if constexpr ( use_pushpop ) + { + if ( between_push_pop ) + { + tmp.emplace_back( n ); + } + } + + std::vector child_lits; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( !constructed.has( f ) && !ntk.is_pi( ntk.get_node( f ) ) && !ntk.is_constant( ntk.get_node( f ) ) ) + { + construct( ntk.get_node( f ) ); + } + child_lits.push_back( lit_not_cond( literals[f], ntk.is_complemented( f ) ) ); + } ); + bill::lit_type node_lit = literals[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + constructed[n] = true; + + if ( ntk.is_and( n ) ) + { + detail::on_and( node_lit, child_lits[0], child_lits[1], add_clause_fn ); + } + else if ( ntk.is_xor( n ) ) + { + detail::on_xor( node_lit, child_lits[0], child_lits[1], add_clause_fn ); + } + else if ( ntk.is_xor3( n ) ) + { + detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], add_clause_fn ); + } + else if ( ntk.is_maj( n ) ) + { + detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], add_clause_fn ); + } + else if ( ntk.is_ite( n ) ) + { + detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], add_clause_fn ); + } + return node_lit; + } + + void push() + { + solver.push(); + between_push_pop = true; + tmp.clear(); + } + + void pop() + { + solver.pop(); + for ( auto& n : tmp ) + { + constructed.erase( n ); + } + between_push_pop = false; + } + + bill::lit_type add_clauses_for_2input_gate( bill::lit_type a, bill::lit_type b, std::optional c = std::nullopt, gate_type type = AND ) + { + assert( type == AND || type == XOR ); + + auto nlit = c ? *c : bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + if ( type == AND ) + { + detail::on_and( nlit, a, b, add_clause_fn ); + } + else if ( type == XOR ) + { + detail::on_xor( nlit, a, b, add_clause_fn ); + } + + return nlit; + } + + bill::lit_type add_clauses_for_3input_gate( bill::lit_type a, bill::lit_type b, bill::lit_type c, std::optional d = std::nullopt, gate_type type = MAJ ) + { + assert( type == MAJ || type == XOR || type == MUX ); + + auto nlit = d ? *d : bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + if ( type == MAJ ) + { + detail::on_maj( nlit, a, b, c, add_clause_fn ); + } + else if ( type == XOR ) + { + detail::on_xor3( nlit, a, b, c, add_clause_fn ); + } + else if ( type == MUX ) + { + detail::on_ite( nlit, a, b, c, add_clause_fn ); + } + + return nlit; + } + + std::optional solve( std::vector assumptions ) + { + ++num_invoke; + auto const res = solver.solve( assumptions, ps.conflict_limit ); + + if ( res == bill::result::states::satisfiable ) + { + auto model = solver.get_model().model(); + for ( auto i = 0u; i < ntk.num_pis(); ++i ) + { + cex.at( i ) = model.at( i + 1 ) == bill::lbool_type::true_; + } + + if constexpr ( has_EXCDC_interface_v ) + { + assert( !ntk.pattern_is_EXCDC( cex ) ); + } + return false; + } + else if ( res == bill::result::states::unsatisfiable ) + { + return true; + } + else + { + return std::nullopt; /* timeout or something wrong */ + } + } + + std::optional validate( node const& root, bill::lit_type const& lit ) + { + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + std::optional res; + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + if constexpr ( use_pushpop ) + { + push(); + } + res = solve( { build_odc_window( root, lit ) } ); + if constexpr ( use_pushpop ) + { + pop(); + } + } + else + { + auto nlit = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + solver.add_clause( { literals[root], lit, nlit } ); + solver.add_clause( { ~( literals[root] ), ~lit, nlit } ); + res = solve( { ~nlit } ); + } + } + else + { + auto nlit = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + solver.add_clause( { literals[root], lit, nlit } ); + solver.add_clause( { ~( literals[root] ), ~lit, nlit } ); + res = solve( { ~nlit } ); + } + + return res; + } + + void block_pattern( std::vector const& pattern ) + { + assert( pattern.size() == ntk.num_pis() ); + std::vector clause; + ntk.foreach_pi( [&]( auto const& n, auto i ) { + clause.emplace_back( lit_not_cond( literals[n], pattern[i] ) ); + } ); + solver.add_clause( clause ); + } + +private: + template> + bill::lit_type build_odc_window( node const& root, bill::lit_type const& lit ) + { + /* literals for the duplicated fanout cone */ + unordered_node_map lits( ntk ); + /* miter literals that should be empty */ + std::vector miter; + + lits[root] = lit; + ntk.incr_trav_id(); + make_lit_fanout_cone_rec( root, lits, miter, 1 ); + ntk.incr_trav_id(); + duplicate_fanout_cone_rec( root, lits, 1 ); + + if constexpr ( has_EXODC_interface_v ) + { + if ( ps.odc_levels == -1 ) + { + assert( miter.size() == 0 ); + auto assump = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + auto dup_po_lit = lit_not_cond( lits.has( ntk.get_node( f ) ) ? lits[f] : literals[f], ntk.is_complemented( f ) ); + solver.add_clause( {~assump, ~po_lits_link[i], dup_po_lit} ); + solver.add_clause( {~assump, po_lits_link[i], ~dup_po_lit} ); + } ); + return assump; + } + } + + /* miter for POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( !lits.has( ntk.get_node( f ) ) ) + return true; /* PO not in TFO, skip */ + add_miter_clauses( ntk.get_node( f ), lits, miter ); + return true; /* next */ + } ); + + assert( miter.size() > 0 && "no fanout node at distance odc_levels and there is no PO in TFO cone (possibly due to a dangling cone)" ); + auto assump = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + miter.emplace_back( ~assump ); + solver.add_clause( miter ); + return assump; + } + + template> + void duplicate_fanout_cone_rec( node const& n, unordered_node_map const& lits, int32_t level ) + { + ntk.foreach_fanout( n, [&]( auto const& fo ) { + if ( ntk.visited( fo ) == ntk.trav_id() ) + return true; /* skip */ + ntk.set_visited( fo, ntk.trav_id() ); + + std::vector l_fi; + ntk.foreach_fanin( fo, [&]( auto const& fi ) { + if ( !constructed.has( fi ) && !ntk.is_pi( ntk.get_node( fi ) ) && !ntk.is_constant( ntk.get_node( fi ) ) ) + { + construct( ntk.get_node( fi ) ); + } + l_fi.emplace_back( lit_not_cond( lits.has( ntk.get_node( fi ) ) ? lits[fi] : literals[fi], ntk.is_complemented( fi ) ) ); + } ); + if ( l_fi.size() == 2u ) + { + assert( ntk.is_and( fo ) || ntk.is_xor( fo ) ); + add_clauses_for_2input_gate( l_fi[0], l_fi[1], lits[fo], ntk.is_and( fo ) ? AND : XOR ); + } + else + { + assert( l_fi.size() == 3u ); + assert( ntk.is_maj( fo ) || ntk.is_xor3( fo ) ); + add_clauses_for_3input_gate( l_fi[0], l_fi[1], l_fi[2], lits[fo], ntk.is_maj( fo ) ? MAJ : XOR ); + } + + if ( level == ps.odc_levels ) + return true; + + duplicate_fanout_cone_rec( fo, lits, level + 1 ); + return true; /* next */ + } ); + } + + template> + void make_lit_fanout_cone_rec( node const& n, unordered_node_map& lits, std::vector& miter, int32_t level ) + { + ntk.foreach_fanout( n, [&]( auto const& fo ) { + if ( ntk.visited( fo ) == ntk.trav_id() ) + return true; /* skip */ + ntk.set_visited( fo, ntk.trav_id() ); + + if ( !constructed.has( fo ) ) + { + construct( fo ); + } + + lits[fo] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + + if ( level == ps.odc_levels ) + { + add_miter_clauses( fo, lits, miter ); + return true; + } + + make_lit_fanout_cone_rec( fo, lits, miter, level + 1 ); + return true; /* next */ + } ); + } + + template> + void add_miter_clauses( node const& n, unordered_node_map const& lits, std::vector& miter ) + { + assert( constructed.has( n ) && literals[n] != literals[ntk.get_constant( false )] ); + miter.emplace_back( add_clauses_for_2input_gate( literals[n], lits[n], std::nullopt, XOR ) ); + } + +private: + Ntk const& ntk; + + validator_params ps; + + node_map literals; + unordered_node_map constructed; + bill::solver solver; + add_clause_fn_t add_clause_fn = [&]( auto const& clause ) { solver.add_clause( clause ); }; + + static const uint32_t MIN_NUM_INVOKE = 20u; + uint32_t num_invoke; + + bool between_push_pop = false; + std::vector tmp; + + std::shared_ptr::add_event_type> add_event; + + std::vector po_lits_link; + +public: + std::vector cex; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cleanup.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cleanup.hpp new file mode 100644 index 00000000000..766d67764fd --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cleanup.hpp @@ -0,0 +1,657 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cleanup.hpp + \brief Cleans up networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/crossed.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +#include + +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +void cleanup_dangling_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, node_map, NtkSrc>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto child, auto ) { + const auto f = old_to_new[child]; + if ( ntk.is_complemented( child ) ) + { + children.push_back( dest.create_not( f ) ); + } + else + { + children.push_back( f ); + } + } ); + + /* clone node */ + if constexpr ( std::is_same_v ) + { + old_to_new[node] = dest.clone_node( ntk, node, children ); + } + else + { + do + { + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v, "NtkDest cannot create AND gates" ); + if ( ntk.is_and( node ) ) + { + old_to_new[node] = dest.create_and( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_or_v ) + { + static_assert( has_create_or_v, "NtkDest cannot create OR gates" ); + if ( ntk.is_or( node ) ) + { + old_to_new[node] = dest.create_or( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v, "NtkDest cannot create XOR gates" ); + if ( ntk.is_xor( node ) ) + { + old_to_new[node] = dest.create_xor( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v, "NtkDest cannot create MAJ gates" ); + if ( ntk.is_maj( node ) ) + { + old_to_new[node] = dest.create_maj( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_ite_v ) + { + static_assert( has_create_ite_v, "NtkDest cannot create ITE gates" ); + if ( ntk.is_ite( node ) ) + { + old_to_new[node] = dest.create_ite( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v, "NtkDest cannot create XOR3 gates" ); + if ( ntk.is_xor3( node ) ) + { + old_to_new[node] = dest.create_xor3( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_nary_and_v ) + { + static_assert( has_create_nary_and_v, "NtkDest cannot create n-ary AND gates" ); + if ( ntk.is_nary_and( node ) ) + { + old_to_new[node] = dest.create_nary_and( children ); + break; + } + } + if constexpr ( has_is_nary_or_v ) + { + static_assert( has_create_nary_or_v, "NtkDest cannot create n-ary OR gates" ); + if ( ntk.is_nary_or( node ) ) + { + old_to_new[node] = dest.create_nary_or( children ); + break; + } + } + if constexpr ( has_is_nary_xor_v ) + { + static_assert( has_create_nary_xor_v, "NtkDest cannot create n-ary XOR gates" ); + if ( ntk.is_nary_xor( node ) ) + { + old_to_new[node] = dest.create_nary_xor( children ); + break; + } + } + if constexpr ( has_is_not_v ) + { + static_assert( has_create_not_v, "NtkDest cannot create NOT gates" ); + if ( ntk.is_not( node ) ) + { + old_to_new[node] = dest.create_not( children[0] ); + break; + } + } + if constexpr ( has_is_buf_v ) + { + static_assert( has_create_buf_v, "NtkDest cannot create buffers" ); + if ( ntk.is_buf( node ) ) + { + old_to_new[node] = dest.create_buf( children[0] ); + break; + } + } + if constexpr ( has_is_function_v ) + { + static_assert( has_create_node_v, "NtkDest cannot create arbitrary function gates" ); + old_to_new[node] = dest.create_node( children, ntk.node_function( node ) ); + break; + } + std::cerr << "[e] something went wrong, could not copy node " << ntk.node_to_index( node ) << "\n"; + } while ( false ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( node ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[node], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[node], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void cleanup_dangling_with_crossings_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, node_map, NtkSrc>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto const& f ) { + if ( ntk.is_crossing( ntk.get_node( f ) ) ) + children.push_back( ntk.is_second( f ) ? dest.make_second( old_to_new[f] ) : old_to_new[f] ); + else + children.push_back( old_to_new[f] ); + } ); + + /* clone node */ + if ( ntk.is_crossing( node ) ) + { + assert( children.size() == 2 ); + old_to_new[node] = dest.create_crossing( children[0], children[1] ).first; + } + else + { + old_to_new[node] = dest.clone_node( ntk, node, children ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( node ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[node], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[node], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void cleanup_luts_impl( Ntk const& ntk, Ntk& dest, LeavesIterator begin, LeavesIterator end, node_map, Ntk>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* iterate through nodes */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + return; /* continue */ + + auto func = ntk.node_function( n ); + + /* constant propagation */ + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( dest.is_constant( old_to_new[f] ) ) + { + if ( dest.constant_value( old_to_new[f] ) != ntk.is_complemented( f ) ) + { + kitty::cofactor1_inplace( func, i ); + } + else + { + kitty::cofactor0_inplace( func, i ); + } + } + } ); + + const auto support = kitty::min_base_inplace( func ); + auto new_func = kitty::shrink_to( func, static_cast( support.size() ) ); + + std::vector> children; + if ( auto var = support.begin(); var != support.end() ) + { + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( *var == i ) + { + auto const& new_f = old_to_new[f]; + children.push_back( ntk.is_complemented( f ) ? dest.create_not( new_f ) : new_f ); + if ( ++var == support.end() ) + { + return false; + } + } + return true; + } ); + } + + if ( new_func.num_vars() == 0u ) + { + old_to_new[n] = dest.get_constant( !kitty::is_const0( new_func ) ); + } + else if ( new_func.num_vars() == 1u ) + { + old_to_new[n] = *( new_func.begin() ) == 0b10 ? children.front() : dest.create_not( children.front() ); + } + else + { + old_to_new[n] = dest.create_node( children, new_func ); + } + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[n], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[n], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void clone_inputs( NtkSrc const& ntk, NtkDest& dest, std::vector>& cis, bool remove_dangling_PIs = false ) +{ + /* network name */ + if constexpr ( has_get_network_name_v && has_set_network_name_v ) + { + dest.set_network_name( ntk.get_network_name() ); + } + + /* PIs & PI names */ + ntk.foreach_pi( [&]( auto n ) { + if ( remove_dangling_PIs && ntk.fanout_size( n ) == 0 ) + { + cis.push_back( dest.get_constant( false ) ); + } + else + { + cis.push_back( dest.create_pi() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + } + } ); + + /* ROs & RO names & register information */ + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + ntk.foreach_ro( [&]( auto const& n, auto i ) { + cis.push_back( dest.create_ro() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + dest.set_register( i, ntk.register_at( i ) ); + } ); + } +} + +template +void clone_outputs( NtkSrc const& ntk, NtkDest& dest, node_map, NtkSrc> const& old_to_new, bool remove_redundant_POs = false ) +{ + /* POs */ + ntk.foreach_po( [&]( auto const& po ) { + auto const f = old_to_new[po]; + auto const n = dest.get_node( f ); + if ( remove_redundant_POs && ( dest.is_pi( n ) || dest.is_constant( n ) ) ) + { + return; + } + dest.create_po( ntk.is_complemented( po ) ? dest.create_not( f ) : f ); + } ); + + /* RIs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + dest.create_ri( ntk.is_complemented( f ) ? dest.create_not( old_to_new[f] ) : old_to_new[f] ); + } ); + } + + /* CO names */ + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + ntk.foreach_co( [&]( auto co, auto index ) { + (void)co; + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } ); + } +} + +} // namespace detail + +template +std::vector> cleanup_dangling( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "NtkSrc does not implement the is_complemented method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + + node_map, NtkSrc> old_to_new( ntk ); + detail::cleanup_dangling_impl( ntk, dest, begin, end, old_to_new ); + std::vector> fs; + + /* create outputs in the same order */ + ntk.foreach_po( [&]( auto po ) { + const auto f = old_to_new[po]; + fs.push_back( ntk.is_complemented( po ) ? dest.create_not( f ) : f ); + } ); + if constexpr ( has_foreach_ri_v ) + { + ntk.foreach_ri( [&]( auto ri ) { + const auto f = old_to_new[ri]; + fs.push_back( ntk.is_complemented( ri ) ? dest.create_not( f ) : f ); + } ); + } + + return fs; +} + +/*! \brief Cleans up dangling nodes. + * + * This method reconstructs a network and omits all dangling nodes. If the flag + * `remove_dangling_PIs` is true, dangling PIs are also omitted. If the flag + * `remove_redundant_POs` is true, redundant POs, i.e. POs connected to a PI or + * constant, are also omitted. The network types of the source and destination + * network are the same. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + */ +template +[[nodiscard]] NtkDest cleanup_dangling( NtkSrc const& ntk, bool remove_dangling_PIs = false, bool remove_redundant_POs = false ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_node_to_index_v, "NtkSrc does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_is_complemented_v, "NtkDest does not implement the is_complemented method" ); + + NtkDest dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis, remove_dangling_PIs ); + + node_map, NtkSrc> old_to_new( ntk ); + if constexpr ( is_crossed_network_type_v ) + { + detail::cleanup_dangling_with_crossings_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + } + else + { + detail::cleanup_dangling_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + } + + detail::clone_outputs( ntk, dest, old_to_new, remove_redundant_POs ); + + return dest; +} + +/*! \brief Cleans up LUT nodes. + * + * This method reconstructs a LUT network and optimizes LUTs when they do not + * depend on all their fanin, or when some of the fanin are constant inputs. + * + * Constant gate inputs will be propagated. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `get_constant` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_node` + * - `foreach_fanin` + * - `create_pi` + * - `create_po` + * - `create_node` + * - `create_not` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `node_function` + */ +template +[[nodiscard]] Ntk cleanup_luts( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_node_v, "Ntk does not implement the create_node method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + Ntk dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis ); + + node_map, Ntk> old_to_new( ntk ); + detail::cleanup_luts_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + + detail::clone_outputs( ntk, dest, old_to_new ); + + return dest; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cnf.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cnf.hpp new file mode 100644 index 00000000000..4a825d63372 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cnf.hpp @@ -0,0 +1,507 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cnf.hpp + \brief CNF generation methods + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" + +namespace mockturtle +{ + +inline constexpr uint32_t make_lit( uint32_t var, bool is_complemented = false ) +{ + return ( var << 1 ) | ( is_complemented ? 1 : 0 ); +} + +inline constexpr uint32_t lit_not( uint32_t lit ) +{ + return lit ^ 0x1; +} + +inline bill::lit_type lit_not( bill::lit_type lit ) +{ + return ~lit; +} + +inline constexpr uint32_t lit_not_cond( uint32_t lit, bool cond ) +{ + return cond ? lit ^ 0x1 : lit; +} + +inline bill::lit_type lit_not_cond( bill::lit_type lit, bool cond ) +{ + return cond ? ~lit : lit; +} + +namespace detail +{ + +/* c = a & b */ +template +inline void on_and( uint32_t c, uint32_t a, uint32_t b, ClauseFn const& fn ) +{ + fn( { a, lit_not( c ) } ); + fn( { b, lit_not( c ) } ); + fn( { lit_not( a ), lit_not( b ), c } ); +} + +/* c = a & b */ +template +inline void on_and( bill::lit_type c, bill::lit_type a, bill::lit_type b, ClauseFn const& fn ) +{ + fn( { a, ~c } ); + fn( { b, ~c } ); + fn( { ~a, ~b, c } ); +} + +/* c = a | b */ +template +inline void on_or( uint32_t c, uint32_t a, uint32_t b, ClauseFn const& fn ) +{ + fn( { lit_not( a ), c } ); + fn( { lit_not( b ), c } ); + fn( { a, b, lit_not( c ) } ); +} + +/* c = a | b */ +template +inline void on_or( bill::lit_type c, bill::lit_type a, bill::lit_type b, ClauseFn const& fn ) +{ + fn( { ~a, c } ); + fn( { ~b, c } ); + fn( { a, b, ~c } ); +} + +/* c = a ^ b */ +template +inline void on_xor( uint32_t c, uint32_t a, uint32_t b, ClauseFn const& fn ) +{ + fn( { lit_not( a ), lit_not( b ), lit_not( c ) } ); + fn( { lit_not( a ), b, c } ); + fn( { a, lit_not( b ), c } ); + fn( { a, b, lit_not( c ) } ); +} + +/* c = a ^ b */ +template +inline void on_xor( bill::lit_type c, bill::lit_type a, bill::lit_type b, ClauseFn const& fn ) +{ + fn( { ~a, ~b, ~c } ); + fn( { ~a, b, c } ); + fn( { a, ~b, c } ); + fn( { a, b, ~c } ); +} + +/* d = */ +template +inline void on_maj( uint32_t d, uint32_t a, uint32_t b, uint32_t c, ClauseFn const& fn ) +{ + fn( { lit_not( a ), lit_not( b ), d } ); + fn( { lit_not( a ), lit_not( c ), d } ); + fn( { lit_not( b ), lit_not( c ), d } ); + fn( { a, b, lit_not( d ) } ); + fn( { a, c, lit_not( d ) } ); + fn( { b, c, lit_not( d ) } ); +} + +/* d = */ +template +inline void on_maj( bill::lit_type d, bill::lit_type a, bill::lit_type b, bill::lit_type c, ClauseFn const& fn ) +{ + fn( { ~a, ~b, d } ); + fn( { ~a, ~c, d } ); + fn( { ~b, ~c, d } ); + fn( { a, b, ~d } ); + fn( { a, c, ~d } ); + fn( { b, c, ~d } ); +} + +/* d = a ^ b ^ c */ +template +inline void on_xor3( uint32_t d, uint32_t a, uint32_t b, uint32_t c, ClauseFn const& fn ) +{ + fn( { lit_not( a ), b, c, d } ); + fn( { a, lit_not( b ), c, d } ); + fn( { a, b, lit_not( c ), d } ); + fn( { a, b, c, lit_not( d ) } ); + fn( { a, lit_not( b ), lit_not( c ), lit_not( d ) } ); + fn( { lit_not( a ), b, lit_not( c ), lit_not( d ) } ); + fn( { lit_not( a ), lit_not( b ), c, lit_not( d ) } ); + fn( { lit_not( a ), lit_not( b ), lit_not( c ), d } ); +} + +/* d = a ^ b ^ c */ +template +inline void on_xor3( bill::lit_type d, bill::lit_type a, bill::lit_type b, bill::lit_type c, ClauseFn const& fn ) +{ + fn( { ~a, b, c, d } ); + fn( { a, ~b, c, d } ); + fn( { a, b, ~c, d } ); + fn( { a, b, c, ~d } ); + fn( { a, ~b, ~c, ~d } ); + fn( { ~a, b, ~c, ~d } ); + fn( { ~a, ~b, c, ~d } ); + fn( { ~a, ~b, ~c, d } ); +} + +/* d = a ? b : c */ +template +inline void on_ite( uint32_t d, uint32_t a, uint32_t b, uint32_t c, ClauseFn const& fn ) +{ + fn( { lit_not( a ), lit_not( b ), d } ); + fn( { lit_not( a ), b, lit_not( d ) } ); + fn( { a, lit_not( c ), d } ); + fn( { a, c, lit_not( d ) } ); +} + +/* d = a ? b : c */ +template +inline void on_ite( bill::lit_type d, bill::lit_type a, bill::lit_type b, bill::lit_type c, ClauseFn const& fn ) +{ + fn( { ~a, ~b, d } ); + fn( { ~a, b, ~d } ); + fn( { a, ~c, d } ); + fn( { a, c, ~d } ); +} + +/* general case */ +template +inline void on_function( uint32_t f, std::vector const& child_lits, kitty::dynamic_truth_table const& function, ClauseFn const& fn ) +{ + const auto cnf = kitty::cnf_characteristic( function ); + + auto lits = child_lits; + lits.push_back( f ); + for ( auto const& cube : cnf ) + { + std::vector clause; + for ( auto i = 0u; i < lits.size(); ++i ) + { + if ( cube.get_mask( i ) ) + { + clause.push_back( lit_not_cond( lits[i], !cube.get_bit( i ) ) ); + } + } + fn( clause ); + } +} + +/* general case */ +template +inline void on_function( bill::lit_type f, std::vector const& child_lits, kitty::dynamic_truth_table const& function, ClauseFn const& fn ) +{ + const auto cnf = kitty::cnf_characteristic( function ); + + auto lits = child_lits; + lits.push_back( f ); + for ( auto const& cube : cnf ) + { + bill::result::clause_type clause; + for ( auto i = 0u; i < lits.size(); ++i ) + { + if ( cube.get_mask( i ) ) + { + clause.push_back( cube.get_bit( i ) ? lits[i] : ~lits[i] ); + } + } + fn( clause ); + } +} + +} // namespace detail + +/*! \brief Clause callback function for generate_cnf. */ +template +using clause_callback_t = std::function const& )>; + +/*! \brief Create a default node literal map. + * + * In the default map, constants are mapped to variable `0` (literal `1` for + * constant-1 and literal `0` for constant-0). Then each primary input is + * mapped to variables `1, ..., n`. Then the next variables are assigned to + * each gate in order, unless `gate_offset` is overridden which will be used for + * the next variable id. Therefore, this function can be used to create two + * independent sets of node literals for two networks, but keep the same indexes + * for the primary inputs. + */ +template +node_map node_literals( Ntk const& ntk, std::optional const& gate_offset = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + node_map node_lits( ntk ); + + if constexpr ( std::is_same::value ) + { + /* constants are mapped to var 0 */ + node_lits[ntk.get_constant( false )] = make_lit( 0 ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_lits[ntk.get_constant( true )] = make_lit( 0, true ); + } + + /* first indexes (starting from 1) are for PIs */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_lits[n] = make_lit( i + 1 ); + } ); + + /* compute literals for nodes */ + uint32_t next_var = gate_offset ? *gate_offset : ntk.num_pis() + 1; + ntk.foreach_gate( [&]( auto const& n ) { + node_lits[n] = make_lit( next_var++ ); + } ); + } + else if constexpr ( std::is_same::value ) + { + /* constants are mapped to var 0 */ + node_lits[ntk.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_lits[ntk.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + + /* first indexes (starting from 1) are for PIs */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_lits[n] = bill::lit_type( i + 1, bill::lit_type::polarities::positive ); + } ); + + /* compute literals for nodes */ + uint32_t next_var = gate_offset ? *gate_offset : ntk.num_pis() + 1; + ntk.foreach_gate( [&]( auto const& n ) { + node_lits[n] = bill::lit_type( next_var++, bill::lit_type::polarities::positive ); + } ); + } + + return node_lits; +} + +namespace detail +{ + +template +class generate_cnf_impl +{ +public: + generate_cnf_impl( Ntk const& ntk, clause_callback_t const& fn, std::optional> const& node_lits ) + : ntk_( ntk ), + fn_( fn ), + node_lits_( node_lits ? *node_lits : node_literals( ntk ) ) + { + } + + std::vector run() + { + /* unit clause for constant-0 */ + fn_( { lit_not( node_lits_[ntk_.get_constant( false )] ) } ); + + /* compute clauses for nodes */ + ntk_.foreach_gate( [&]( auto const& n ) { + std::vector child_lits; + ntk_.foreach_fanin( n, [&]( auto const& f ) { + child_lits.push_back( lit_not_cond( node_lits_[f], ntk_.is_complemented( f ) ) ); + } ); + lit_t node_lit = node_lits_[n]; + + if constexpr ( has_is_and_v ) + { + if ( ntk_.is_and( n ) ) + { + detail::on_and( node_lit, child_lits[0], child_lits[1], fn_ ); + return true; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk_.is_or( n ) ) + { + detail::on_or( node_lit, child_lits[0], child_lits[1], fn_ ); + return true; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk_.is_xor( n ) ) + { + detail::on_xor( node_lit, child_lits[0], child_lits[1], fn_ ); + return true; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk_.is_maj( n ) ) + { + detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], fn_ ); + return true; + } + } + + if constexpr ( has_is_ite_v ) + { + if ( ntk_.is_ite( n ) ) + { + detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], fn_ ); + return true; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk_.is_xor3( n ) ) + { + detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], fn_ ); + return true; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk_.is_nary_and( n ) ) + { + fmt::print( "[e] nary-AND not yet supported in generate_cnf" ); + std::abort(); + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk_.is_nary_or( n ) ) + { + fmt::print( "[e] nary-OR not yet supported in generate_cnf" ); + std::abort(); + } + } + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk_.is_nary_xor( n ) ) + { + fmt::print( "[e] nary-XOR not yet supported in generate_cnf" ); + std::abort(); + } + } + + /* general case */ + detail::on_function( node_lit, child_lits, ntk_.node_function( n ), fn_ ); + return true; + } ); + + std::vector output_lits; + ntk_.foreach_po( [&]( auto const& f ) { + output_lits.push_back( lit_not_cond( node_lits_[f], ntk_.is_complemented( f ) ) ); + } ); + + return output_lits; + } + +private: + Ntk const& ntk_; + clause_callback_t const& fn_; + + node_map node_lits_; +}; + +} // namespace detail + +/*! \brief Generates CNF for a logic network. + * + * This function generates a CNF for a logic network using the Tseytin encoding + * for regular gates and ISOP-based CNF generation for arbitrary node functions. + * + * Input to the function are the network `ntk` and a clause callback function + * `fn`. For each clause that is generated, `fn` is called. A clause is + * represented as a vector of literals `std::vector`, following the + * customary literal convention, i.e., for a variable `v` its positive literal + * is `2 * v` and its negative literal is `2 * v + 1`. The third optional + * parameter can be used to pass an alternative mapping of nodes to literals. + * If none is given, it uses the default literal map created with the + * `node_literals` function. + * + * The return value of the function is a vector with a literal for each primary + * output in the network, following the same order as the primary outputs have + * been created. + * + * \param ntk Logic network + * \param fn Clause creation function + * \param node_lits (optional) custom node literal map + */ +template +std::vector generate_cnf( Ntk const& ntk, clause_callback_t const& fn, std::optional> const& node_lits = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + + detail::generate_cnf_impl impl( ntk, fn, node_lits ); + return impl.run(); +} + +template +std::vector generate_cnf( Ntk const& ntk, clause_callback_t const& fn, std::optional> const& node_lits = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + + detail::generate_cnf_impl impl( ntk, fn, node_lits ); + return impl.run(); +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/collapse_mapped.hpp b/third-party/mockturtle/include/mockturtle/algorithms/collapse_mapped.hpp new file mode 100644 index 00000000000..7107bdda4da --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/collapse_mapped.hpp @@ -0,0 +1,463 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file collapse_mapped.hpp + \brief Collapses mapped network into k-LUT network + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/window_utils.hpp" +#include "../views/color_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "simulation.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class collapse_mapped_network_impl +{ +public: + collapse_mapped_network_impl( NtkSource const& ntk ) + : ntk( ntk ) + { + } + + void run( NtkDest& dest ) + { + node_map, NtkSource> node_to_signal( ntk ); + + /* special map for output drivers to perform some optimizations */ + enum class driver_type + { + none, + pos, + neg, + mixed + }; + node_map node_driver_type( ntk, driver_type::none ); + + /* opposites are filled for nodes with mixed driver types, since they have + two nodes in the network. */ + std::unordered_map, signal> opposites; + + /* initial driver types */ + ntk.foreach_po( [&]( auto const& f ) { + switch ( node_driver_type[f] ) + { + case driver_type::none: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::pos; + break; + case driver_type::pos: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::mixed : driver_type::pos; + break; + case driver_type::neg: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::mixed; + break; + case driver_type::mixed: + default: + break; + } + } ); + + /* it could be that internal nodes also point to an output driver node */ + ntk.foreach_node( [&]( auto const n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) || !ntk.is_cell_root( n ) ) + return; + + ntk.foreach_cell_fanin( n, [&]( auto fanin ) { + if ( node_driver_type[fanin] == driver_type::neg ) + { + node_driver_type[fanin] = driver_type::mixed; + } + } ); + } ); + + /* constants */ + auto add_constant_to_map = [&]( bool value ) { + const auto n = ntk.get_node( ntk.get_constant( value ) ); + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = dest.get_constant( value ); + break; + + case driver_type::neg: + node_to_signal[n] = dest.get_constant( !value ); + break; + + case driver_type::mixed: + node_to_signal[n] = dest.get_constant( value ); + opposites[n] = dest.get_constant( !value ); + break; + } + }; + + add_constant_to_map( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + add_constant_to_map( true ); + } + + /* primary inputs */ + ntk.foreach_pi( [&]( auto n ) { + signal dest_signal; + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + dest_signal = dest.create_pi(); + node_to_signal[n] = dest_signal; + break; + + case driver_type::neg: + dest_signal = dest.create_pi(); + node_to_signal[n] = dest.create_not( dest_signal ); + break; + + case driver_type::mixed: + dest_signal = dest.create_pi(); + node_to_signal[n] = dest_signal; + opposites[n] = dest.create_not( node_to_signal[n] ); + break; + } + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + dest.set_name( dest_signal, ntk.get_name( ntk.make_signal( n ) ) ); + } + } ); + + /* ROs & RO names & register information */ + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + std::vector> cis; + ntk.foreach_ro( [&]( auto const& n, auto i ) { + cis.push_back( dest.create_ro() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + dest.set_register( i, ntk.register_at( i ) ); + } ); + + auto it = std::begin( cis ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto n ) { + node_to_signal[n] = *it++; + } ); + } + } + + fanout_view fanout_ntk{ ntk }; + fanout_ntk.clear_visited(); + color_view> color_ntk{ fanout_ntk }; + + /* nodes */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) || !ntk.is_cell_root( n ) ) + return; + + std::vector> children; + ntk.foreach_cell_fanin( n, [&]( auto fanin ) { + children.push_back( node_to_signal[fanin] ); + } ); + + kitty::dynamic_truth_table tt; + + if constexpr ( has_cell_function_v ) + { + tt = ntk.cell_function( n ); + } + else + { + /* compute function constructing a window */ + std::vector> roots{ n }; + std::vector> leaves; + + ntk.foreach_cell_fanin( n, [&]( auto fanin ) { + leaves.push_back( fanin ); + } ); + + std::vector> gates{ collect_nodes( color_ntk, leaves, roots ) }; + window_view window_ntk{ color_ntk, leaves, roots, gates }; + + using Ntk = mockturtle::window_view>>; + default_simulator sim( window_ntk.num_pis() ); + unordered_node_map node_to_value( window_ntk ); + + simulate_nodes( window_ntk, node_to_value, sim ); + + tt = node_to_value[n]; + } + + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = dest.create_node( children, tt ); + break; + + case driver_type::neg: + node_to_signal[n] = dest.create_node( children, ~tt ); + break; + + case driver_type::mixed: + node_to_signal[n] = dest.create_node( children, tt ); + opposites[n] = dest.create_node( children, ~tt ); + break; + } + } ); + + /* outputs */ + ntk.foreach_po( [&]( auto const& f, auto index ) { + (void)index; + + if ( ntk.is_complemented( f ) && node_driver_type[f] == driver_type::mixed ) + { + dest.create_po( opposites[ntk.get_node( f )] ); + } + else + { + dest.create_po( node_to_signal[f] ); + } + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } + } ); + + /* RIs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + dest.create_ri( ntk.is_complemented( f ) ? dest.create_not( node_to_signal[f] ) : node_to_signal[f] ); + } ); + } + + /* CO names */ + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + ntk.foreach_co( [&]( auto co, auto index ) { + (void)co; + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } ); + } + } + +private: + NtkSource const& ntk; +}; + +} /* namespace detail */ + +/*! \brief Collapse mapped network into k-LUT network. + * + * Collapses a mapped network into a k-LUT network. In the mapped network each + * cell is represented in terms of a collection of nodes from the subject graph. + * This method creates a new network in which each cell is represented by a + * single node. + * + * This function performs some optimizations with respect to possible output + * complementations in the subject graph: + * + * - If an output driver is only used in positive form, nothing changes + * - If an output driver is only used in complemented form, the cell function + * of the node is negated. + * - If an output driver is used in both forms, two nodes will be created for + * the mapped node. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `has_mapping` + * - `get_constant` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_node` + * - `foreach_cell_fanin` + * - `is_constant` + * - `is_pi` + * - `is_cell_root` + * - `cell_function` + * - `is_complemented` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_node` + * - `create_not` + */ +template +std::optional collapse_mapped_network( NtkSource const& ntk ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_has_mapping_v, "NtkSource does not implement the has_mapping method" ); + static_assert( has_num_gates_v, "NtkSource does not implement the num_gates method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_foreach_cell_fanin_v, "NtkSource does not implement the foreach_cell_fanin method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_cell_root_v, "NtkSource does not implement the is_cell_root method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + if ( !ntk.has_mapping() && ntk.num_gates() > 0 ) + { + return std::nullopt; + } + else + { + detail::collapse_mapped_network_impl p( ntk ); + NtkDest dest; + p.run( dest ); + return dest; + } +} + +/*! \brief Collapse mapped network into k-LUT network. + * + * Collapses a mapped network into a k-LUT network. In the mapped network each + * cell is represented in terms of a collection of nodes from the subject graph. + * This method creates a new network in which each cell is represented by a + * single node. + * + * This function performs some optimizations with respect to possible output + * complementations in the subject graph: + * + * - If an output driver is only used in positive form, nothing changes + * - If an output driver is only used in complemented form, the cell function + * of the node is negated. + * - If an output driver is used in both forms, two nodes will be created for + * the mapped node. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `has_mapping` + * - `get_constant` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_node` + * - `foreach_cell_fanin` + * - `is_constant` + * - `is_pi` + * - `is_cell_root` + * - `cell_function` + * - `is_complemented` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_node` + * - `create_not` + */ +template +bool collapse_mapped_network( NtkDest& dest, NtkSource const& ntk ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_has_mapping_v, "NtkSource does not implement the has_mapping method" ); + static_assert( has_num_gates_v, "NtkSource does not implement the num_gates method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_foreach_cell_fanin_v, "NtkSource does not implement the foreach_cell_fanin method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_cell_root_v, "NtkSource does not implement the is_cell_root method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + if ( !ntk.has_mapping() && ntk.num_gates() > 0 ) + { + return false; + } + else + { + detail::collapse_mapped_network_impl p( ntk ); + p.run( dest ); + return true; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cover_to_graph.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cover_to_graph.hpp new file mode 100644 index 00000000000..63864cd295c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cover_to_graph.hpp @@ -0,0 +1,306 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cover_to_graph.hpp + \brief transforms a cover data structure into another network type + + \author Andrea Costamagna + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/cover.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +struct signals_connector +{ + signals_connector() + { + signals.reserve( 10000u ); + } + void insert( signal signal_ntk, uint64_t node_index ) + { + signals[node_index] = signal_ntk; + } + + std::unordered_map> signals; +}; + +/*! \brief cover_to_graph_converter + * This data type is equipped with the main operations involved in the cover to graph conversion. + * Given a cover network its features are mapped into the corresponding ones of a graph using the + * signals_connector for storing the signal of the new network associated to the node index in the cover one. + * The mapping is performed by the convert method, which must be called explicitly to perform the mapping. + */ +template +class cover_to_graph_converter +{ + + using type_cover_signals = std::vector; + +public: + cover_to_graph_converter( Ntk& ntk, const cover_network& cover_ntk ) + : _ntk( ntk ), + _cover_ntk( cover_ntk ) + { + } + +#pragma region recursive functions + /*! \brief recursive_or + * If only one signal is presented the function returns the signal itself. + * If two signals are presented the function returns their disjunction. + * In all other cases recursively split the input signals into two subsets of size differing by at most one. + * These two will give rise to a unique output, which is the OR of two signals coming from the two subgraphs. + * The problem of finding the network of each subgraph presents the same structure as the original problem. + * Therefore, recursion can be performed. + */ + signal recursive_or( const std::vector>& signals ) + { + if ( signals.size() == 0u ) + { + std::cerr << "signals size is zero in recursive or\n"; + return _ntk.get_constant( 0 ); + } + else if ( signals.size() == 1u ) + { + return signals[0]; + } + else if ( signals.size() == 2u ) + { + signal signal_out = _ntk.create_or( signals[0], signals[1] ); + return signal_out; + } + else + { + std::size_t const half_size = signals.size() / 2; + std::vector> vector_l( signals.begin(), signals.begin() + half_size ); + std::vector> vector_r( signals.begin() + half_size, signals.end() ); + + return _ntk.create_or( recursive_or( vector_l ), recursive_or( vector_r ) ); + } + } + + /*! \brief recursive_and + * If only one signal is presented the function returns the signal itself. + * If two signals are presented the function returns their conjunction. + * In all other cases recursively split the input signals into two subsets of size differing by at most one. + * These two will give rise to a unique output, which is the AND of two signals coming from the two subgraphs. + * The problem of finding the network of each subgraph presents the same structure as the original problem. + * Therefore, recursion can be performed. + */ + signal recursive_and( std::vector> const& signals ) + { + if ( signals.size() == 0u ) + { + std::cerr << "signals size is zero in recursive and\n"; + return _ntk.get_constant( 0 ); + } + else if ( signals.size() == 1u ) + { + return signals[0]; + } + else if ( signals.size() == 2u ) + { + signal signal_out = _ntk.create_and( signals[0], signals[1] ); + return signal_out; + } + else + { + std::size_t const half_size = signals.size() / 2; + std::vector> vector_l( signals.begin(), signals.begin() + half_size ); + std::vector> vector_r( signals.begin() + half_size, signals.end() ); + return _ntk.create_and( recursive_and( vector_l ), recursive_and( vector_r ) ); + } + } +#pragma endregion + + /*! \brief convert_cube_to_graph + * Given a node, a cube stored into it and the boolean type associated to the SOP/POS, all children are scanned. + * Unless the cube is independent of the value of the children ( don't care ), the signal is stored in a vector. + * Finally, depending on the bit value of the cube, the signal influencing the cover or their negation are used + * to create the subgraph. This create the products/sums in the SOP/POS. + */ +#pragma region converter functions + signal convert_cube_to_graph( const mockturtle::cover_storage_node& Nde, const kitty::cube& cb, const bool& is_sop ) + { + std::vector> signals; + + for ( auto j = 0u; j < Nde.children.size(); j++ ) + { + if ( cb.get_mask( j ) == 1 ) + { + if ( cb.get_bit( j ) == 1 ) + { + signals.emplace_back( ( is_sop ) ? _connector.signals[Nde.children[j].index] : !_connector.signals[Nde.children[j].index] ); + } + else + { + signals.emplace_back( ( is_sop ) ? !_connector.signals[Nde.children[j].index] : _connector.signals[Nde.children[j].index] ); + } + } + } + + return is_sop ? recursive_and( signals ) : recursive_or( signals ); + } + + /*! \brief convert_node_to_graph + * This helper function receives as input a node, storing the cover information. + * This information corresponds to a vector of cubes and to a boolean determining whether + * the cubes represent the ON set or the OFF set. + * Each cube is mapped into a subgraph and the output signals are collected in a vector, corresponding to the + * products/sums of the SOP/POS. + * Depending on the boolean, the SOP/POS is finally performed using the recursive OR/AND. + */ + signal convert_node_to_graph( const mockturtle::cover_storage_node& Nde ) + { + auto& cbs = _cover_ntk._storage->data.covers[Nde.data[1].h1].first; + + std::vector> signals_internal; + bool is_sop = _cover_ntk._storage->data.covers[Nde.data[1].h1].second; + + for ( auto const& cb : cbs ) + { + signals_internal.emplace_back( convert_cube_to_graph( Nde, cb, is_sop ) ); + } + + return ( is_sop ? recursive_or( signals_internal ) : recursive_and( signals_internal ) ); + } + + Ntk get_network() + { + return _ntk; + } + + /*! \brief convert + * This method combines the helper functions and performs the mapping of a cover network into the desired graph. + */ + void run() + { + /* convert the pi */ + for ( auto const& inpt : _cover_ntk._storage->inputs ) + { + _connector.insert( _ntk.create_pi(), inpt ); + } + + /* convert the nodes */ + for ( auto const& nde : _cover_ntk._storage->nodes ) + { + uint64_t index = _cover_ntk._storage->hash[nde]; + bool condition1 = ( std::find( _cover_ntk._storage->inputs.begin(), _cover_ntk._storage->inputs.end(), index ) != _cover_ntk._storage->inputs.end() ); + bool condition2 = nde.data[1].h1 == 0 || nde.data[1].h1 == 1; + + /* convert only the nodes that are neither inputs nor constants */ + if ( !condition1 && !condition2 ) + { + _connector.insert( convert_node_to_graph( nde ), _cover_ntk._storage->hash[nde] ); + } /* convert separately the constant 0 */ + else if ( nde.data[1].h1 == 0 ) + { + _connector.insert( _ntk.get_constant( false ), _cover_ntk._storage->hash[nde] ); + } /* convert separately the constant 1 */ + else if ( nde.data[1].h1 == 1 ) + { + _connector.insert( _ntk.get_constant( true ), _cover_ntk._storage->hash[nde] ); + } + } + + /* convert the outputs */ + for ( const auto& outpt : _cover_ntk._storage->outputs ) + { + _ntk.create_po( _connector.signals[outpt.index] ); + } + } + +private: + Ntk& _ntk; + cover_network const& _cover_ntk; + signals_connector _connector; +}; + +} /* namespace detail */ + +/*! \brief Inline convert a `cover_network` into another network type. + * + * **Required network functions:** + * - `create_and` + * - `create_or` + * - `create_buf` + * - `create_not` + * + * \param cover_ntk Input network of type `cover_network`. + * \param ntk Output network of type `Ntk`. + */ +template +void convert_cover_to_graph( Ntk& ntk, const cover_network& cover_ntk ) +{ + static_assert( has_create_and_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_or_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_buf_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + detail::cover_to_graph_converter converter( ntk, cover_ntk ); + converter.run(); +} + +/*! \brief Out-of-place convert a `cover_network` into another network type. + * + * **Required network functions:** + * - `create_and` + * - `create_or` + * - `create_buf` + * - `create_not` + * + * \param cover_ntk Input network of type `cover_network`. + * \return ntk Output network of type `Ntk`. + */ +template +Ntk convert_cover_to_graph( const cover_network& cover_ntk ) +{ + static_assert( has_create_and_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_or_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_buf_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + Ntk ntk; + detail::cover_to_graph_converter converter( ntk, cover_ntk ); + converter.run(); + return converter.get_network(); +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration.hpp new file mode 100644 index 00000000000..2faaf690df6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration.hpp @@ -0,0 +1,1840 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cut_enumeration.hpp + \brief Cut enumeration + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Sahand Kashani-Akhavan + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "../traits.hpp" +#include "../utils/cuts.hpp" +#include "../utils/mixed_radix.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/truth_table_cache.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for cut_enumeration. + * + * The data structure `cut_enumeration_params` holds configurable parameters + * with default arguments for `cut_enumeration`. + */ +struct cut_enumeration_params +{ + /*! \brief Maximum number of leaves for a cut. */ + uint32_t cut_size{ 4u }; + + /*! \brief Maximum number of cuts for a node. */ + uint32_t cut_limit{ 25u }; + + /*! \brief Maximum number of fan-ins for a node. */ + uint32_t fanin_limit{ 10u }; + + /*! \brief Prune cuts by removing don't cares. */ + bool minimize_truth_table{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose. */ + bool very_verbose{ false }; +}; + +struct cut_enumeration_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for truth table computation. */ + stopwatch<>::duration time_truth_table{ 0 }; + + /*! \brief Prints report. */ + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] truth table time = {:>5.2f} secs\n", to_seconds( time_truth_table ) ); + } +}; + +static constexpr uint32_t max_cut_size = 16; + +template +struct cut_data; + +template +struct cut_data +{ + uint32_t func_id; + T data; +}; + +template +struct cut_data +{ + T data; +}; + +template +using cut_type = cut>; + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct network_cuts; + +template +network_cuts cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps = {}, cut_enumeration_stats* pst = nullptr ); + +/* function to update a cut */ +template +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + (void)cut; + (void)cuts; + (void)ntk; + (void)n; + } +}; + +namespace detail +{ +template +class cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Cut database for a network. + * + * The function `cut_enumeration` returns an instance of type `network_cuts` + * which contains a cut database and can be queried to return all cuts of a + * node, or the function of a cut (if it was computed). + * + * An instance of type `network_cuts` can only be constructed from the + * `cut_enumeration` algorithm. + */ +template +struct network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 26; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +private: + explicit network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::dynamic_truth_table zero( 0u ), proj( 1u ); + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) { return _cuts[node_index]; } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const { return _cuts[node_index]; } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::dynamic_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::cut_enumeration_impl; + + template + friend network_cuts<_Ntk, _ComputeTruth, _CutData> cut_enumeration( _Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ); + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + +private: + /* compressed representation of cuts */ + std::vector _cuts; + + /* cut truth tables */ + truth_table_cache _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class cut_enumeration_impl +{ +public: + using cut_t = typename network_cuts::cut_t; + using cut_set_t = typename network_cuts::cut_set_t; + + explicit cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + +private: + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = kitty::extend_to( cuts._truth_tables[( *cut )->func_id], res.size() ); + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + auto tt_res_shrink = shrink_to( tt_res, static_cast( support.size() ) ); + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + return cuts._truth_tables.insert( tt_res_shrink ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, ps.cut_size ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, ps.cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, ps.cut_size ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Cut enumeration. + * + * This function implements the cut enumeration algorithm. The algorithm + * traverses all nodes in topological order and computes a node's cuts based + * on its fanins' cuts. Dominated cuts are filtered and are not added to the + * cut set. For each node a unit cut is added to the end of each cut set. + * + * The template parameter `ComputeTruth` controls whether truth tables should + * be computed for each cut. Computing truth tables slows down the execution + * time of the algorithm. + * + * The number of computed cuts is controlled via the `cut_limit` parameter. + * To decide which cuts are collected in each node's cut set, cuts are sorted. + * Unit cuts do not participate in the sorting and are always added to the end + * of each cut set. + * + * The algorithm can be configured by specifying the template argument `CutData` + * which holds the application specific data assigned to each cut. Examples + * on how to specify custom cost functions for sorting cuts based on the + * application specific cut data can be found in the files contained in the + * directory `include/mockturtle/algorithms/cut_enumeration`. + * + * **Required network functions:** + * - `is_constant` + * - `is_ci` + * - `size` + * - `get_node` + * - `node_to_index` + * - `foreach_node` + * - `foreach_fanin` + * - `compute` for `kitty::dynamic_truth_table` (if `ComputeTruth` is true) + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes + one can wrap the network parameter in a ``topo_view`` view. + + .. note:: + + The implementation of this algorithm was heavily inspired buy cut + enumeration implementations in ABC. + \endverbatim + */ +template +network_cuts cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( !ComputeTruth || has_compute_v, "Ntk does not implement the compute method for kitty::dynamic_truth_table" ); + + cut_enumeration_stats st; + network_cuts res( ntk.size() ); + detail::cut_enumeration_impl p( ntk, ps, st, res ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return res; +} + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct fast_network_cuts; + +template +fast_network_cuts fast_cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps = {}, cut_enumeration_stats* pst = nullptr ); + +namespace detail +{ +template +class fast_cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Cut database for a network. + * + * The function `cut_enumeration` returns an instance of type `fast_network_cuts` + * which contains a cut database and can be queried to return all cuts of a + * node, or the function of a cut (if it was computed). + * + * Comparing to `network_cuts`, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * An instance of type `fast_network_cuts` can only be constructed from the + * `fast_cut_enumeration` algorithm. + */ +template +struct fast_network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 50; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +private: + explicit fast_network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::static_truth_table zero, proj; + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) { return _cuts[node_index]; } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const { return _cuts[node_index]; } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::static_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::fast_cut_enumeration_impl; + + template + friend fast_network_cuts<_Ntk, _NumVars, _ComputeTruth, _CutData> fast_cut_enumeration( _Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ); + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + +private: + /* compressed representation of cuts */ + std::vector _cuts; + + /* cut truth tables */ + truth_table_cache> _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class fast_cut_enumeration_impl +{ +public: + using cut_t = typename fast_network_cuts::cut_t; + using cut_set_t = typename fast_network_cuts::cut_set_t; + + explicit fast_cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, fast_network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + +private: + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector> tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = cuts._truth_tables[( *cut )->func_id]; + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, NumVars ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, NumVars ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, NumVars ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + fast_network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Fast cut enumeration. + * + * This function implements the cut enumeration algorithm. The algorithm + * traverses all nodes in topological order and computes a node's cuts based + * on its fanins' cuts. Dominated cuts are filtered and are not added to the + * cut set. For each node a unit cut is added to the end of each cut set. + * + * The template parameter `ComputeTruth` controls whether truth tables should + * be computed for each cut. Computing truth tables slows down the execution + * time of the algorithm. + * + * The cut size is controlled using the template parameter `NumVars` instead + * of the `cut_size` parameter as in `cut_enumeration`. + * + * Comparing to `cut_enumeration`, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * The number of computed cuts is controlled via the `cut_limit` parameter. + * To decide which cuts are collected in each node's cut set, cuts are sorted. + * Unit cuts do not participate in the sorting and are always added to the end + * of each cut set. + * + * The algorithm can be configured by specifying the template argument `CutData` + * which holds the application specific data assigned to each cut. Examples + * on how to specify custom cost functions for sorting cuts based on the + * application specific cut data can be found in the files contained in the + * directory `include/mockturtle/algorithms/cut_enumeration`. + * + * **Required network functions:** + * - `is_constant` + * - `is_ci` + * - `size` + * - `get_node` + * - `node_to_index` + * - `foreach_node` + * - `foreach_fanin` + * - `compute` for `kitty::static_truth_table` (if `ComputeTruth` is true) + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes + one can wrap the network parameter in a ``topo_view`` view. + + .. note:: + + The implementation of this algorithm was heavily inspired by cut + enumeration implementations in ABC. + \endverbatim + */ +template +fast_network_cuts fast_cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( !ComputeTruth || has_compute_v, "Ntk does not implement the compute method for kitty::dynamic_truth_table" ); + + cut_enumeration_stats st; + fast_network_cuts res( ntk.size() ); + detail::fast_cut_enumeration_impl p( ntk, ps, st, res ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return res; +} + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct dynamic_network_cuts; + +namespace detail +{ +template +class dynamic_cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Dynamic cut database for a network. + * + * Struct `dynamic_network_cuts` contains a cut database and can be queried + * to return all cuts of a node, or the function of a cut (if it was computed). + * + * Comparing to `network_cuts`, it supports dynamic allocation of cuts for + * networks in expansion. Moreover, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * An instance of type `dynamic_network_cuts` can only be constructed from the + * `dynamic_cut_enumeration_impl` algorithm. + */ +template +struct dynamic_network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 16u; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +public: + explicit dynamic_network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::static_truth_table zero, proj; + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) + { + if ( node_index >= _cuts.size() ) + _cuts.resize( node_index + 1 ); + + return _cuts[node_index]; + } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const + { + assert( node_index < _cuts.size() ); + return _cuts[node_index]; + } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::static_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::dynamic_cut_enumeration_impl; + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + + void clear_cut_set( uint32_t index ) + { + _cuts[index].clear(); + } + +private: + /* compressed representation of cuts */ + std::deque _cuts; + + /* cut truth tables */ + truth_table_cache> _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ +template +class dynamic_cut_enumeration_impl +{ +public: + using cut_t = typename dynamic_network_cuts::cut_t; + using cut_set_t = typename dynamic_network_cuts::cut_set_t; + + explicit dynamic_cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, dynamic_network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + + void compute_cuts( node const& n ) + { + const auto index = ntk.node_to_index( n ); + + if ( cuts.cuts( index ).size() > 0 ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_cuts( ntk.get_node( f ) ); + } ); + + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + + void init_cuts() + { + cuts.add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + cuts.add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( true ) ) ) ); + ntk.foreach_ci( [&]( auto const& n ) { + cuts.add_unit_cut( ntk.node_to_index( n ) ); + } ); + } + + void clear_cuts( node const& n ) + { + const auto index = ntk.node_to_index( n ); + if ( cuts.cuts( index ).size() == 0 ) + return; + + cuts.clear_cut_set( index ); + } + +private: + inline bool fast_support_minimization( kitty::static_truth_table const& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + } + + return true; + } + + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector> tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = cuts._truth_tables[( *cut )->func_id]; + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, NumVars ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, NumVars ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, NumVars ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + dynamic_network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +// This function expects to receive a network where nodes are sorted in +// topological order. Cuts are represented as a 64-bit bit vector where each bit +// determines whether a given node exists in the cut. + +/*! \brief Cut enumeration. + * + * This function implements a generic fast cut enumeration algorithm for graphs + * containing at most 64 nodes. It is generic as it supports graphs in which + * nodes can have variable fan-in. Speed and space-efficiency are achieved by + * representing cuts as 64-bit bit vectors, i.e. each bit represents whether or + * not a node is in a cut. Cut-set union and domination operations then + * transform into bitwise operations that can be performed in a few clock cycles + * each. + * + * Like the larger cut_enumeration algorithm, this algorithm traverses all nodes + * in topological order and computes a node's cuts based on its fanins' cuts. + * Dominated cuts are filtered and are not added to the cut set. For each node a + * unit cut is added to the end of each cut set. + * + * This function computes all cuts of the network (i.e. the number of generated + * cuts is not bounded). Though the number of cuts cannot be bounded, their size + * can be bound by passing a `cut_size` argument to the function. + * + * **Required network functions:** + * - `fanin_size` + * - `foreach_fanin` + * - `foreach_gate` + * - `foreach_ci` + * - `get_node` + * - `node_to_index` + * - `size` + * + * Note that this algorithm *only* works for graphs with at most 64 nodes. + * However, since we cannot know the size of a graph at compile-time, this + * function returns the results wrapped in an std::optional. + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes one + can wrap the network parameter in a ``topo_view`` view. + \endverbatim + */ +template +std::optional>> +fast_small_cut_enumeration( Ntk const& ntk, const uint8_t cut_size = 4 ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + // You cannot check whether a graph is topologically sorted at compile-time, + // but the is_topologically_sorted_v template is used inside the + // topo_view class to determine whether the input graph should just be copied + // as it is already topologically-sorted, or whether the graph's topological + // order is to be computed. + // static_assert( is_topologically_sorted_v, "Ntk is not a topologically-sorted network" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + // Max 64 nodes, so 8 bits are enough for indices. + using node_idx_t = uint8_t; + using cut_t = uint64_t; + using cut_set_t = std::vector; + using cut_sets_t = std::vector; + + // It is not possible to know the size of a network at compile-time, so I will + // return a boolean flag stating whether the cut-sets returned by this + // function are valid or not. + constexpr node_idx_t max_nodes = 64; + if ( ntk.size() > max_nodes ) + { + return std::nullopt; + } + + ////////////////////////////////////////////////////////////////////////////// + // Final cut-sets to be computed ///////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // size() returns the number of nodes including constants, PIs, and dead + // nodes, so no need to allocate +1 memory slots like in the lecture notes + // to explicitly represent constants. + // By definition of the vector constructor, each cut-set is initialized to {}. + cut_sets_t cut_sets( ntk.size() ); + + ////////////////////////////////////////////////////////////////////////////// + // Helper functions ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + auto set_bit = []( + node_idx_t idx ) { + return static_cast( 1 ) << idx; + }; + + // Algorithm for counting #1 bits in a uint64_t. It is efficient as it only + // iterates as many times as the bit count to avoid always performing 64 + // iterations. Inspired from the following threads: + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive + // https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation + auto bit_cnt = []( + cut_t n ) { + uint8_t count = 0; + + while ( n > 0 ) + { + count = count + 1; + n = n & ( n - 1 ); + } + + return count; + }; + + // Operation to perform on each n-tuple. In the cut generation algorithm this + // involves computing a new cut from the fan-in nodes' selected cuts, checking + // whether an existing cut dominates it, and if not, adding it to the cutset + // of the current node. + auto visit_n_tuple = [&cut_sets, &bit_cnt, &cut_size]( + // Node for which we are computing the cut. + node_idx_t node_idx, + // Fan-in of the current node. + std::vector const& fanin_idx, + // Index of the cut to choose from the given fan-in node. + std::vector const& cut_idx ) { + // Compute new cut. + cut_t C = 0; + for ( auto i = 0U; i < fanin_idx.size(); i++ ) + { + C |= cut_sets.at( fanin_idx[i] ).at( cut_idx[i] ); + } + + // Restrict cut sizes if too large. + if ( bit_cnt( C ) > cut_size ) + { + return; + } + + // Don't add new cut if existing cut dominates it. A cut C' dominates + // another cut C if C' is a subset of C. Being a subset means that C' has at + // most the same bits set as C, but no more bits. + // + // So if we AND their bitsets together, we can see what they have in common, + // then we can XOR this with C' original bits to see if C' has any bit + // active that C does not. + // + // C' = 0b 00110 + // C = 0b 01010 AND + // -------- + // 0b 00010 + // C' = 0b 00110 XOR + // -------- + // 0b 00100 => C' does NOT dominate C as it contains a node that C does not. + for ( auto C_prime : cut_sets.at( node_idx ) ) + { + cut_t shared_nodes = C_prime & C; + cut_t C_prime_extra_nodes = shared_nodes ^ C_prime; + + bool C_prime_dominates_C = C_prime_extra_nodes == 0; + if ( C_prime_dominates_C ) + { + return; + } + } + + cut_sets.at( node_idx ).push_back( C ); + }; + + // Enumerates cuts of a given node. The inputs of the node can have variable + // fan-in, so the cut-sets they have could have different sizes. We therefore + // cannot use N nested for-loops to perform a cross product of the fan-in cuts + // since we don't know the cut-set sizes in advance. We instead use the + // mixed-radix n-tuple generation algorithm in TAOCP, Vol 4A, algorithm M. + auto cut_enumeration_node = [&cut_sets, &visit_n_tuple]( + Ntk const& ntk, + node const& node ) { + node_idx_t node_idx = ntk.node_to_index( node ); + + // Index of the node's fan-ins. + std::vector fanin_idx; + + // Number of cuts of a given fan-in node ("radix" in TAOCP, Vol 4A, Algorithm M). + std::vector cut_set_size; // radix (m[n-1], ... , m[0]) in TAOCP + + // Index of a cut of a given fan-in node ("value" in TAOCP, Vol 4A, Algorithm M). + std::vector cut_idx; // value (a[n-1], ... , a[0]) in TAOCP + + // We start by initializing the indices of the fan-in nodes' cuts and their + // radix. Need to get the the index of the fan-in nodes for this so we can + // query their number of cuts. + ntk.foreach_fanin( + node, + [&]( auto sig ) { + auto fanin_node = ntk.get_node( sig ); + auto fanin_node_idx = ntk.node_to_index( fanin_node ); + + fanin_idx.push_back( fanin_node_idx ); + // Radix of the given fan-in is determined by the number of cuts it has. + cut_set_size.push_back( cut_sets.at( fanin_node_idx ).size() ); + // Start counting from (0, 0, ... , 0) + cut_idx.push_back( 0 ); + } ); + + // Fan-in = number of cut-sets we must perform a cross-product over. + auto const num_cut_sets = ntk.fanin_size( node ); + + uint8_t j = 0; + while ( j != num_cut_sets ) + { + visit_n_tuple( node_idx, fanin_idx, cut_idx ); + + // Mixed-radix n-tuple generation algorithm. Adding 1 to the n-tuple. + j = 0; + while ( ( j != num_cut_sets ) && ( cut_idx.at( j ) == cut_set_size.at( j ) - 1 ) ) + { + cut_idx.at( j ) = 0; + j += 1; + } + if ( j != num_cut_sets ) + { + cut_idx.at( j ) += 1; + } + } + }; + + ////////////////////////////////////////////////////////////////////////////// + // Main algorithm //////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // Primary inputs only have themselves as their cut-set. + ntk.foreach_ci( + [&]( auto node ) { + auto const idx = ntk.node_to_index( node ); + cut_sets.at( idx ) = { set_bit( idx ) }; + } ); + + // Going through remaining gates (excluding constants and primary inputs). + ntk.foreach_gate( + [&]( auto node ) { + // Technically don't need to do this as vectors are constructed with zero + // length, but we leave it for clarity. + auto const idx = ntk.node_to_index( node ); + cut_sets.at( idx ) = {}; + + // Internally uses TAOCP Vol 4A algorithm M, mixed-radix n-tuple + // generation, to enumerate the cross-product of the node's fan-in + // cut-sets. + cut_enumeration_node( ntk, node ); + + cut_sets.at( idx ).push_back( set_bit( idx ) ); + } ); + + return cut_sets; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/cnf_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/cnf_cut.hpp new file mode 100644 index 00000000000..17ce75dfbee --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/cnf_cut.hpp @@ -0,0 +1,109 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cnf_cut.hpp + \brief Cut enumeration for CNF mapping + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Cut for CNF mapping applications. + + This cut type uses the clause count in the CNF encoding of the cut function + as cost function. It requires truth table computation during cut enumeration + or LUT mapping in order to work. +*/ +struct cut_enumeration_cnf_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + float cost{ 0 }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + auto tt = cuts.truth_table( cut ); + auto cnf = kitty::cnf_characteristic( tt ); + cut->data.cost = cnf; + float flow = cut.size() < 2 ? 0.0f : 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp new file mode 100644 index 00000000000..2404bf2df47 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp @@ -0,0 +1,100 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file exact_map_cut.hpp + \brief Cut enumeration for mapping with exact synthesis + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation for graph mapping with a complete database */ +struct cut_enumeration_exact_map_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + uint8_t match_index{ 0 }; + bool ignore{ false }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + float flow = 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/gia_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/gia_cut.hpp new file mode 100644 index 00000000000..9ad01267c80 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/gia_cut.hpp @@ -0,0 +1,82 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file gia_cut.hpp + \brief Cut enumeration as in giaCut.c + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation based on ABC's giaCut.c + + See giaCut.c in ABC's repository. +*/ +struct cut_enumeration_gia_cut +{ + uint32_t num_tree_leaves; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + if ( c1->data.num_tree_leaves < c2->data.num_tree_leaves ) + { + return true; + } + if ( c1->data.num_tree_leaves > c2->data.num_tree_leaves ) + { + return false; + } + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + (void)n; + (void)cuts; + cut->data.num_tree_leaves = std::count_if( cut.begin(), cut.end(), + [&ntk]( auto index ) { + return ntk.fanout_size( index ) == 1; + } ); + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp new file mode 100644 index 00000000000..ebaee4beaf6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp @@ -0,0 +1,102 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mf_cut.hpp + \brief Cut enumeration for MF mapping (see giaMf.c) + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation based on ABC's giaMf.c + + See giaMf.c in ABC's repository. +*/ +struct cut_enumeration_mf_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + float cost{ 0 }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + float flow = cut->data.cost = cut.size() < 2 ? 0.0f : 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/rewrite_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/rewrite_cut.hpp new file mode 100644 index 00000000000..04a8226207c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/rewrite_cut.hpp @@ -0,0 +1,88 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file rewrite_cut.hpp + \brief Cut enumeration for rewriting + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation for rewrite */ +struct cut_enumeration_rewrite_cut +{ + uint32_t cost; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + if ( c1->data.cost < c2->data.cost ) + return true; + if ( c1->data.cost > c2->data.cost ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t value = 0; + + for ( auto leaf : cut ) + { + value += ( ntk.fanout_size( ntk.index_to_node( leaf ) ) == 1 ) ? 1u : 0u; + } + + cut->data.cost = value; + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, C = " << std::setw( 3 ) << c->data.cost; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/spectr_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/spectr_cut.hpp new file mode 100644 index 00000000000..d14dfac919d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/spectr_cut.hpp @@ -0,0 +1,197 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file spectr_cut.hpp + \brief Cut enumeration based on spectral properties of a function + + \author Giulia Meuli + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "../../utils/cuts.hpp" +#include "../cut_enumeration.hpp" +#include "../lut_mapping.hpp" +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Cut based on spectral properties. + + This cut type uses the number of non-zero coefficients in the cut function as + cost function. It requires truth table computation during cut enumeration + or LUT mapping in order to work. +*/ +struct cut_enumeration_spectr_cut +{ + uint32_t delay{ 0u }; + float flow{ 0.0f }; + float cost{ 0.0f }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + + if ( c1.size() == c2.size() ) + { + if ( c1->data.cost < c2->data.cost ) + return true; + if ( c1->data.cost > c2->data.cost ) + return false; + } + if ( c1.size() > c2.size() && c1->data.cost < c2->data.cost ) + { + return false; + } + if ( c1.size() < c2.size() && c1->data.cost > c2->data.cost ) + { + return true; + } + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() > c2.size(); +} + +template<> +struct lut_mapping_update_cuts +{ + + template + static void grow_xor_cut( Ntk const& ntk, node const& n, std::map>& node_to_cut ) + { + ntk.foreach_fanin( n, [&]( auto ch ) { + auto ch_node = ntk.get_node( ch ); + if ( ntk.is_xor( ch_node ) ) + { + auto leaves_ch = node_to_cut[ch_node]; + + if ( leaves_ch.size() + node_to_cut[n].size() < max_cut_size ) + { + node_to_cut[n].insert( node_to_cut[n].end(), leaves_ch.begin(), leaves_ch.end() ); + } + else + { + node_to_cut[n].push_back( ch_node ); + } + } + else + { + node_to_cut[n].push_back( ch_node ); + } + } ); + + std::stable_sort( node_to_cut[n].begin(), node_to_cut[n].end() ); + node_to_cut[n].erase( unique( node_to_cut[n].begin(), node_to_cut[n].end() ), node_to_cut[n].end() ); + } + + template + static void apply( NetworkCuts& cuts, Ntk const& ntk ) + { + + std::map> node_to_cut; + + topo_view( ntk ).foreach_node( [&]( auto n ) { + if ( ntk.is_xor( n ) ) + { + const auto index = ntk.node_to_index( n ); + auto& cut_set = cuts.cuts( index ); + + /* clear the cut set of the node */ + cut_set.clear(); + + /* add an empty cut and modify its leaves */ + grow_xor_cut( ntk, n, node_to_cut ); + + auto& my_cut = cut_set.add_cut( node_to_cut[n].begin(), node_to_cut[n].end() ); + + assert( node_to_cut[n].size() <= 16 ); + /* set to zero cost */ + my_cut->data.cost = 0u; + + /* crate cut truth table */ + kitty::dynamic_truth_table tt( node_to_cut[n].size() ); + kitty::create_parity( tt ); + my_cut->func_id = cuts.insert_truth_table( tt ); + } + } ); + } +}; + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + + auto tt = cuts.truth_table( cut ); + auto spectrum = kitty::rademacher_walsh_spectrum( tt ); + cut->data.cost = std::count_if( spectrum.begin(), spectrum.end(), []( auto s ) { return s != 0; } ); + + float flow = cut.size() < 2 ? 0.0f : 1.0f; + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp new file mode 100644 index 00000000000..4702a66f10d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp @@ -0,0 +1,100 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file tech_map_cut.hpp + \brief Cut enumeration for technology mapping + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation for technology mapping */ +struct cut_enumeration_tech_map_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + uint8_t match_index{ 0 }; + bool ignore{ false }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1->data.flow < c2->data.flow - eps; +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + float flow = 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_rewriting.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_rewriting.hpp new file mode 100644 index 00000000000..d58bbf8fd98 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_rewriting.hpp @@ -0,0 +1,868 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cut_rewriting.hpp + \brief Cut rewriting + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cut_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "detail/mffc_utils.hpp" +#include "dont_cares.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for cut_rewriting. + * + * The data structure `cut_rewriting_params` holds configurable parameters with + * default arguments for `cut_rewriting`. + */ +struct cut_rewriting_params +{ + cut_rewriting_params() + { + cut_enumeration_ps.cut_size = 6; + cut_enumeration_ps.cut_limit = 12; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Cut enumeration parameters. */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Allow zero-gain substitutions. */ + bool allow_zero_gain{ false }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Candidate selection strategy. */ + enum + { + minimize_weight, + greedy + } candidate_selection_strategy = minimize_weight; + + /*! \brief Minimum candidate cut size */ + uint32_t min_cand_cut_size{ 3u }; + + /*! \brief Minimum candidate cut size override (in conflict graph) */ + std::optional min_cand_cut_size_override{}; + + /*! \brief If true, candidates are only accepted if they do not increase logic level of node. */ + bool preserve_depth{ false }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose. */ + bool very_verbose{ false }; +}; + +/*! \brief Statistics for cut_rewriting. + * + * The data structure `cut_rewriting_stats` provides data collected by running + * `cut_rewriting`. + */ +struct cut_rewriting_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Runtime for cut enumeration. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for rewriting. */ + stopwatch<>::duration time_rewriting{ 0 }; + + /*! \brief Runtime to find minimal independent set. */ + stopwatch<>::duration time_mis{ 0 }; + + void report( bool show_time_mis = true ) const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] cut enum. time = {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( "[i] rewriting time = {:>5.2f} secs\n", to_seconds( time_rewriting ) ); + if ( show_time_mis ) + { + fmt::print( "[i] ind. set time = {:>5.2f} secs\n", to_seconds( time_mis ) ); + } + } +}; + +namespace detail +{ + +class graph +{ +public: + auto add_vertex( uint32_t weight ) + { + auto index = _weights.size(); + _weights.emplace_back( weight ); + _adjacent.emplace_back(); + ++_num_vertices; + return index; + } + + void add_edge( uint32_t v1, uint32_t v2 ) + { + if ( v1 == v2 ) + return; + if ( _adjacent[v1].count( v2 ) ) + return; + + _adjacent[v1].insert( v2 ); + _adjacent[v2].insert( v1 ); + ++_num_edges; + } + + void remove_vertex( uint32_t vertex ) + { + assert( _weights[vertex] != -1 ); + _weights[vertex] = -1; + + _num_edges -= _adjacent[vertex].size(); + + for ( auto w : _adjacent[vertex] ) + { + _adjacent[w].erase( vertex ); + } + _adjacent[vertex].clear(); + + --_num_vertices; + } + + bool has_vertex( uint32_t vertex ) const + { + return _weights[vertex] >= 0; + } + + template + void foreach_adjacent( uint32_t vertex, Fn&& fn ) const + { + std::for_each( _adjacent[vertex].begin(), _adjacent[vertex].end(), fn ); + } + + template + void foreach_vertex( Fn&& fn ) const + { + for ( auto i = 0u; i < _weights.size(); ++i ) + { + if ( has_vertex( i ) ) + { + fn( i ); + } + } + } + + auto degree( uint32_t vertex ) const { return _adjacent[vertex].size(); } + auto weight( uint32_t vertex ) const { return _weights[vertex]; } + auto gwmin_value( uint32_t vertex ) const { return (double)weight( vertex ) / ( degree( vertex ) + 1 ); } + auto gwmax_value( uint32_t vertex ) const { return (double)weight( vertex ) / ( degree( vertex ) * ( degree( vertex ) + 1 ) ); } + + auto num_vertices() const { return _num_vertices; } + auto num_edges() const { return _num_edges; } + +private: + uint32_t _num_vertices{ 0u }; + std::size_t _num_edges{ 0u }; + + std::vector> _adjacent; + + std::vector _weights; /* degree = -1 means vertex is removed */ +}; + +inline std::vector maximum_weighted_independent_set_gwmin( graph& g ) +{ + std::vector mwis; + + std::vector vertices( g.num_vertices() ); + std::iota( vertices.begin(), vertices.end(), 0 ); + + std::stable_sort( vertices.begin(), vertices.end(), [&g]( auto v, auto w ) { + const auto value_v = g.gwmin_value( v ); + const auto value_w = g.gwmin_value( w ); + return value_v > value_w || ( value_v == value_w && g.degree( v ) > g.degree( w ) ); + } ); + + for ( auto i : vertices ) + { + if ( !g.has_vertex( i ) ) + continue; + + /* add vertex to independent set, then remove it and all its neighbors */ + mwis.emplace_back( i ); + std::vector neighbors; + g.foreach_adjacent( i, [&]( auto v ) { neighbors.emplace_back( v ); } ); + g.remove_vertex( i ); + + for ( auto v : neighbors ) + { + g.remove_vertex( v ); + } + } + + return mwis; +} + +inline std::vector maximal_weighted_independent_set( graph& g ) +{ + std::vector mwis; + + auto num_vertices = g.num_vertices(); + for ( auto i = 0u; i < num_vertices; ++i ) + { + if ( !g.has_vertex( i ) ) + continue; + + /* add vertex to independent set, then remove it and all its neighbors */ + mwis.emplace_back( i ); + std::vector neighbors; + g.foreach_adjacent( i, [&]( auto v ) { neighbors.emplace_back( v ); } ); + g.remove_vertex( i ); + + for ( auto v : neighbors ) + { + g.remove_vertex( v ); + } + } + + return mwis; +} + +struct cut_enumeration_cut_rewriting_cut +{ + int32_t gain{ -1 }; +}; + +template +std::tuple, uint32_t>>> network_cuts_graph( Ntk const& ntk, network_cuts const& cuts, cut_rewriting_params const& ps ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_clear_visited_v, "Ntk does not implement the clear_visited method" ); + + graph g; + + using cut_addr = std::pair, uint32_t>; + std::vector> conflicts( cuts.nodes_size() ); + std::vector vertex_to_cut_addr; + std::vector> cut_addr_to_vertex( cuts.nodes_size() ); + + ntk.clear_visited(); + + ntk.foreach_node( [&]( auto const& n, auto index ) { + if ( index >= cuts.nodes_size() || ntk.is_constant( n ) || ntk.is_pi( n ) ) + return; + + if ( mffc_size( ntk, n ) == 1 ) + return; + + const auto& set = cuts.cuts( ntk.node_to_index( n ) ); + + auto cctr{ 0u }; + for ( auto const& cut : set ) + { + if ( ps.min_cand_cut_size_override ) + { + if ( cut->size() < *ps.min_cand_cut_size_override ) + continue; + } + else if ( cut->size() < ps.min_cand_cut_size ) + continue; + + if ( ( *cut )->data.gain < ( ps.allow_zero_gain ? 0 : 1 ) ) + continue; + + std::vector> leaves; + for ( auto leaf_index : *cut ) + { + leaves.push_back( ntk.index_to_node( leaf_index ) ); + } + cut_view dcut( ntk, leaves, ntk.make_signal( n ) ); + dcut.foreach_gate( [&]( auto const& n2 ) { + // if ( dcut.is_constant( n2 ) || dcut.is_pi( n2 ) ) + // return; + conflicts[ntk.node_to_index( n2 )].emplace_back( n, cctr ); + } ); + + auto v = g.add_vertex( ( *cut )->data.gain ); + assert( v == vertex_to_cut_addr.size() ); + vertex_to_cut_addr.emplace_back( n, cctr ); + cut_addr_to_vertex[ntk.node_to_index( n )].emplace_back( static_cast( v ) ); + + ++cctr; + } + } ); + + for ( auto n = 0u; n < conflicts.size(); ++n ) + { + for ( auto j = 1u; j < conflicts[n].size(); ++j ) + { + for ( auto i = 0u; i < j; ++i ) + { + const auto [n1, c1] = conflicts[n][i]; + const auto [n2, c2] = conflicts[n][j]; + + if ( cut_addr_to_vertex[ntk.node_to_index( n1 )][c1] != cut_addr_to_vertex[ntk.node_to_index( n2 )][c2] ) + { + g.add_edge( cut_addr_to_vertex[ntk.node_to_index( n1 )][c1], cut_addr_to_vertex[ntk.node_to_index( n2 )][c2] ); + } + } + } + } + + return { g, vertex_to_cut_addr }; +} + +template +struct has_rewrite_with_dont_cares : std::false_type +{ +}; + +template +struct has_rewrite_with_dont_cares()( std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_rewrite_with_dont_cares_v = has_rewrite_with_dont_cares::value; + +template +class cut_rewriting_with_compatibility_graph_impl +{ +public: + cut_rewriting_with_compatibility_graph_impl( Ntk& ntk, RewritingFn&& rewriting_fn, cut_rewriting_params const& ps, cut_rewriting_stats& st, NodeCostFn const& cost_fn ) + : ntk( ntk ), + rewriting_fn( rewriting_fn ), + ps( ps ), + st( st ), + cost_fn( cost_fn ) {} + + void run() + { + stopwatch t( st.time_total ); + + /* enumerate cuts */ + const auto cuts = call_with_stopwatch( st.time_cuts, [&]() { return cut_enumeration( ntk, ps.cut_enumeration_ps ); } ); + + /* for cost estimation we use reference counters initialized by the fanout size */ + ntk.clear_values(); + ntk.foreach_node( [&]( auto const& n ) { + ntk.set_value( n, ntk.fanout_size( n ) ); + } ); + + /* store best replacement for each cut */ + node_map>, Ntk> best_replacements( ntk ); + + /* iterate over all original nodes in the network */ + const auto size = ntk.size(); + auto max_total_gain = 0u; + progress_bar pbar{ ntk.size(), "cut_rewriting |{0}| node = {1:>4}@{2:>2} / " + std::to_string( size ) + " comm. gain = {3}", ps.progress }; + ntk.foreach_node( [&]( auto const& n, auto index ) { + /* stop once all original nodes were visited */ + if ( index >= size ) + return false; + + /* do not iterate over constants or PIs */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return true; + + /* skip cuts with small MFFC */ + if ( mffc_size( ntk, n ) == 1 ) + return true; + + /* foreach cut */ + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + /* skip trivial cuts */ + if ( cut->size() < ps.min_cand_cut_size ) + continue; + + const auto tt = cuts.truth_table( *cut ); + assert( cut->size() == static_cast( tt.num_vars() ) ); + + pbar( index, ntk.node_to_index( n ), best_replacements[n].size(), max_total_gain ); + + std::vector> children; + for ( auto l : *cut ) + { + children.push_back( ntk.make_signal( ntk.index_to_node( l ) ) ); + } + + int32_t value = recursive_deref( ntk, n ); + { + stopwatch t( st.time_rewriting ); + int32_t best_gain{ -1 }; + + const auto on_signal = [&]( auto const& f_new ) { + auto [v, contains] = recursive_ref_contains( ntk.get_node( f_new ), n ); + recursive_deref( ntk, ntk.get_node( f_new ) ); + + int32_t gain = contains ? -1 : value - v; + + if ( gain > 0 || ( ps.allow_zero_gain && gain == 0 ) ) + { + if ( best_gain == -1 ) + { + ( *cut )->data.gain = best_gain = gain; + best_replacements[n].push_back( f_new ); + } + else if ( gain > best_gain ) + { + ( *cut )->data.gain = best_gain = gain; + best_replacements[n].back() = f_new; + } + } + + return true; + }; + + if ( ps.use_dont_cares ) + { + if constexpr ( has_rewrite_with_dont_cares_v ) + { + std::vector> pivots; + for ( auto const& c : children ) + { + pivots.push_back( ntk.get_node( c ) ); + } + rewriting_fn( ntk, cuts.truth_table( *cut ), satisfiability_dont_cares( ntk, pivots ), children.begin(), children.end(), on_signal ); + } + else + { + rewriting_fn( ntk, cuts.truth_table( *cut ), children.begin(), children.end(), on_signal ); + } + } + else + { + rewriting_fn( ntk, cuts.truth_table( *cut ), children.begin(), children.end(), on_signal ); + } + + if ( best_gain > 0 ) + { + max_total_gain += best_gain; + } + } + + recursive_ref( ntk, n ); + } + + return true; + } ); + + stopwatch t2( st.time_mis ); + auto [g, map] = network_cuts_graph( ntk, cuts, ps ); + + if ( ps.very_verbose ) + { + std::cout << "[i] replacement dependency graph has " << g.num_vertices() << " vertices and " << g.num_edges() << " edges\n"; + } + + const auto is = ( ps.candidate_selection_strategy == cut_rewriting_params::minimize_weight ) ? maximum_weighted_independent_set_gwmin( g ) : maximal_weighted_independent_set( g ); + + if ( ps.very_verbose ) + { + std::cout << "[i] size of independent set is " << is.size() << "\n"; + } + + for ( const auto v : is ) + { + const auto v_node = map[v].first; + const auto v_cut = map[v].second; + + if ( ps.very_verbose ) + { + std::cout << "[i] try to rewrite cut #" << v_cut << " in node #" << ntk.node_to_index( v_node ) << "\n"; + } + + if ( best_replacements[v_node].empty() ) + continue; + + const auto replacement = best_replacements[v_node][v_cut]; + + if ( ntk.is_constant( ntk.get_node( replacement ) ) || v_node == ntk.get_node( replacement ) ) + continue; + + if ( ps.very_verbose ) + { + std::cout << "[i] optimize cut #" << v_cut << " in node #" << ntk.node_to_index( v_node ) << " and replace with node " << ntk.node_to_index( ntk.get_node( replacement ) ) << "\n"; + } + + ntk.substitute_node( v_node, replacement ); + } + } + +private: + std::pair recursive_ref_contains( node const& n, node const& repl ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return { 0, false }; + + /* recursively collect nodes */ + int32_t value = cost_fn( ntk, n ); + bool contains = ( n == repl ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + contains = contains || ( ntk.get_node( s ) == repl ); + if ( ntk.incr_value( ntk.get_node( s ) ) == 0 ) + { + const auto [v, c] = recursive_ref_contains( ntk.get_node( s ), repl ); + value += v; + contains = contains || c; + } + } ); + return { value, contains }; + } + +private: + Ntk& ntk; + RewritingFn&& rewriting_fn; + cut_rewriting_params const& ps; + cut_rewriting_stats& st; + NodeCostFn cost_fn; +}; + +} /* namespace detail */ + +/*! \brief In-place cut rewriting algorithm with compatibility graph. + * + * This algorithm enumerates cut of a network and then tries to rewrite the cut + * in terms of gates of the same network. The rewritten structures are added + * to the network, and if they lead to area improvement, will be used as new + * parts of the logic. The resulting network therefore has a lot of dangling + * nodes from unsuccessful candidates, which can be removed by calling + * `cleanup_dangling` after the rewriting algorithm. + * + * The rewriting function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. There are + * some rewriting algorithms in the folder + * `mockturtle/algorithms/node_resynthesis`, since the resynthesis functions + * have the same signature. + * + * In contrast to node resynthesis, cut rewriting uses the same type for the + * input and output network. Consequently, the algorithm does not return a + * new network but applies changes in-place to the input network. + * + * **Required network functions:** + * - `fanout_size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * - `clear_values` + * - `incr_value` + * - `decr_value` + * - `set_value` + * - `node_to_index` + * - `index_to_node` + * - `substitute_node` + * - `make_signal` + * + * \param ntk Network (will be modified) + * \param rewriting_fn Rewriting function + * \param ps Rewriting params + * \param pst Rewriting statistics + * \param cost_fn Node cost function (a functor with signature `uint32_t(Ntk const&, node const&)`) + */ +template> +void cut_rewriting_with_compatibility_graph( Ntk& ntk, RewritingFn&& rewriting_fn, cut_rewriting_params const& ps = {}, cut_rewriting_stats* pst = nullptr, NodeCostFn const& cost_fn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + cut_rewriting_stats st; + if constexpr ( std::is_same_v ) + { + detail::cut_rewriting_with_compatibility_graph_impl p( ntk, rewriting_fn, ps, st, cost_fn ); + p.run(); + } + else + { + fanout_view_params fvps; + fvps.update_on_delete = false; + fanout_view ntk_fo{ ntk, fvps }; + detail::cut_rewriting_with_compatibility_graph_impl, RewritingFn, NodeCostFn> p( ntk_fo, rewriting_fn, ps, st, cost_fn ); + p.run(); + } + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +namespace detail +{ + +template +struct cut_rewriting_impl +{ + cut_rewriting_impl( Ntk const& ntk, RewritingFn const& rewriting_fn, cut_rewriting_params const& ps, cut_rewriting_stats& st ) + : ntk_( ntk ), + rewriting_fn_( rewriting_fn ), + ps_( ps ), + st_( st ) {} + + NtkDest run() + { + stopwatch t( st_.time_total ); + + /* initial node map */ + node_map, Ntk> old2new( ntk_ ); + Ntk res; + old2new[ntk_.get_constant( false )] = res.get_constant( false ); + if ( ntk_.get_node( ntk_.get_constant( true ) ) != ntk_.get_node( ntk_.get_constant( false ) ) ) + { + old2new[ntk_.get_constant( true )] = res.get_constant( true ); + } + ntk_.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + /* enumerate cuts */ + const auto cuts = call_with_stopwatch( st_.time_cuts, [&]() { return cut_enumeration( ntk_, ps_.cut_enumeration_ps ); } ); + + /* for cost estimation we use reference counters initialized by the fanout size */ + initialize_values_with_fanout( ntk_ ); + + /* original cost */ + const auto orig_cost = costs( ntk_ ); + + progress_bar pbar{ ntk_.num_gates(), "cut_rewriting |{0}| node = {1:>4} / " + std::to_string( ntk_.num_gates() ) + " original cost = " + std::to_string( orig_cost ), ps_.progress }; + ntk_.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i ); + + /* nothing to optimize? */ + int32_t value = mffc_size( ntk_, n ); + if ( value == 1 ) + { + std::vector> children( ntk_.fanin_size( n ) ); + ntk_.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = ntk_.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f]; + } ); + + old2new[n] = res.clone_node( ntk_, n, children ); + } + else + { + /* foreach cut */ + int32_t best_gain = -1; + signal best_signal; + for ( auto& cut : cuts.cuts( ntk_.node_to_index( n ) ) ) + { + /* skip small enough cuts */ + if ( cut->size() == 1 || cut->size() < ps_.min_cand_cut_size ) + continue; + + const auto tt = cuts.truth_table( *cut ); + assert( cut->size() == static_cast( tt.num_vars() ) ); + + std::vector> children( cut->size() ); + auto ctr = 0u; + for ( auto l : *cut ) + { + children[ctr++] = old2new[ntk_.index_to_node( l )]; + } + + const auto on_signal = [&]( auto const& f_new ) { + auto value2 = recursive_ref( res, res.get_node( f_new ) ); + recursive_deref( res, res.get_node( f_new ) ); + int32_t gain = value - value2; + + if ( ( gain > 0 || ( ps_.allow_zero_gain && gain == 0 ) ) && gain > best_gain ) + { + if constexpr ( has_level_v ) + { + if ( !ps_.preserve_depth || res.level( res.get_node( f_new ) ) <= ntk_.level( n ) ) + { + best_gain = gain; + best_signal = f_new; + } + } + else + { + best_gain = gain; + best_signal = f_new; + } + } + + return true; + }; + stopwatch<> t( st_.time_rewriting ); + rewriting_fn_( res, cuts.truth_table( *cut ), children.begin(), children.end(), on_signal ); + } + + if ( best_gain == -1 ) + { + std::vector> children( ntk_.fanin_size( n ) ); + ntk_.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = ntk_.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f]; + } ); + + old2new[n] = res.clone_node( ntk_, n, children ); + } + else + { + old2new[n] = best_signal; + } + } + + recursive_ref( res, res.get_node( old2new[n] ) ); + } ); + + /* create POs */ + ntk_.foreach_po( [&]( auto const& f ) { + res.create_po( ntk_.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f] ); + } ); + + NtkDest ret = cleanup_dangling( res ); + + /* new costs */ + return costs( ret ) > orig_cost ? static_cast( ntk_ ) : ret; + } + +private: + Ntk const& ntk_; + RewritingFn const& rewriting_fn_; + cut_rewriting_params const& ps_; + cut_rewriting_stats& st_; +}; + +} // namespace detail + +/*! \brief Cut rewriting algorithm. + * + * This algorithm enumerates cut of a network and then tries to rewrite the cut + * in terms of gates of the same network. The rewritten structures are added + * to the network, and if they lead to area improvement, will be used as new + * parts of the logic. + * + * The rewriting function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. There are + * some rewriting algorithms in the folder + * `mockturtle/algorithms/node_resynthesis`, since the resynthesis functions + * have the same signature. + * + * In contrast to node resynthesis, cut rewriting uses the same type for the + * input and output network. + * + * \param ntk Network + * \param rewriting_fn Rewriting function + * \param ps Rewriting params + * \param pst Rewriting statistics + */ +template> +Ntk cut_rewriting( Ntk const& ntk, RewritingFn const& rewriting_fn = {}, cut_rewriting_params const& ps = {}, cut_rewriting_stats* pst = nullptr ) +{ + cut_rewriting_stats st; + const auto result = [&]() { + if ( ps.preserve_depth ) + { + depth_view depth_ntk{ ntk }; + return detail::cut_rewriting_impl, RewritingFn, NodeCostFn>( depth_ntk, rewriting_fn, ps, st ).run(); + } + else + { + return detail::cut_rewriting_impl( ntk, rewriting_fn, ps, st ).run(); + } + }(); + + if ( ps.verbose ) + { + st.report( false ); + } + if ( pst ) + { + *pst = st; + } + + ntk.clear_values(); + return result; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/decomposition.hpp b/third-party/mockturtle/include/mockturtle/algorithms/decomposition.hpp new file mode 100644 index 00000000000..541de0b0e22 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/decomposition.hpp @@ -0,0 +1,301 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file decomposition.hpp + \brief Shannon and Davio decomposition + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../traits.hpp" +#include "node_resynthesis/null.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class shannon_decomposition_impl +{ +public: + shannon_decomposition_impl( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn ) + : ntk_( ntk ), + func_( func ), + vars_( vars ), + pis_( children ), + resyn_( resyn ) + { + cache_.insert( { func.construct(), ntk_.get_constant( false ) } ); + + for ( auto i = 0u; i < pis_.size(); ++i ) + { + auto var = func.construct(); + kitty::create_nth_var( var, i ); + cache_.insert( { func.construct(), pis_[i] } ); + } + } + + signal run() + { + return decompose( 0u, func_ ); + } + +private: + signal decompose( uint32_t var_index, kitty::dynamic_truth_table const& func ) + { + /* cache lookup... */ + auto it = cache_.find( func ); + if ( it != cache_.end() ) + { + return it->second; + } + + /* ...and for the complement */ + it = cache_.find( ~func ); + if ( it != cache_.end() ) + { + return ntk_.create_not( it->second ); + } + + signal f; + if ( var_index == vars_.size() ) + { + auto copy = func; + const auto support = kitty::min_base_inplace( copy ); + const auto small_func = kitty::shrink_to( copy, static_cast( support.size() ) ); + std::vector> small_pis( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + small_pis[i] = pis_[support[i]]; + } + resyn_( ntk_, small_func, small_pis.begin(), small_pis.end(), [&]( auto const& _f ) { + f = _f; + return false; + } ); + } + else + { + /* decompose */ + const auto f0 = decompose( var_index + 1, kitty::cofactor0( func, vars_[var_index] ) ); + const auto f1 = decompose( var_index + 1, kitty::cofactor1( func, vars_[var_index] ) ); + f = ntk_.create_ite( pis_[vars_[var_index]], f1, f0 ); + } + cache_.insert( { func, f } ); + return f; + } + +private: + Ntk& ntk_; + kitty::dynamic_truth_table func_; + std::vector vars_; + std::vector> const& pis_; + SynthesisFn const& resyn_; + std::unordered_map, kitty::hash> cache_; +}; + +template +class davio_decomposition_impl +{ +public: + davio_decomposition_impl( Ntk& ntk, bool polarity, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn ) + : ntk_( ntk ), + polarity_( polarity ), + func_( func ), + pis_( children ), + vars_( vars ), + resyn_( resyn ) + { + cache_.insert( { func.construct(), ntk_.get_constant( false ) } ); + + for ( auto i = 0u; i < pis_.size(); ++i ) + { + auto var = func.construct(); + kitty::create_nth_var( var, i ); + cache_.insert( { func.construct(), pis_[i] } ); + } + } + + signal run() + { + return decompose( 0u, func_ ); + } + +private: + signal decompose( uint32_t var_index, kitty::dynamic_truth_table const& func ) + { + /* cache lookup... */ + auto it = cache_.find( func ); + if ( it != cache_.end() ) + { + return it->second; + } + + /* ...and for the complement */ + it = cache_.find( ~func ); + if ( it != cache_.end() ) + { + return ntk_.create_not( it->second ); + } + + signal f; + if ( var_index == vars_.size() ) + { + auto copy = func; + const auto support = kitty::min_base_inplace( copy ); + const auto small_func = kitty::shrink_to( copy, static_cast( support.size() ) ); + std::vector> small_pis( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + small_pis[i] = pis_[support[i]]; + } + resyn_( ntk_, small_func, small_pis.begin(), small_pis.end(), [&]( auto const& _f ) { + f = _f; + return false; + } ); + } + else + { + /* decompose */ + const auto f0 = decompose( var_index + 1, kitty::cofactor0( func, vars_[var_index] ) ); + const auto f1 = decompose( var_index + 1, kitty::cofactor1( func, vars_[var_index] ) ); + + if ( polarity_ ) + { + f = ntk_.create_xor( f0, ntk_.create_and( pis_[vars_[var_index]], ntk_.create_xor( f0, f1 ) ) ); + } + else + { + f = ntk_.create_xor( f1, ntk_.create_and( ntk_.create_not( pis_[vars_[var_index]] ), ntk_.create_xor( f0, f1 ) ) ); + } + } + cache_.insert( { func, f } ); + return f; + } + +private: + Ntk& ntk_; + bool polarity_; + kitty::dynamic_truth_table func_; + std::vector> const& pis_; + std::vector const& vars_; + SynthesisFn const& resyn_; + std::unordered_map, kitty::hash> cache_; +}; + +} // namespace detail + +/*! \brief Shannon decomposition + * + * This function applies Shannon decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_ite` + * - `get_constant` + */ +template> +signal shannon_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::shannon_decomposition_impl impl( ntk, func, vars, children, resyn ); + return impl.run(); +} + +/*! \brief Positive Davio decomposition + * + * This function applies positive Davio decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_xor` + * - `get_constant` + */ +template> +signal positive_davio_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_ite method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::davio_decomposition_impl impl( ntk, true, func, vars, children, resyn ); + return impl.run(); +} + +/*! \brief Negative Davio decomposition + * + * This function applies positive Davio decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_xor` + * - `get_constant` + */ +template> +signal negative_davio_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_ite method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::davio_decomposition_impl impl( ntk, false, func, vars, children, resyn ); + return impl.run(); +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/detail/database_generator.hpp b/third-party/mockturtle/include/mockturtle/algorithms/detail/database_generator.hpp new file mode 100644 index 00000000000..49414c0cd60 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/detail/database_generator.hpp @@ -0,0 +1,96 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file database_generator.hpp + \brief Utility class to generate a database in form of a logic + network from functions. + + \author Heinz Riener +*/ + +#pragma once + +#include +#include + +#include + +namespace mockturtle::detail +{ + +struct database_generator_params +{ + uint32_t num_vars{ 4u }; + bool multiple_candidates{ false }; + bool verbose{ false }; +}; /* database_generator_param */ + +template +class database_generator +{ +public: + using signal = mockturtle::signal; + +public: + explicit database_generator( Ntk& ntk, ResynFn const& resyn, database_generator_params const& ps ) + : ntk( ntk ), resyn( resyn ), ps( ps ) + { + for ( auto i = 0u; i < ps.num_vars; ++i ) + { + pis.emplace_back( ntk.create_pi() ); + } + } + + void add_function( kitty::dynamic_truth_table tt ) + { + /* normalize the function first if necessary */ + if ( !kitty::is_normal( tt ) ) + { + tt = ~tt; + } + + /* resynthesize the function and add it to the database */ + resyn( ntk, tt, std::begin( pis ), std::end( pis ), + [&]( const signal& s ) { + if ( ps.verbose ) + { + std::cout << "[i] function: "; + kitty::print_binary( tt ); + std::cout << " stored at PO #" << ntk.num_pos() << std::endl; + } + ntk.create_po( s ); + return ps.multiple_candidates; + } ); + } + + Ntk& ntk; + ResynFn const& resyn; + database_generator_params const& ps; + + std::vector pis; +}; /* database_generator */ + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/detail/mffc_utils.hpp b/third-party/mockturtle/include/mockturtle/algorithms/detail/mffc_utils.hpp new file mode 100644 index 00000000000..94da0450d94 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/detail/mffc_utils.hpp @@ -0,0 +1,135 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mffc_utils.hpp + \brief Utility functions for DAG-aware reference counting and + MFFC-size computation + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../../traits.hpp" +#include "../../utils/cost_functions.hpp" + +namespace mockturtle::detail +{ + +template +void initialize_values_with_fanout( Ntk& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + ntk.clear_values(); + ntk.foreach_node( [&]( auto const& n ) { + ntk.set_value( n, ntk.fanout_size( n ) ); + } ); +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n, TermCond const& terminate ) +{ + /* terminate? */ + if ( terminate( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}( ntk, n ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_value( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref( ntk, ntk.get_node( s ), terminate ); + } + } ); + return value; +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n, TermCond const& terminate ) +{ + /* terminate? */ + if ( terminate( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}( ntk, n ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.incr_value( ntk.get_node( s ) ) == 0 ) + { + value += recursive_ref( ntk, ntk.get_node( s ), terminate ); + } + } ); + return value; +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n, LeavesIterator begin, LeavesIterator end ) +{ + const auto terminate = [&]( auto const& n ) { return std::find( begin, end, n ) != end; }; + return recursive_deref( ntk, n, terminate ); +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n, LeavesIterator begin, LeavesIterator end ) +{ + const auto terminate = [&]( auto const& n ) { return std::find( begin, end, n ) != end; }; + return recursive_ref( ntk, n, terminate ); +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n ) +{ + const auto terminate = [&]( auto const& n ) { return ntk.is_constant( n ) || ntk.is_pi( n ); }; + return recursive_deref( ntk, n, terminate ); +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n ) +{ + const auto terminate = [&]( auto const& n ) { return ntk.is_constant( n ) || ntk.is_pi( n ); }; + return recursive_ref( ntk, n, terminate ); +} + +template> +uint32_t mffc_size( Ntk const& ntk, node const& n ) +{ + auto v1 = recursive_deref( ntk, n ); + auto v2 = recursive_ref( ntk, n ); + assert( v1 == v2 ); + (void)v2; + return v1; +} + +} /* namespace mockturtle::detail */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/detail/minmc_xags.hpp b/third-party/mockturtle/include/mockturtle/algorithms/detail/minmc_xags.hpp new file mode 100644 index 00000000000..457556412e9 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/detail/minmc_xags.hpp @@ -0,0 +1,198 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file minmc_xags.hpp + \brief Optimum MC XAGs up to 5 variables + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +namespace mockturtle::detail +{ + +// These circuits are based on Cryptology ePrint Archive: Report 2015/848 +// Meltem Sönmez Turan and Rene Péralta: +// The Multiplicative Complexity of Boolean Functions on Four and Five Variables + +// These circuits have been resynthesized to match the affine representative +// in kitty::hybrid_exact_spectral_canonization + +// The original (and partly revised) networks are kept in the legacy namespace +// below + +// clang-format off +static std::vector, std::string>>> minmc_xags = { + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}, + {1, 0x8, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}}, + {{0, 0x00, {1 << 8 | 0, 0}, "0"}, + {2, 0x88, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {1, 0x80, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}}, + {{0, 0x0000, {1 << 8 | 0, 0}, "0"}, + {1, 0x8000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + {2, 0x8080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + {3, 0x0888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {4, 0x8888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {5, 0x2a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {6, 0xf888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {7, 0x7888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}}, + {{ 0, 0x00000000, {1 << 8 | 0, 0}, "0"}, + { 1, 0x80000000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18}, "(abcde)"}, + { 2, 0x80008000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + { 3, 0x00808080, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 14, 20}, "[(abcde)(abc)]"}, + { 4, 0x80808080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + { 5, 0x08888000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 14, 10, 12, 16, 18}, "[(abcd)(abe)]"}, + { 6, 0xaa2a2a80, {7 << 16 | 1 << 8 | 5, 4, 6, 12, 8, 2, 14, 8, 16, 18, 2, 10, 20, 22, 16, 24}, "[(abc)(ad)(ae)(ade)(abcde)]"}, + { 7, 0x88080808, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(abc)(ab)]"}, + { 8, 0x2888a000, {4 << 16 | 1 << 8 | 5, 4, 10, 6, 8, 14, 12, 2, 16, 18}, "[(acd)(abe)]"}, + { 9, 0xf7788000, {8 << 16 | 1 << 8 | 5, 6, 8, 2, 4, 12, 10, 8, 6, 18, 10, 20, 14, 16, 22, 24, 10, 26}, "[(abcd)(abe)(ce)(de)(cde)]"}, + {10, 0xa8202020, {5 << 16 | 1 << 8 | 5, 8, 10, 12, 6, 4, 14, 16, 6, 2, 18, 20}, "[(ac)(abc)(abde)]"}, + {11, 0x08880888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {12, 0xbd686868, {8 << 16 | 1 << 8 | 5, 8, 10, 12, 4, 2, 14, 4, 6, 16, 6, 18, 2, 20, 22, 24, 12, 26}, "[(ab)(ac)(bc)(abc)(de)(ade)(abcde)]"}, + {13, 0xaa808080, {6 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 14, 12, 18, 16, 2, 20, 22}, "[(abcde)(abc)(ade)]"}, + {14, 0x7e686868, {9 << 16 | 1 << 8 | 5, 8, 10, 12, 4, 12, 2, 14, 16, 2, 6, 18, 6, 20, 14, 22, 24, 26, 12, 28}, "[(ab)(ac)(bc)(abc)(ade)(bde)(cde)(abcde)]"}, + {15, 0x2208a208, {7 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 10, 14, 14, 8, 18, 12, 16, 2, 20, 22, 24}, "[(ab)(abc)(ad)(abcde)]"}, + {16, 0x08888888, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20}, "[(abcde)(ab)]"}, + {17, 0x88888888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {18, 0xea404040, {5 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 18, 12, 20}, "[(abc)(ade)(bc)]"}, + {19, 0x2a802a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {20, 0x73d28c88, {9 << 16 | 1 << 8 | 5, 2, 4, 10, 4, 14, 12, 8, 16, 18, 10, 6, 2, 20, 22, 18, 12, 26, 24, 28}, "[(ab)(bd)(abd)(bcd)(abcd)(ae)(ce)(de)(ade)(cde)]"}, + {21, 0xea808080, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 2, 14, 18, 20, 2, 22}, "[(bcde)(abc)(ade)]"}, + {22, 0xa28280a0, {6 << 16 | 1 << 8 | 5, 6, 8, 12, 10, 4, 2, 14, 16, 18, 6, 2, 20, 22}, "[(ac)(acd)(abcd)(ae)(abe)]"}, + {23, 0x13284c88, {8 << 16 | 1 << 8 | 5, 2, 6, 12, 2, 4, 14, 12, 8, 16, 10, 20, 4, 18, 22, 24, 16, 26}, "[(ab)(bd)(abd)(abcd)(ace)(de)]"}, + {24, 0xa2220888, {5 << 16 | 1 << 8 | 5, 6, 8, 4, 12, 14, 4, 16, 10, 2, 18, 20}, "[(ab)(abcd)(ae)]"}, + {25, 0xaae6da80, {12 << 16 | 1 << 8 | 5, 4, 6, 12, 10, 4, 2, 14, 16, 18, 12, 20, 6, 10, 22, 24, 6, 26, 2, 8, 28, 30, 18, 32, 12, 34}, "[(abc)(ad)(cd)(ae)(be)(ade)(bde)(cde)(abcde)]"}, + {26, 0x58d87888, {9 << 16 | 1 << 8 | 5, 2, 4, 8, 2, 6, 14, 16, 6, 10, 18, 12, 6, 20, 2, 22, 24, 26, 16, 28}, "[(ab)(cd)(ce)(ace)(cde)(abcde)]"}, + {27, 0x8c88ac28, {8 << 16 | 1 << 8 | 5, 6, 10, 12, 4, 2, 14, 4, 8, 16, 6, 18, 2, 20, 22, 24, 18, 26}, "[(ab)(ac)(bd)(abd)(bcd)(ace)(abcde)]"}, + {28, 0x8880f880, {7 << 16 | 1 << 8 | 5, 2, 4, 12, 6, 8, 14, 10, 16, 18, 12, 6, 20, 22, 16, 24}, "[(abc)(abd)(cd)(cde)(abcde)]"}, + {29, 0x9ee8e888, {11 << 16 | 1 << 8 | 5, 2, 4, 12, 6, 14, 8, 10, 16, 6, 8, 20, 18, 10, 2, 24, 4, 26, 18, 22, 28, 30, 12, 32}, "[(ab)(acd)(bcd)(ace)(bce)(ade)(bde)(cde)(abcde)]"}, + {30, 0x4268c268, {9 << 16 | 1 << 8 | 5, 4, 6, 10, 12, 8, 4, 16, 12, 14, 2, 18, 20, 22, 6, 2, 24, 26, 12, 28}, "[(ab)(ac)(bc)(abc)(ad)(abcde)]"}, + {31, 0x16704c80, {7 << 16 | 1 << 8 | 5, 2, 10, 12, 4, 8, 14, 2, 4, 18, 10, 6, 20, 22, 16, 24}, "[(abc)(bd)(ce)(ade)]"}, + {32, 0x78888888, {4 << 16 | 1 << 8 | 5, 6, 8, 10, 12, 2, 4, 16, 14, 18}, "[(ab)(cde)]"}, + {33, 0x4966bac0, {10 << 16 | 1 << 8 | 5, 2, 8, 4, 6, 12, 10, 14, 2, 16, 18, 10, 6, 12, 8, 24, 4, 22, 26, 28, 20, 30}, "[(bc)(ad)(cd)(acd)(abcd)(ae)(be)(bce)(de)(ade)]"}, + {34, 0x372840a0, {9 << 16 | 1 << 8 | 5, 2, 4, 12, 2, 6, 14, 4, 6, 18, 16, 20, 10, 12, 8, 22, 24, 26, 16, 28}, "[(ac)(acd)(bcd)(abcd)(abe)(de)]"}, + {35, 0x5208d288, {7 << 16 | 1 << 8 | 5, 2, 4, 10, 12, 14, 8, 6, 2, 16, 18, 14, 12, 22, 20, 24}, "[(ab)(ad)(cd)(abce)]"}, + {36, 0x7ca00428, {10 << 16 | 1 << 8 | 5, 8, 2, 4, 12, 10, 14, 10, 2, 18, 14, 8, 20, 6, 4, 22, 2, 24, 26, 28, 16, 30}, "[(ab)(ac)(bd)(acd)(bcd)(abcd)(abe)(cde)]"}, + {37, 0xf8880888, {5 << 16 | 1 << 8 | 5, 2, 4, 12, 10, 6, 14, 8, 16, 18, 12, 20}, "[(ab)(abcd)(cde)]"}, + {38, 0x2ec0ae40, {8 << 16 | 1 << 8 | 5, 8, 6, 4, 12, 14, 8, 2, 16, 10, 18, 20, 14, 4, 22, 24, 18, 26}, "[(bc)(abc)(ad)(bd)(abd)(abce)]"}, + {39, 0xf888f888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {40, 0x58362ec0, {11 << 16 | 1 << 8 | 5, 6, 10, 10, 8, 14, 12, 4, 2, 16, 18, 20, 8, 2, 22, 24, 6, 12, 4, 26, 28, 30, 20, 32}, "[(bc)(ad)(bd)(abd)(ae)(be)(ce)(ace)(bce)(abcde)]"}, + {41, 0x0eb8f6c0, {13 << 16 | 1 << 8 | 5, 6, 10, 2, 8, 14, 10, 12, 2, 16, 18, 14, 4, 22, 8, 20, 4, 12, 6, 28, 26, 24, 30, 32, 12, 34, 22, 36}, "[(bc)(ad)(bd)(cd)(acd)(abe)(ce)(abcde)]"}, + {42, 0x567cea40, {10 << 16 | 1 << 8 | 5, 2, 8, 10, 12, 14, 4, 16, 2, 6, 18, 20, 10, 6, 4, 22, 24, 20, 12, 28, 26, 30}, "[(bc)(abc)(ad)(be)(ce)(abcde)]"}, + {43, 0xf8887888, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(ab)(cd)]"}, + {44, 0x78887888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}, + {45, 0xe72890a0, {8 << 16 | 1 << 8 | 5, 4, 10, 12, 6, 2, 14, 8, 4, 6, 18, 20, 10, 8, 22, 24, 16, 26}, "[(ac)(cd)(bcd)(abe)(de)]"}, + {46, 0x268cea40, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 8, 2, 14, 4, 10, 16, 12, 20, 18, 22}, "[(bc)(abc)(ad)(be)]"}, + {47, 0x6248eac0, {7 << 16 | 1 << 8 | 5, 2, 8, 6, 12, 14, 10, 2, 16, 18, 6, 4, 20, 22, 12, 24}, "[(bc)(ad)(abcd)(abe)]"}} +}; +// clang-format on + +namespace legacy +{ + +// These circuits are based on Cryptology ePrint Archive: Report 2015/848 +// Meltem Sönmez Turan and Rene Péralta: +// The Multiplicative Complexity of Boolean Functions on Four and Five Variables + +// clang-format off +static std::vector, std::string>>> minmc_xags = { + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}, + {1, 0x8, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}}, + {{0, 0x00, {1 << 8 | 0, 0}, "0"}, + {2, 0x88, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {1, 0x80, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}}, + {{0, 0x0000, {1 << 8 | 0, 0}, "0"}, + {1, 0x8000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + {2, 0x8080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + {3, 0x0888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {4, 0x8888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {5, 0x2a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {6, 0xf888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {7, 0x7888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}}, + {{ 0, 0x00000000, {1 << 8 | 0, 0}, "0"}, + { 1, 0x80000000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18}, "(abcde)"}, + { 2, 0x80008000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + { 3, 0x00808080, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 14, 20}, "[(abcde)(abc)]"}, + { 4, 0x80808080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + { 5, 0x40808080, {4 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 2, 12, 16, 18}, "[(bcde)(abc)]"}, + { 6, 0x22080808, {7 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 14, 12, 18, 4, 20, 16, 2, 22, 24}, "[(abcde)(abc)(ade)(ab)]"}, + { 7, 0x88080808, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(abc)(ab)]"}, + { 8, 0x2a808080, {4 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 18}, "[(abc)(ade)]"}, + { 9, 0x15808080, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 2, 14, 18, 20, 18, 22}, "[(bcde)(abc)(ade)(de)]"}, + {10, 0xc8080808, {5 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 14, 16, 2, 4, 18, 20}, "[(bcde)(ab)(abc)]"}, + {11, 0x08880888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {12, 0x95404040, {8 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 16, 12, 18, 14, 2, 20, 22, 12, 24, 14, 26}, "[(abcde)(abc)(ade)(de)(bc)]"}, + {13, 0xaa808080, {6 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 14, 12, 18, 16, 2, 20, 22}, "[(abcde)(abc)(ade)]"}, + {14, 0x6a404040, {7 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 16, 12, 18, 14, 2, 20, 22, 12, 24}, "[(abcde)(abc)(ade)(bc)]"}, + {15, 0xaa802a80, {6 << 16 | 1 << 8 | 5, 4, 6, 2, 8, 10, 14, 16, 2, 12, 18, 20, 14, 22}, "[(abcde)(abc)(ad)]"}, + {16, 0x08888888, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20}, "[(abcde)(ab)]"}, + {17, 0x88888888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {18, 0xea404040, {5 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 18, 12, 20}, "[(abc)(ade)(bc)]"}, + {19, 0x2a802a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {20, 0x37080808, {6 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 14, 16, 2, 4, 18, 20, 12, 22}, "[(bcde)(abc)(ab)(de)]"}, + {21, 0xea808080, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 2, 14, 18, 20, 2, 22}, "[(bcde)(abc)(ade)]"}, + {22, 0x8c804c80, {5 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 14, 16, 8, 4, 18, 20}, "[(bcde)(bd)(abc)]"}, + {23, 0xea802a80, {8 << 16 | 1 << 8 | 5, 6, 4, 6, 12, 14, 6, 16, 8, 10, 16, 20, 2, 19, 22, 24, 2, 26}, "[(bcde)(abc)(ad)]"}, + {24, 0x7f008000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 14, 10, 8, 16, 18}, "[(abcd)(de)]"}, + {25, 0x96704c80, {12 << 16 | 1 << 8 | 5, 10, 8, 12, 2, 10, 14, 16, 6, 6, 4, 8, 20, 22, 2, 4, 24, 26, 12, 18, 28, 22, 16, 32, 30, 34}, "[(abcde)(abc)(ade)(ce)(bd)]"}, + {26, 0x77080808, {7 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 10, 14, 16, 18, 12, 20, 14, 22, 16, 24}, "[(abcde)(abc)(ab)(de)]"}, + {27, 0x66804c80, {8 << 16 | 1 << 8 | 5, 6, 4, 12, 2, 6, 14, 16, 8, 2, 8, 10, 20, 22, 4, 18, 24, 26}, "[(abcde)(abc)(ade)(bd)]"}, + {28, 0xa6408c40, {9 << 16 | 1 << 8 | 5, 4, 6, 8, 6, 14, 2, 2, 10, 18, 4, 8, 20, 22, 16, 12, 24, 26, 22, 28}, "[(abcde)(abc)(ade)(bd)(bc)]"}, + {29, 0xff808080, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 10, 14, 16, 18, 14, 20, 16, 22}, "[(abcde)(abc)(de)]"}, + {30, 0x7808f808, {7 << 16 | 1 << 8 | 5, 2, 4, 8, 10, 12, 14, 16, 12, 18, 8, 6, 20, 22, 12, 24}, "[(abcde)(abc)(ab)(cd)]"}, + {31, 0xe6804c80, {6 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 4, 8, 20, 18, 22}, "[(abc)(ade)(bd)]"}, + {32, 0x7f808080, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 10, 16, 14, 18}, "[(abc)(de)]"}, + {33, 0xd6704c80, {10 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 12, 14, 18, 10, 4, 8, 6, 22, 24, 26, 16, 28, 20, 30}, "[(bcde)(abc)(ade)(ce)(bd)]"}, + {34, 0x1a702a80, {7 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 4, 14, 16, 10, 6, 18, 2, 8, 22, 20, 24}, "[(bcde)(abc)(ad)(ce)]"}, + {35, 0xb8887888, {5 << 16 | 1 << 8 | 5, 6, 8, 10, 12, 14, 2, 4, 16, 18, 12, 20}, "[(bcde)(ab)(cd)]"}, + {36, 0xd9804c80, {7 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 4, 6, 16, 12, 14, 18, 4, 8, 22, 20, 24}, "[(bcde)(abc)(ade)(bd)(de)]"}, + {37, 0xbf808080, {5 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 2, 12, 16, 18, 14, 20}, "[(bcde)(abc)(de)]"}, + {38, 0x3808f808, {7 << 16 | 1 << 8 | 5, 4, 8, 10, 12, 2, 4, 16, 14, 18, 8, 6, 20, 22, 16, 24}, "[(bcde)(abc)(ab)(cd)]"}, + {39, 0xf888f888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {40, 0xa9b08c40, {15 << 16 | 1 << 8 | 5, 8, 4, 6, 4, 10, 4, 14, 16, 18, 12, 16, 2, 10, 8, 24, 14, 26, 2, 2, 8, 30, 14, 28, 32, 34, 22, 20, 36, 38, 30, 40}, "[(abcde)(abc)(ade)(de)(ce)(bd)(bc)]"}, + {41, 0x56b08c40, {13 << 16 | 1 << 8 | 5, 8, 2, 2, 12, 14, 4, 10, 8, 6, 2, 6, 4, 22, 2, 10, 24, 26, 20, 6, 28, 30, 18, 16, 32, 34, 26, 36}, "[(abcde)(abc)(ade)(ce)(bd)(bc)]"}, + {42, 0x664c2a80, {11 << 16 | 1 << 8 | 5, 8, 2, 6, 12, 14, 8, 8, 6, 18, 4, 4, 10, 22, 6, 20, 24, 26, 2, 16, 28, 30, 22, 32}, "[(abcde)(abc)(ad)(be)]"}, + {43, 0xf8887888, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(ab)(cd)]"}, + {44, 0x78887888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}, + {45, 0xd6b08c40, {8 << 16 | 1 << 8 | 5, 2, 10, 12, 4, 6, 2, 6, 16, 18, 8, 14, 20, 6, 10, 24, 22, 26}, "[(abc)(ade)(bc)(bd)(ce)]"}, + {46, 0xe64c2a80, {5 << 16 | 1 << 8 | 5, 4, 6, 12, 8, 2, 14, 4, 10, 18, 16, 20}, "[(abc)(ad)(be)]"}, + {47, 0x7c704c80, {10 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 4, 16, 2, 18, 12, 14, 20, 22, 8, 4, 24, 6, 10, 28, 26, 30}, "[(bcde)(abc)(bd)(ce)]"}} +}; +// clang-format on + +} // namespace legacy + +} /* namespace mockturtle::detail */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/detail/resub_utils.hpp b/third-party/mockturtle/include/mockturtle/algorithms/detail/resub_utils.hpp new file mode 100644 index 00000000000..9db62ab8829 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/detail/resub_utils.hpp @@ -0,0 +1,782 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file resub_utils.hpp + \brief Utility classes for the resubstitution framework + + class `node_mffc_inside`, class `window_simulator` (originally `simulator`), + and class `default_resub_functor` moved from resubstitution.hpp + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace mockturtle::experimental::detail +{ + +struct divisor_collector_params +{ + /*! \brief Maximum number of nodes to be collected in the transitive fanin cone. */ + uint32_t max_num_tfi{ std::numeric_limits::max() }; + + /*! \brief Maximum number of nodes to collect (in total). */ + uint32_t max_num_collect{ std::numeric_limits::max() }; + + /*! \brief Maximum fanout size when considering a node in the "wings". */ + uint32_t max_fanouts{ std::numeric_limits::max() }; + + /*! \brief Maximum level when considering a node in the "wings". + * + * The network should be wrapped with `depth_view` for this parameter + * to be in effect. + */ + uint32_t max_level{ std::numeric_limits::max() }; +}; + +/*! \brief Implements helper functions for collecting divisors/supported nodes. */ +template +class divisor_collector +{ +public: + using node = typename Ntk::node; + + divisor_collector( Ntk const& ntk, divisor_collector_params ps = {} ) + : ntk( ntk ), ps( ps ) + { + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method (please wrap with fanout_view)" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + assert( ps.max_num_collect >= ps.max_num_tfi ); + } + + void set_max_level( uint32_t max_level ) + { + ps.max_level = max_level; + } + + /*! \brief Collect nodes in the transitive fanin cone of root until leaves in a topological order. + * + * `root` itself is included and will be the last element. + * Constant node(s) is not collected. + * Leaves are not collected. + * + * \param root The root node + * \param leaves The leaf nodes forming a cut supporting the root node + * \param tfi The container where TFI nodes are collected + */ + void collect_tfi( node const& root, std::vector const& leaves, std::vector& tfi ) + { + ntk.incr_trav_id(); + for ( const auto& l : leaves ) + { + ntk.set_visited( l, ntk.trav_id() ); + } + collect_tfi_rec( root, tfi ); + } + + /*! \brief Collect nodes in the TFI (BFS) and wings. + * + * Constant node(s) is not collected. + * Any node in the TFO of root is not collected. + * Collects TFI nodes with breadth-first search until PIs (unbounded) + * or until the limit on `max_num_tfi` exceeds, and then collects "wings" + * nodes until the limit on `max_num_collect` exceeds. + * Collected nodes are in a topological order, with `root` itself being the last element. + * + * \param root The root node + * \param collected The container where TFI and "wings" nodes are collected + */ + void collect_tfi_and_wings( node const& root, std::vector& collected ) + { + collect_tfi_bfs( root, collected ); + std::reverse( collected.begin(), collected.end() ); /* now in topo order */ + collected.pop_back(); /* remove `root` */ + + /* note: we cannot use range-based loop here because we push to the vector in the loop */ + for ( auto i = 0u; i < collected.size(); ++i ) + { + if ( !collect_wings( root, collected.at( i ), collected ) ) + { + break; + } + } + + collected.emplace_back( root ); + return; + } + + /*! \brief Collect all nodes that are supported by the leaves until the root (TFI + wings). + * + * Constant node(s) is not collected. + * Leaves are not collected. + * Any node in the TFO of root is not collected. + * Collected nodes are in a topological order, with `root` itself being the last element. + * + * \param root The root node + * \param leaves The leaf nodes forming a cut supporting the root node + * \param supported The container where supported nodes are collected + */ + void collect_supported_nodes( node const& root, std::vector const& leaves, std::vector& supported ) + { + /* collect TFI nodes until (excluding) leaves */ + collect_tfi( root, leaves, supported ); + supported.pop_back(); /* remove `root` */ + + if ( supported.size() > ps.max_num_tfi ) + { + return; + } + + /* collect "wings" */ + for ( auto const& l : leaves ) + { + if ( !collect_wings( root, l, supported ) ) + { + supported.emplace_back( root ); + return; + } + } + + /* note: we cannot use range-based loop here because we push to the vector in the loop */ + for ( auto i = 0u; i < supported.size(); ++i ) + { + if ( !collect_wings( root, supported.at( i ), supported ) ) + { + break; + } + } + + supported.emplace_back( root ); + return; + } + +private: + void collect_tfi_rec( node const& n, std::vector& tfi ) + { + /* collect until leaves and skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + /* collect in topological order -- lower nodes first */ + ntk.foreach_fanin( n, [&]( const auto& f ) { + collect_tfi_rec( ntk.get_node( f ), tfi ); + } ); + + if ( !ntk.is_constant( n ) ) + { + tfi.emplace_back( n ); + } + } + + /*! \brief Collect nodes in the transitive fanin cone of root with breadth-first search. + * + * `root` itself is included and will be the first element. + * Constant node(s) is not collected. + * Collects until PIs (unbounded) or until the limit on `max_num_tfi` exceeds. + * Collected nodes are NOT in a topological order. + * + * \param root The root node + * \param tfi The container where TFI nodes are collected + */ + void collect_tfi_bfs( node const& root, std::vector& tfi ) + { + ntk.incr_trav_id(); + assert( tfi.size() == 0 ); + tfi.reserve( ps.max_num_tfi ); + tfi.emplace_back( root ); + ntk.set_visited( root, ntk.trav_id() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); /* don't collect constant node */ + uint32_t i{ 0 }; + while ( i < tfi.size() && tfi.size() < ps.max_num_tfi ) + { + node const& n = tfi.at( i++ ); + ntk.foreach_fanin( n, [&]( const auto& f ) { + node const& ni = ntk.get_node( f ); + if ( ntk.visited( ni ) != ntk.trav_id() ) + { + tfi.emplace_back( ni ); + ntk.set_visited( ni, ntk.trav_id() ); + } + } ); + } + } + + /*\return Whether to continue collecting */ + bool collect_wings( node const& root, node const& n, std::vector& supported ) + { + if ( ntk.fanout_size( n ) > ps.max_fanouts ) + { + return true; + } + + /* if the fanout has all fanins in the set, add it */ + ntk.foreach_fanout( n, [&]( node const& p ) { + if ( ntk.visited( p ) == ntk.trav_id() ) + { + return true; /* next fanout */ + } + + if constexpr ( has_level_v ) + { + if ( ntk.level( p ) > ps.max_level ) + { + return true; /* next fanout */ + } + } + + bool all_fanins_visited = true; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.visited( ntk.get_node( g ) ) != ntk.trav_id() ) + { + all_fanins_visited = false; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( !all_fanins_visited ) + { + return true; /* next fanout */ + } + + bool has_root_as_child = false; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.get_node( g ) == root ) + { + has_root_as_child = true; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( has_root_as_child ) + { + return true; /* next fanout */ + } + + supported.emplace_back( p ); + ntk.set_visited( p, ntk.trav_id() ); + + /* quit fanout-loop if there are too many nodes collected */ + return supported.size() < ps.max_num_collect; + } ); + return supported.size() < ps.max_num_collect; + } + +private: + Ntk const& ntk; + divisor_collector_params ps; +}; /* divisor_collector */ + +template +class window_simulator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit window_simulator( Ntk const& ntk, std::vector& tts, uint32_t num_pis ) + : ntk( ntk ), tts( tts ), node_to_id( ntk ), num_pis( num_pis ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for TT" ); + assert( ntk.get_node( ntk.get_constant( false ) ) == ntk.get_node( ntk.get_constant( true ) ) && "network types whose constant nodes are different are not supported" ); + + tts.resize( num_pis + 1 ); + auto tt = kitty::create( num_pis ); + tts[0] = tt; + node_to_id[ntk.get_constant( false )] = 0; + + for ( auto i = 0u; i < num_pis; ++i ) + { + kitty::create_nth_var( tt, i ); + tts[i + 1] = tt; + } + } + + /*! \brief Simulates a window in a network. + * + * Every node in `nodes` must have all of its fanins either in `leaves`, or in + * `nodes` and precedes it (i.e., supported and in a topological order). + * + * After simulation, `tts` contains: + * - `tts[0]` is the constant-zero truth table + * - `tts[1]` to `tts[num_pis]` are the projection functions (primary inputs) + * - `tts[num_pis + 1 + i]` is the truth table for the node `nodes[i]` + */ + std::vector& simulate( std::vector const& leaves, std::vector const& nodes ) + { + node_to_id.resize(); + assert( leaves.size() <= num_pis ); + for ( auto i = 0u; i < leaves.size(); ++i ) + { + node_to_id[leaves[i]] = i + 1; + } + + tts.resize( num_pis + 1 ); + for ( auto const& n : nodes ) + { + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + assert( node_to_id.has( f ) && node_to_id[f] < tts.size() && "some node in `nodes` has a fanin outside of `nodes` and `leaves`, or `nodes` is not in a topological order" ); + fanin_values[i] = tts[node_to_id[f]]; + } ); + node_to_id[n] = tts.size(); + tts.emplace_back( ntk.compute( n, std::begin( fanin_values ), std::begin( fanin_values ) + ntk.fanin_size( n ) ) ); + } + + assert( tts.size() == num_pis + 1 + nodes.size() ); + return tts; + } + +private: + Ntk const& ntk; + std::vector& tts; + incomplete_node_map node_to_id; + std::array fanin_values; + uint32_t num_pis; +}; /* window_simulator */ + +} /* namespace mockturtle::experimental::detail */ + +namespace mockturtle::detail +{ + +/* based on abcRefs.c */ +template +class node_mffc_inside +{ +public: + using node = typename Ntk::node; + +public: + explicit node_mffc_inside( Ntk const& ntk ) + : ntk( ntk ) + { + static_assert( has_incr_fanout_size_v, "Ntk does not implement the incr_fanout_size method" ); + static_assert( has_decr_fanout_size_v, "Ntk does not implement the decr_fanout_size method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + template + int32_t call_on_mffc_and_count( node const& n, std::vector const& leaves, Fn&& fn ) + { + /* increment the fanout counters for the leaves */ + for ( const auto& l : leaves ) + ntk.incr_fanout_size( l ); + + /* dereference the node */ + auto count1 = node_deref_rec( n ); + + /* call `fn` on MFFC nodes */ + ntk.incr_trav_id(); + node_mffc_cone_rec( n, true, fn ); + + /* reference it back */ + auto count2 = node_ref_rec( n ); + (void)count2; + assert( count1 == count2 ); + + for ( const auto& l : leaves ) + ntk.decr_fanout_size( l ); + + return count1; + } + + int32_t run( node const& n, std::vector const& leaves, std::vector& inside ) + { + inside.clear(); + return call_on_mffc_and_count( n, leaves, [&inside]( node const& m ) { inside.emplace_back( m ); } ); + } + +private: + /* ! \brief Dereference the node's MFFC */ + int32_t node_deref_rec( node const& n ) + { + if ( ntk.is_pi( n ) ) + return 0; + + int32_t counter = 1; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + ntk.decr_fanout_size( p ); + if ( ntk.fanout_size( p ) == 0 ) + { + counter += node_deref_rec( p ); + } + } ); + + return counter; + } + + /* ! \brief Reference the node's MFFC */ + int32_t node_ref_rec( node const& n ) + { + if ( ntk.is_pi( n ) ) + return 0; + + int32_t counter = 1; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + auto v = ntk.fanout_size( p ); + ntk.incr_fanout_size( p ); + if ( v == 0 ) + { + counter += node_ref_rec( p ); + } + } ); + + return counter; + } + + template + void node_mffc_cone_rec( node const& n, bool top_most, Fn&& fn ) + { + /* skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + if ( !top_most && ( ntk.is_pi( n ) || ntk.fanout_size( n ) > 0 ) ) + { + return; + } + + /* recurse on children */ + ntk.foreach_fanin( n, [&]( const auto& f ) { + node_mffc_cone_rec( ntk.get_node( f ), false, fn ); + } ); + + /* collect the internal nodes */ + fn( n ); + } + +private: + Ntk const& ntk; +}; /* node_mffc_inside */ + +template +class window_simulator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using truthtable_t = TT; + + explicit window_simulator( Ntk const& ntk, uint32_t num_divisors, uint32_t max_pis ) + : ntk( ntk ), num_divisors( num_divisors ), tts( num_divisors + 1 ), node_to_index( ntk.size(), 0u ), phase( ntk.size(), false ) + { + auto tt = kitty::create( max_pis ); + tts[0] = tt; + + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + kitty::create_nth_var( tt, i ); + tts[i + 1] = tt; + } + } + + void resize() + { + if ( ntk.size() > node_to_index.size() ) + { + node_to_index.resize( ntk.size(), 0u ); + } + if ( ntk.size() > phase.size() ) + { + phase.resize( ntk.size(), false ); + } + } + + void assign( node const& n, uint32_t index ) + { + assert( n < node_to_index.size() ); + assert( index < num_divisors + 1 ); + node_to_index[n] = index; + } + + truthtable_t get_tt( signal const& s ) const + { + auto const tt = tts.at( node_to_index.at( ntk.get_node( s ) ) ); + return ntk.is_complemented( s ) ? ~tt : tt; + } + + void set_tt( uint32_t index, truthtable_t const& tt ) + { + tts[index] = tt; + } + + void normalize( std::vector const& nodes ) + { + for ( const auto& n : nodes ) + { + assert( n < phase.size() ); + assert( n < node_to_index.size() ); + + if ( n == 0 ) + { + return; + } + + auto& tt = tts[node_to_index.at( n )]; + if ( kitty::get_bit( tt, 0 ) ) + { + tt = ~tt; + phase[n] = true; + } + else + { + phase[n] = false; + } + } + } + + bool get_phase( node const& n ) const + { + assert( n < phase.size() ); + return phase.at( n ); + } + +private: + Ntk const& ntk; + uint32_t num_divisors; + + std::vector tts; + std::vector node_to_index; + std::vector phase; +}; /* window_simulator */ + +struct default_resub_functor_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: default_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts ) ); + } +}; + +/*! \brief A window-based resub functor which is basically doing functional reduction (fraig). */ +template +class default_resub_functor +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using stats = default_resub_functor_stats; + + explicit default_resub_functor( Ntk const& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, default_resub_functor_stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) const + { + /* The default resubstitution functor does not insert any gates + and consequently does not use the argument `max_inserts`. Other + functors, however, make use of this argument. */ + (void)care; + (void)max_inserts; + assert( kitty::is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + return std::nullopt; + } + +private: + std::optional resub_const( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( const auto& d : divs ) + { + if ( root == d ) + { + break; + } + + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + { + continue; /* next */ + } + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + +private: + Ntk const& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t num_divs; + stats& st; +}; /* default_resub_functor */ + +template +void update_node_level_once( Ntk& ntk, node const& n, bool first_node ) +{ + uint32_t curr_level = ntk.level( n ); + + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + + /* update only one more level */ + if ( first_node ) + { + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level_once( ntk, p, false ); + } ); + } + } +} + +/*! \brief Register an `on_modified` event that lazily updates node levels. + * + * This is a trick learnt from ABC's implementation and is used in + * enumeration-based resubstitution algorithms. It only updates the level of + * the modified node and its fanout nodes. The update is not propagated to + * the fanouts' fanouts, thus being fast but inaccurate. + * + * This method can be called in the constructor of an algorithm's implementation + * class. Note that its return value should be stored and + * `release_lazy_level_update_events` should then be called in the destructor + * of the class. + */ +template::modified_event_type>> +event_t register_lazy_level_update_events( Ntk& ntk ) +{ + static_assert( has_foreach_fanout_v, "Ntk does not have fanout interface." ); + using node = typename Ntk::node; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + ntk.resize_levels(); + update_node_level_once( ntk, n, true ); + }; + + return ntk.events().register_modified_event( update_level_of_existing_node ); +} + +template::modified_event_type>> +void release_lazy_level_update_events( Ntk& ntk, event_t& event ) +{ + ntk.events().release_modified_event( event ); +} + +} /* namespace mockturtle::detail */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/detail/switching_activity.hpp b/third-party/mockturtle/include/mockturtle/algorithms/detail/switching_activity.hpp new file mode 100644 index 00000000000..c5df29cba5a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/detail/switching_activity.hpp @@ -0,0 +1,70 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file switching_activity.hpp + \brief Utility to compute the switching activity + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include "../simulation.hpp" + +#include +#include + +namespace mockturtle::detail +{ + +/*! \brief Switching Activity. + * + * This function computes the switching activity for each node + * in the network by performing random simulation. + * + * \param ntk Network + * \param simulation_size Number of simulation bits + */ +template +std::vector switching_activity( Ntk const& ntk, unsigned simulation_size = 2048 ) +{ + std::vector sw_map( ntk.size() ); + partial_simulator sim( ntk.num_pis(), simulation_size ); + + auto tts = simulate_nodes( ntk, sim ); + + ntk.foreach_node( [&]( auto const& n ) { + float ones = static_cast( kitty::count_ones( tts[n] ) ); + float activity = 2.0 * ones / simulation_size * ( simulation_size - ones ) / simulation_size; + sw_map[ntk.node_to_index( n )] = activity; + } ); + + return sw_map; +} + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/dont_cares.hpp b/third-party/mockturtle/include/mockturtle/algorithms/dont_cares.hpp new file mode 100644 index 00000000000..0e03d54f327 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/dont_cares.hpp @@ -0,0 +1,358 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dont_cares.hpp + \brief Compute don't cares + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../algorithms/cnf.hpp" +#include "../algorithms/reconv_cut.hpp" +#include "../algorithms/simulation.hpp" +#include "../traits.hpp" +#include "../utils/include/percy.hpp" +#include "../utils/node_map.hpp" +#include "../views/color_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Computes satisfiability don't cares of a set of nodes. + * + * This function returns an under approximation of input assignments that + * cannot occur on a given set of nodes in a network. They may therefore be + * used as don't care conditions. + * + * \param ntk Network + * \param leaves Set of nodes + * \param max_tfi_inputs Maximum number of inputs in the transitive fanin. + */ +template +kitty::dynamic_truth_table satisfiability_dont_cares( Ntk const& ntk, std::vector> const& leaves, uint64_t max_tfi_inputs = 16u ) +{ + reconvergence_driven_cut_parameters ps; + ps.max_leaves = max_tfi_inputs; + reconvergence_driven_cut_statistics st; + + detail::reconvergence_driven_cut_impl cuts( ntk, ps, st ); + auto const extended_leaves = cuts.run( leaves ).first; + + fanout_view fanout_ntk{ ntk }; + fanout_ntk.clear_visited(); + color_view color_ntk{ fanout_ntk }; + + std::vector> gates{ collect_nodes( color_ntk, extended_leaves, leaves ) }; + window_view window_ntk{ color_ntk, extended_leaves, leaves, gates }; + + default_simulator sim( window_ntk.num_pis() ); + const auto tts = simulate_nodes( window_ntk, sim ); + + /* first create care and then invert */ + kitty::dynamic_truth_table care( static_cast( leaves.size() ) ); + for ( auto i = 0u; i < ( 1u << window_ntk.num_pis() ); ++i ) + { + uint32_t entry{ 0u }; + for ( auto j = 0u; j < leaves.size(); ++j ) + { + entry |= kitty::get_bit( tts[leaves[j]], i ) << j; + } + kitty::set_bit( care, entry ); + } + return ~care; +} + +/*! \brief Computes observability don't cares of a node. + * + * This function returns input assignments for which a change of the + * node's value cannot be observed at any of the roots. They may + * therefore be used as don't care conditions. + * + * \param ntk Network + * \param node A node in the ntk + * \param leaves Set of leave nodes + * \param roots Set of root nodes + */ +template +kitty::dynamic_truth_table observability_dont_cares( Ntk const& ntk, node const& n, std::vector> const& leaves, std::vector> const& roots ) +{ + fanout_view fanout_ntk{ ntk }; + fanout_ntk.clear_visited(); + color_view color_ntk{ fanout_ntk }; + + std::vector> gates{ collect_nodes( color_ntk, leaves, roots ) }; + window_view window_ntk{ color_ntk, leaves, roots, gates }; + + default_simulator sim( window_ntk.num_pis() ); + unordered_node_map node_to_value0( ntk ); + unordered_node_map node_to_value1( ntk ); + + node_to_value0[n] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + simulate_nodes( ntk, node_to_value0, sim ); + + node_to_value1[n] = ~sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + simulate_nodes( ntk, node_to_value1, sim ); + + kitty::dynamic_truth_table care( static_cast( leaves.size() ) ); + for ( const auto& r : roots ) + { + care |= node_to_value0[r] ^ node_to_value1[r]; + } + return ~care; +} + +namespace detail +{ + +template +void clearTFO_rec( Ntk const& ntk, Container& tts, node const& n, std::set>& roots, int level ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) /* visited */ + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + tts.erase( n ); + + if ( level == 0 ) + { + roots.insert( n ); + return; + } + + ntk.foreach_fanout( n, [&]( auto const& fo ) { + clearTFO_rec( ntk, tts, fo, roots, level - 1 ); + } ); +} + +template +void simulate_TFO_rec( Ntk const& ntk, node const& n, partial_simulator const& sim, Container& tts, int level ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) /* visited */ + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + if ( !tts.has( n ) ) + { + simulate_node( ntk, n, tts, sim ); + } + else + { + assert( tts[n].num_bits() == sim.num_bits() ); + } + + if ( level == 0 ) + { + return; + } + + ntk.foreach_fanout( n, [&]( auto const& fo ) { + simulate_TFO_rec( ntk, fo, sim, tts, level - 1 ); + } ); +} + +} /* namespace detail */ + +/*! \brief Compute the observability don't care patterns in a partial_simulator with respect to a node. + * + * A pattern is unobservable w.r.t. a node `n` if under this input assignment, + * replacing `n` with `!n` does not affect the value of any primary output or + * any leaf node of `levels` levels of transitive fanout cone. + * + * Return value: a `partial_truth_table` with the same length as `sim.num_bits()`. + * A `1` in it corresponds to an unobservable pattern. + * + * \param sim The `partial_simulator` containing the patterns to be tested. + * \param tts Stores the simulation signatures of each node. Can be empty or incomplete. + * \param levels Level of transitive fanout to consider. -1 = consider until PO. + */ +template> +kitty::partial_truth_table observability_dont_cares( Ntk const& ntk, node const& n, partial_simulator const& sim, Container& tts, int levels = -1 ) +{ + std::set> roots; + unordered_node_map tts_roots( ntk ); + + /* Make sure n is up-to-date and record its truth table. */ + if ( !tts.has( n ) || tts[n].num_bits() != sim.num_bits() ) + { + simulate_node( ntk, n, tts, sim ); + } + auto const tt_n = tts[n]; + + /* Clear (mark) TFO nodes and collect roots (leaves). */ + ntk.incr_trav_id(); + detail::clearTFO_rec( ntk, tts, n, roots, levels ); + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) /* PO is in TFO */ + { + roots.insert( ntk.get_node( f ) ); + } + } ); + + /* Simulate the negated version and collect TTs of roots. */ + tts[n] = ~tt_n; + ntk.incr_trav_id(); + detail::simulate_TFO_rec( ntk, n, sim, tts, levels ); + for ( const auto& r : roots ) + { + tts_roots[r] = tts[r]; + } + + /* Revert the negation and simulate again */ + tts[n] = tt_n; + ntk.incr_trav_id(); + detail::clearTFO_rec( ntk, tts, n, roots, levels ); + ntk.incr_trav_id(); + detail::simulate_TFO_rec( ntk, n, sim, tts, levels ); + + kitty::partial_truth_table care( sim.num_bits() ); + for ( const auto& r : roots ) + { + assert( tts[r].num_bits() == sim.num_bits() ); + care |= tts[r] ^ tts_roots[r]; + } + + if constexpr ( has_EXODC_interface_v ) + { + if ( levels == -1 ) + { + for ( auto i = 0u; i < sim.num_bits(); ++i ) + { + if ( kitty::get_bit( care, i ) ) + { + kitty::cube pat1, pat2; + ntk.foreach_po( [&]( auto const& f, auto po_index ) + { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) /* PO is in TFO */ + { + pat1.set_mask( po_index ); + pat2.set_mask( po_index ); + assert( tts[f].num_bits() == sim.num_bits() ); + assert( tts_roots[f].num_bits() == sim.num_bits() ); + if ( kitty::get_bit( tts[f], i ) ^ ntk.is_complemented( f ) ) + pat1.set_bit( po_index ); + if ( kitty::get_bit( tts_roots[f], i ) ^ ntk.is_complemented( f ) ) + pat2.set_bit( po_index ); + } + }); + + if ( ntk.are_observably_equivalent( pat1, pat2 ) ) + { + kitty::clear_bit( care, i ); + } + } + } + } + } + + return ~care; +} + +/*! \brief Check if a pattern is observable with respect to a node. + * + * A pattern is unobservable w.r.t. a node `n` if under this input assignment, + * replacing `n` with `!n` does not affect the value of any primary output or + * any leaf node of `levels` levels of transitive fanout cone. + * + * \param levels Level of transitive fanout to consider. -1 = consider until PO. + */ +template +bool pattern_is_observable( Ntk const& ntk, node const& n, std::vector const& pattern, int levels = -1 ) +{ + partial_simulator sim( ntk.num_pis(), 0 ); + sim.add_pattern( pattern ); + unordered_node_map tts( ntk ); + + auto const care = observability_dont_cares( ntk, n, sim, tts, levels ); + return !kitty::is_const0( care ); +} + +/*! \brief SAT-based satisfiability don't cares checker + * + * Initialize this class with a network and then call `is_dont_care` on a node + * to check whether the given assignment is a satisfiability don't care. + * + * The assignment is assumed to be directly at the inputs of the gate, not + * taking into account possible complemented fanins. + */ +template +struct satisfiability_dont_cares_checker +{ + explicit satisfiability_dont_cares_checker( Ntk const& ntk ) + : ntk_( ntk ), + literals_( node_literals( ntk ) ) + { + init(); + } + + bool is_dont_care( node const& n, std::vector const& assignment ) + { + if ( ntk_.fanin_size( n ) != assignment.size() ) + return false; + + std::vector assumptions( assignment.size() ); + ntk_.foreach_fanin( n, [&]( auto const& f, auto i ) { + assumptions[i] = lit_not_cond( literals_[ntk_.get_node( f )], assignment[i] == ntk_.is_complemented( f ) ); + } ); + + return solver_.solve( &assumptions[0], &assumptions[0] + assumptions.size(), 0 ) == percy::failure; + } + +private: + void init() + { + generate_cnf( + ntk_, [&]( auto const& clause ) { + solver_.add_clause( clause ); + }, + literals_ ); + } + +private: + Ntk const& ntk_; + percy::bsat_wrapper solver_; + node_map literals_; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/dsd_decomposition.hpp b/third-party/mockturtle/include/mockturtle/algorithms/dsd_decomposition.hpp new file mode 100644 index 00000000000..66bbaf6fa0b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/dsd_decomposition.hpp @@ -0,0 +1,238 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dsd_decomposition.hpp + \brief DSD decomposition + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for dsd_decomposition */ +struct dsd_decomposition_params +{ + /*! \brief Apply XOR decomposition. */ + bool with_xor{ true }; +}; + +namespace detail +{ + +template +class dsd_decomposition_impl +{ +public: + dsd_decomposition_impl( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector> const& children, Fn&& on_prime, dsd_decomposition_params const& ps ) + : _ntk( ntk ), + remainder( func ), + pis( children ), + _on_prime( on_prime ), + _ps( ps ) + { + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( kitty::has_var( func, i ) ) + { + support.push_back( i ); + } + } + } + + signal run() + { + /* terminal cases */ + if ( kitty::is_const0( remainder ) ) + { + return _ntk.get_constant( false ); + } + if ( kitty::is_const0( ~remainder ) ) + { + return _ntk.get_constant( true ); + } + + /* projection case */ + if ( support.size() == 1u ) + { + auto var = remainder.construct(); + kitty::create_nth_var( var, support.front() ); + if ( remainder == var ) + { + return pis[support.front()]; + } + else + { + if ( remainder != ~var ) + { + fmt::print( "remainder = {}, vars = {}\n", kitty::to_binary( remainder ), remainder.num_vars() ); + assert( false ); + } + assert( remainder == ~var ); + return _ntk.create_not( pis[support.front()] ); + } + } + + /* try top decomposition */ + for ( auto var : support ) + { + if ( auto res = kitty::is_top_decomposable( remainder, var, &remainder, _ps.with_xor ); + res != kitty::top_decomposition::none ) + { + /* remove var from support, pis do not change */ + support.erase( std::remove( support.begin(), support.end(), var ), support.end() ); + const auto right = run(); + + switch ( res ) + { + default: + assert( false ); + case kitty::top_decomposition::and_: + return _ntk.create_and( pis[var], right ); + case kitty::top_decomposition::or_: + return _ntk.create_or( pis[var], right ); + case kitty::top_decomposition::lt_: + return _ntk.create_lt( pis[var], right ); + case kitty::top_decomposition::le_: + return _ntk.create_le( pis[var], right ); + case kitty::top_decomposition::xor_: + return _ntk.create_xor( pis[var], right ); + } + } + } + + /* try bottom decomposition */ + for ( auto j = 1u; j < support.size(); ++j ) + { + for ( auto i = 0u; i < j; ++i ) + { + if ( auto res = kitty::is_bottom_decomposable( remainder, support[i], support[j], &remainder, _ps.with_xor ); + res != kitty::bottom_decomposition::none ) + { + /* update pis based on decomposition type */ + switch ( res ) + { + default: + assert( false ); + case kitty::bottom_decomposition::and_: + pis[support[i]] = _ntk.create_and( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::or_: + pis[support[i]] = _ntk.create_or( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::lt_: + pis[support[i]] = _ntk.create_lt( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::le_: + pis[support[i]] = _ntk.create_le( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::xor_: + pis[support[i]] = _ntk.create_xor( pis[support[i]], pis[support[j]] ); + break; + } + + /* remove var from support */ + support.erase( support.begin() + j ); + + return run(); + } + } + } + + /* cannot decompose anymore */ + std::vector> new_pis; + for ( auto var : support ) + { + new_pis.push_back( pis[var] ); + } + auto prime_large = remainder; + kitty::min_base_inplace( prime_large ); + auto prime = kitty::shrink_to( prime_large, static_cast( support.size() ) ); + return _on_prime( prime, new_pis ); + } + +private: + Ntk& _ntk; + kitty::dynamic_truth_table remainder; + std::vector support; + std::vector> pis; + Fn&& _on_prime; + dsd_decomposition_params const& _ps; +}; + +} // namespace detail + +/*! \brief DSD decomposition + * + * This function applies DSD decomposition on an input truth table and + * constructs a network based on all possible decompositions. If the truth + * table is only partially decomposable, then the remaining *prime function* + * is returned back to the caller using the call back `on_prime` together with + * the computed primary inputs for that remainder. + * + * The `on_prime` function must be of type `NtkDest::signal( + * kitty::dynamic_truth_table const&, std::vector const&)`. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_or` + * - `create_lt` + * - `create_le` + * - `create_xor` + */ +template +signal dsd_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector> const& children, Fn&& on_prime, dsd_decomposition_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + static_assert( has_create_lt_v, "Ntk does not implement the create_lt method" ); + static_assert( has_create_le_v, "Ntk does not implement the create_le method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + + detail::dsd_decomposition_impl impl( ntk, func, children, on_prime, ps ); + return impl.run(); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/emap.hpp b/third-party/mockturtle/include/mockturtle/algorithms/emap.hpp new file mode 100644 index 00000000000..055d9b25bba --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/emap.hpp @@ -0,0 +1,5972 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2024 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file emap.hpp + \brief An extended technology mapper + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "../networks/aig.hpp" +#include "../networks/block.hpp" +#include "../networks/klut.hpp" +#include "../utils/cuts.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/tech_library.hpp" +#include "../views/binding_view.hpp" +#include "../views/cell_view.hpp" +#include "../views/choice_view.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "detail/mffc_utils.hpp" +#include "detail/switching_activity.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for emap. + * + * The data structure `emap_params` holds configurable parameters + * with default arguments for `emap`. + */ +struct emap_params +{ + emap_params() + { + cut_enumeration_ps.cut_limit = 16; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 16. + * The maximum cut limit is 19. + * By default, truth table minimization + * is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Do area-oriented mapping. */ + bool area_oriented_mapping{ false }; + + /*! \brief Maps using multi-output gates */ + bool map_multioutput{ false }; + + /*! \brief Matching mode + * + * Boolean uses Boolean matching (up to 6-input cells), + * Structural uses pattern matching for fully-DSD cells, + * Hybrid combines the two. + */ + enum matching_mode_t + { + boolean, + structural, + hybrid + } matching_mode = hybrid; + + /*! \brief Target required time (for each PO). */ + double required_time{ 0.0f }; + + /*! \brief Required time relaxation in percentage (10 = 10%). */ + double relax_required{ 0.0f }; + + /*! \brief Custom input arrival times. */ + std::vector arrival_times{}; + + /*! \brief Custom output required times. */ + std::vector required_times{}; + + /*! \brief Number of rounds for area flow optimization. */ + uint32_t area_flow_rounds{ 3u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t ela_rounds{ 2u }; + + /*! \brief Number of rounds for exact switching power optimization. */ + uint32_t eswp_rounds{ 0u }; + + /*! \brief Number of patterns for switching activity computation. */ + uint32_t switching_activity_patterns{ 2048u }; + + /*! \brief Compute area-oriented alternative matches */ + bool use_match_alternatives{ true }; + + /*! \brief Remove the cuts that are contained in others */ + bool remove_dominated_cuts{ false }; + + /*! \brief Remove overlapping multi-output cuts */ + bool remove_overlapping_multicuts{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for emap. + * + * The data structure `emap_stats` provides data collected by running + * `emap`. + */ +struct emap_stats +{ + /*! \brief Area result. */ + double area{ 0 }; + /*! \brief Worst delay result. */ + double delay{ 0 }; + /*! \brief Power result. */ + double power{ 0 }; + /*! \brief Power result. */ + uint32_t inverters{ 0 }; + + /*! \brief Mapped multi-output gates. */ + uint32_t multioutput_gates{ 0 }; + + /*! \brief Runtime for multi-output matching. */ + stopwatch<>::duration time_multioutput{ 0 }; + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Cut enumeration stats. */ + cut_enumeration_stats cut_enumeration_st{}; + + /*! \brief Delay and area stats for each round. */ + std::vector round_stats{}; + + /*! \brief Mapping error. */ + bool mapping_error{ false }; + + void report() const + { + for ( auto const& stat : round_stats ) + { + std::cout << stat; + } + std::cout << fmt::format( "[i] Area = {:>5.2f}; Delay = {:>5.2f};", area, delay ); + if ( power != 0 ) + std::cout << fmt::format( " Power = {:>5.2f};\n", power ); + else + std::cout << "\n"; + if ( multioutput_gates ) + { + std::cout << fmt::format( "[i] Multi-output gates = {:>5}\n", multioutput_gates ); + std::cout << fmt::format( "[i] Multi-output runtime = {:>5.2f} secs\n", to_seconds( time_multioutput ) ); + } + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +#pragma region cut set +template +struct cut_enumeration_emap_cut +{ + /* stats */ + uint32_t delay; + float flow; + bool ignore; + + /* pattern index for structural matching*/ + uint32_t pattern_index; + + /* function */ + kitty::static_truth_table<6> function; + + /* list of supergates matching the cut for positive and negative output phases */ + std::array> const*, 2> supergates; + /* input negations, 0: pos, 1: neg */ + std::array negations; +}; + +struct cut_enumeration_emap_multi_cut +{ + /* stats */ + uint64_t id{ 0 }; +}; + +enum class emap_cut_sort_type +{ + DELAY = 0, + DELAY2 = 1, + AREA = 2, + AREA2 = 3, + NONE = 4 +}; + +template +class emap_cut_set +{ +public: + /*! \brief Standard constructor. + */ + emap_cut_set() + { + clear(); + } + + /*! \brief Assignment operator. + */ + emap_cut_set& operator=( emap_cut_set const& other ) + { + if ( this != &other ) + { + _pcend = _pend = _pcuts.begin(); + _set_limit = other._set_limit; + + auto it = other.begin(); + while ( it != other.end() ) + { + **_pend++ = **it++; + ++_pcend; + } + } + + return *this; + } + + /*! \brief Clears a cut set. + */ + void clear() + { + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } + } + + /*! \brief Sets the cut limit. + */ + void set_cut_limit( uint32_t limit ) + { + _set_limit = std::min( MaxCuts, limit ); + } + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ) + { + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; + } + + /*! \brief Appends a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param cut Cut to insert + */ + void append_cut( CutType const& cut ) + { + assert( _pend != _pcuts.end() ); + + **_pend++ = cut; + ++_pcend; + } + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const + { + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; + } + + static bool sort_delay( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + return c1.size() < c2.size(); + } + + static bool sort_delay2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + return c1->flow < c2->flow - eps; + } + + static bool sort_area( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + return c1->delay < c2->delay - eps; + } + + static bool sort_area2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + return c1.size() < c2.size(); + } + + /*! \brief Compare two cuts using sorting functions. + * + * This method compares two cuts using a sorting function. + * + * \param cut1 first cut. + * \param cut2 second cut. + * \param sort sorting function. + */ + static bool compare( CutType const& cut1, CutType const& cut2, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + if ( sort == emap_cut_sort_type::DELAY ) + { + return sort_delay( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::DELAY2 ) + { + return sort_delay2( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::AREA ) + { + return sort_area( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::AREA2 ) + { + return sort_area2( cut1, cut2 ); + } + else + { + return false; + } + } + + /*! \brief Inserts a cut into a set without checking dominance. + * + * This method will insert a cut into a set and maintain an order. This + * method doesn't remove the cuts that are dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param sort Cut prioritization function. + */ + void simple_insert( CutType const& cut, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + /* insert cut in a sorted way */ + typename std::array::iterator ipos = _pcuts.begin(); + + bool limit_reached = std::distance( _pcuts.begin(), _pend ) >= _set_limit; + + /* do not insert if worst than set_limit */ + if ( limit_reached ) + { + if ( sort == emap_cut_sort_type::AREA && !sort_area( cut, **( ipos + _set_limit - 1 ) ) ) + { + return; + } + else if ( sort != emap_cut_sort_type::AREA ) + { + return; + } + } + + if ( sort == emap_cut_sort_type::NONE ) + { + ipos = _pend; + } + else /* AREA */ + { + ipos = std::upper_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area( *a, *b ); } ); + } + + /* check for redundant cut */ + typename std::array::iterator jpos = ipos; + if ( cut->ignore ) + { + while ( jpos != _pcuts.begin() ) + { + --jpos; + if ( ( *jpos )->size() < cut.size() ) + break; + if ( ( *jpos )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *jpos )->begin() ) ) + return; + } + } + else if ( ipos != _pcuts.begin() ) + { + if ( ( *( ipos - 1 ) )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *( ipos - 1 ) )->begin() ) ) + { + return; + } + } + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() || limit_reached ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; + } + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. Variable `skip0` tell to skip the dominance check on + * cut zero. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param skip0 Skip dominance check on cut zero. + * \param sort Cut prioritization function. + */ + void insert( CutType const& cut, bool skip0 = false, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + auto begin = _pcuts.begin(); + + if ( skip0 && _pend != _pcuts.begin() ) + ++begin; + + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( begin, _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + simple_insert( cut, sort ); + } + + /*! \brief Replaces a cut of the set. + * + * This method replaces the cut at position `index` in the set by `cut` + * and maintains the cuts order. The function does not check whether + * index is in the valid range. + * + * \param index Index of the cut to replace. + * \param cut Cut to insert. + */ + void replace( uint32_t index, CutType const& cut ) + { + *_pcuts[index] = cut; + } + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto const& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ) + { + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; + } + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ) + { + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } + } + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, emap_cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + + /*! \brief Returns if the cut set contains already `cut`. */ + bool is_contained( CutType const& cut ) + { + typename std::array::iterator ipos = _pcuts.begin(); + + while ( ipos != _pend ) + { + if ( ( *ipos )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *ipos )->begin() ) ) + return true; + ++ipos; + } + + return false; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; + uint32_t _set_limit{ MaxCuts }; +}; +#pragma endregion + +#pragma region Hashing +template +struct emap_triple_hash +{ + inline uint64_t operator()( const std::array& p ) const + { + uint64_t seed = hash_block( p[0] ); + + for ( uint32_t i = 1; i < max_multioutput_cut_size; ++i ) + { + hash_combine( seed, hash_block( p[i] ) ); + } + + return seed; + } +}; +#pragma endregion + +template +struct best_gate_emap +{ + supergate const* gate; + double arrival; + float area; + float flow; + unsigned phase : 16; + unsigned cut : 12; + unsigned size : 4; +}; + +template +struct node_match_emap +{ + /* best gate match for positive and negative output phases */ + supergate const* best_gate[2]; + /* alternative best gate for positibe and negative output phase */ + best_gate_emap best_alternative[2]; + /* fanin pin phases for both output phases */ + uint16_t phase[2]; + /* best cut index for both phases */ + uint16_t best_cut[2]; + /* node is mapped using only one phase */ + bool same_match; + /* node is mapped to a multi-output gate */ + bool multioutput_match[2]; + + /* arrival time at node output */ + double arrival[2]; + /* required time at node output */ + double required[2]; + /* area of the best matches */ + float area[2]; + + /* number of references in the cover 0: pos, 1: neg */ + uint32_t map_refs[2]; + /* references estimation */ + float est_refs[2]; + /* area flow */ + float flows[2]; +}; + +template +class emap_impl +{ +private: + union multi_match_data + { + uint64_t data{ 0 }; + struct + { + uint64_t in_tfi : 1; + uint64_t cut_index : 31; + uint64_t node_index : 32; + }; + }; + union multioutput_info + { + uint32_t data; + struct + { + unsigned index : 29; + unsigned lowest_index : 1; + unsigned highest_index : 1; + unsigned has_info : 1; + }; + }; + +public: + static constexpr float epsilon = 0.0005; + static constexpr uint32_t max_cut_num = 20; + using cut_t = cut>; + using cut_set_t = emap_cut_set; + using cut_merge_t = typename std::array; + using fanin_cut_t = typename std::array; + using support_t = typename std::array; + using TT = kitty::static_truth_table<6>; + using truth_compute_t = typename std::array; + using node_match_t = std::vector>; + using klut_map = std::unordered_map, 2>>; + using block_map = std::unordered_map, 2>>; + + static constexpr uint32_t max_multioutput_cut_size = 3; + static constexpr uint32_t max_multioutput_output_size = 2; + using multi_cuts_t = fast_network_cuts; + using multi_cut_t = typename multi_cuts_t::cut_t; + using multi_leaves_set_t = std::array; + using multi_output_set_t = std::vector; + using multi_hash_t = phmap::flat_hash_map>; + using multi_match_t = std::array; + using multi_cut_set_t = std::vector>; + using multi_single_matches_t = std::vector; + using multi_matches_t = std::vector>; + + using clock = typename std::chrono::steady_clock; + using time_point = typename clock::time_point; + +public: + explicit emap_impl( Ntk const& ntk, tech_library const& library, emap_params const& ps, emap_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + node_tuple_match( ntk.size() ), + switch_activity( ps.eswp_rounds ? switching_activity( ntk, ps.switching_activity_patterns ) : std::vector( 0 ) ), + cuts( ntk.size() ) + { + std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() ); + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + tmp_visited.reserve( 100 ); + } + + explicit emap_impl( Ntk const& ntk, tech_library const& library, std::vector const& switch_activity, emap_params const& ps, emap_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + node_tuple_match( ntk.size() ), + switch_activity( switch_activity ), + cuts( ntk.size() ) + { + std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() ); + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + tmp_visited.reserve( 100 ); + } + + cell_view run_block() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_block_network(); + + /* multi-output initialization */ + if ( ps.map_multioutput && ps.matching_mode != emap_params::structural ) + { + compute_multioutput_match(); + } + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* search for large matches */ + if ( ps.matching_mode == emap_params::structural || CutSize > 6 ) + { + if ( !compute_struct_match() ) + { + return res; + } + } + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover_block( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + + binding_view run_klut() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_map_network(); + + /* multi-output initialization */ + if ( ps.map_multioutput && ps.matching_mode != emap_params::structural ) + { + compute_multioutput_match(); + } + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* search for large matches */ + if ( ps.matching_mode == emap_params::structural || CutSize > 6 ) + { + if ( !compute_struct_match() ) + { + return res; + } + } + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + + binding_view run_node_map() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_map_network(); + + /* [i] multi-output support is currently not implemented */ + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match_node() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match_node() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + +private: + bool improve_mapping() + { + /* compute mapping using global area flow */ + uint32_t i = 0; + while ( i++ < ps.area_flow_rounds ) + { + if ( !compute_mapping() ) + { + return false; + } + } + + /* compute mapping using exact area */ + i = 0; + compute_required_time( true ); + while ( i++ < ps.ela_rounds ) + { + if ( !compute_mapping_exact_reversed() ) + { + return false; + } + } + + /* compute mapping using exact switching activity estimation */ + i = 0; + while ( i++ < ps.eswp_rounds ) + { + if ( !compute_mapping_exact_reversed() ) + { + return false; + } + } + + return true; + } + +#pragma region Core + template + bool compute_mapping_match() + { + bool warning_box = false; + + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + + if ( !compute_matches_node( n, warning_box ) ) + { + continue; + } + + /* load multi-output cuts and data */ + if ( ps.map_multioutput && node_tuple_match[index].has_info ) + { + match_multi_add_cuts( n ); + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* select alternative matches to use */ + select_alternatives( n ); + + /* try multi-output matches */ + if constexpr ( DO_AREA ) + { + if ( ps.map_multioutput && node_tuple_match[index].highest_index ) + { + if ( match_multioutput( n ) ) + multi_node_update( n ); + } + } + } + + double area_old = area; + bool success = set_mapping_refs_and_req(); + + if ( warning_box ) + { + std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n"; + } + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + inline bool compute_matches_node( node const& n, bool& warning_box ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.est_refs[0] = node_data.est_refs[1] = static_cast( ntk.fanout_size( n ) ); + node_data.map_refs[0] = node_data.map_refs[1] = 0; + node_data.required[0] = node_data.required[1] = std::numeric_limits::max(); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 0.0 */ + node_data.flows[0] = node_data.flows[1] = 0.0f; + node_data.best_alternative[0].flow = node_data.best_alternative[1].flow = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + node_data.best_alternative[0].arrival = node_data.best_alternative[1].arrival = 0.0f; + /* skip if cuts have been computed before */ + if ( cuts[index].size() == 0 ) + { + add_zero_cut( index ); + match_constants( index ); + } + return false; + } + else if ( ntk.is_pi( n ) ) + { + node_data.flows[0] = 0.0f; + node_data.best_alternative[0].flow = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.flows[1] = lib_inv_area / node_data.est_refs[1]; + node_data.best_alternative[1].flow = lib_inv_area / node_data.est_refs[1]; + /* skip if cuts have been computed before */ + if ( cuts[index].size() == 0 ) + { + add_unit_cut( index ); + } + return false; + } + + if ( ps.matching_mode == emap_params::structural ) + return true; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + warning_box |= initialize_box( n ); + return false; + } + } + + /* compute cuts for node */ + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( n ); + } + else + { + merge_cuts( n ); + } + + return true; + } + + template + void merge_cuts2( node const& n ) + { + static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + + /* compute cuts */ + const auto fanin = 2; + ntk.foreach_fanin( ntk.index_to_node( index ), [this]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* move pre-computed structural cuts to a temporary cutset */ + bool reinsert_cuts = false; + if ( rcuts.size() ) + { + temp_cuts.clear(); + for ( auto& cut : rcuts ) + { + if ( ( *cut )->ignore ) + continue; + recompute_cut_data( *cut, n ); + temp_cuts.simple_insert( *cut ); + reinsert_cuts = true; + } + rcuts.clear(); + } + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + + cut_t new_cut; + new_cut->pattern_index = 0; + fanin_cut_t vcuts; + + for ( auto const& c1 : *lcuts[0] ) + { + /* skip cuts of pattern matching */ + if ( ( *c1 )->pattern_index > 1 ) + continue; + vcuts[0] = c1; + + for ( auto const& c2 : *lcuts[1] ) + { + /* skip cuts of pattern matching */ + if ( ( *c2 )->pattern_index > 1 ) + continue; + + if ( !c1->merge( *c2, new_cut, max_cut_size ) ) + { + continue; + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + continue; + } + + /* compute function */ + vcuts[1] = c2; + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + if ( reinsert_cuts ) + { + for ( auto const& cut : temp_cuts ) + { + rcuts.simple_insert( *cut, sort ); + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + } + + template + void merge_cuts( node const& n ) + { + static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + cut_t best_cut; + + /* compute cuts */ + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + } ); + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + fanin_cut_t vcuts; + + if ( fanin > 1 && fanin <= ps.cut_enumeration_ps.fanin_limit ) + { + cut_t new_cut, tmp_cut; + + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, max_cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, max_cut_size ) ) + { + return true; /* continue */ + } + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + else if ( fanin == 1 ) + { + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + vcuts[0] = cut; + + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + + cuts_total += rcuts.size(); + + add_unit_cut( index ); + } + + bool compute_struct_match() + { + if ( ps.matching_mode == emap_params::boolean ) + return true; + + /* compatible only with AIGs */ + if constexpr ( !is_aig_network_type_v ) + { + if ( ps.matching_mode == emap_params::structural ) + { + std::cerr << "[e] MAP ERROR: structural library works only with AIGs\n"; + return false; + } + return true; + } + + /* no large gates identified */ + if ( library.num_structural_gates() == 0 ) + { + if ( ps.matching_mode == emap_params::structural ) + { + std::cerr << "[e] MAP ERROR: structural library is empty\n"; + return false; + } + return true; + } + + bool warning_box = false; + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + if ( ntk.is_constant( n ) ) + { + add_zero_cut( index ); + match_constants( index ); + continue; + } + else if ( ntk.is_pi( n ) ) + { + add_unit_cut( index ); + continue; + } + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + add_unit_cut( index ); + continue; + } + } + + /* compute cuts for node */ + merge_cuts_structural( n ); + } + + if ( warning_box ) + { + std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n"; + } + + /* round stats */ + if ( ps.verbose ) + { + st.round_stats.push_back( fmt::format( "[i] SCuts : Cuts = {:>12d} Time = {:>12.2f}\n", cuts_total, to_seconds( clock::now() - time_begin ) ) ); + } + + return true; + } + + void merge_cuts_structural( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + + /* compute cuts */ + const auto fanin = 2; + std::array children_phase; + ntk.foreach_fanin( ntk.index_to_node( index ), [&]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + children_phase[i] = ntk.is_complemented( child ) ? 1 : 0; + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + + cut_t new_cut; + std::vector vcuts( fanin ); + + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + /* filter large cuts */ + if ( c1->size() + c2->size() > CutSize || c1->size() + c2->size() > NInputs ) + continue; + /* filter cuts involving constants */ + if ( ( *c1 )->pattern_index == 0 || ( *c2 )->pattern_index == 0 ) + continue; + + vcuts[0] = c1; + vcuts[1] = c2; + uint32_t pattern_id1 = ( ( *c1 )->pattern_index << 1 ) | children_phase[0]; + uint32_t pattern_id2 = ( ( *c2 )->pattern_index << 1 ) | children_phase[1]; + if ( pattern_id1 > pattern_id2 ) + { + std::swap( vcuts[0], vcuts[1] ); + std::swap( pattern_id1, pattern_id2 ); + } + + uint32_t new_pattern = library.get_pattern_id( pattern_id1, pattern_id2 ); + + /* pattern not matched */ + if ( new_pattern == UINT32_MAX ) + continue; + + create_structural_cut( new_cut, vcuts, new_pattern, pattern_id1, pattern_id2 ); + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + continue; + + /* match cut and compute data */ + compute_cut_data_structural( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + } + + template + bool compute_mapping_match_node() + { + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.best_gate[0] = node_data.best_gate[1] = nullptr; + node_data.same_match = 0; + node_data.multioutput_match[0] = node_data.multioutput_match[1] = false; + node_data.required[0] = node_data.required[1] = std::numeric_limits::max(); + node_data.map_refs[0] = node_data.map_refs[1] = 0; + node_data.est_refs[0] = node_data.est_refs[1] = static_cast( ntk.fanout_size( n ) ); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 0 */ + node_data.flows[0] = node_data.flows[1] = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + add_zero_cut( index ); + match_constants( index ); + continue; + } + else if ( ntk.is_pi( n ) ) + { + /* all terminals have flow 0 */ + node_data.flows[0] = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.flows[1] = lib_inv_area / node_data.est_refs[1]; + add_unit_cut( index ); + continue; + } + + /* compute the node mapping */ + add_node_cut( n ); + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* select alternative matches to use */ + select_alternatives( n ); + } + double area_old = area; + bool success = set_mapping_refs_and_req(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + void add_node_cut( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto& rcuts = &cuts[index]; + + std::vector fanin_indexes; + fanin_indexes.reserve( Ntk::max_fanin_size ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + fanin_indexes.push_back( ntk.node_to_index( ntk.get_node( f ) ) ); + } ); + + assert( fanin_indexes.size() <= CutSize ); + + cut_t new_cut = rcuts.add_cut( fanin_indexes.begin(), fanin_indexes.end() ); + new_cut->function = kitty::extend_to<6>( ntk.node_function( n ) ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + ++cuts_total; + } + + template + bool compute_mapping() + { + for ( auto const& n : topo_order ) + { + uint32_t index = ntk.node_to_index( n ); + + /* reset mapping */ + node_match[index].map_refs[0] = node_match[index].map_refs[1] = 0u; + + if ( ntk.is_constant( n ) ) + continue; + if ( ntk.is_pi( n ) ) + { + node_match[index].flows[1] = lib_inv_area / node_match[index].est_refs[1]; + node_match[index].best_alternative[1].flow = lib_inv_area / node_match[index].est_refs[1]; + continue; + } + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + } + continue; + } + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* try a multi-output match */ + if constexpr ( DO_AREA ) + { + if ( ps.map_multioutput && node_tuple_match[index].highest_index ) + { + bool multi_success = match_multioutput( n ); + if ( multi_success ) + multi_node_update( n ); + } + } + + assert( node_match[index].arrival[0] < node_match[index].required[0] + epsilon ); + assert( node_match[index].arrival[1] < node_match[index].required[1] + epsilon ); + } + + double area_old = area; + bool success = set_mapping_refs_and_req(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + bool compute_mapping_exact_reversed() + { + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_constant( *it ) || ntk.is_pi( *it ) ) + continue; + + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip not mapped nodes */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_backward_white_box( n ); + } + continue; + } + } + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + double old_required = -1; + if ( node_data.same_match ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, *it, use_phase ); + + /* propagate required time over the output inverter if present */ + if ( node_data.map_refs[use_phase ^ 1] > 0 ) + { + old_required = node_data.required[use_phase]; + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[use_phase ^ 1] - lib_inv_delay ); + } + } + else if ( !node_data.map_refs[0] || !node_data.map_refs[1] ) + { + use_phase = node_data.map_refs[0] ? 0 : 1; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, *it, use_phase ); + node_data.same_match = true; + } + + /* match positive phase */ + match_phase_exact( *it, 0u ); + + /* match negative phase */ + match_phase_exact( *it, 1u ); + + /* restore required time */ + if ( old_required > 0 ) + { + node_data.required[use_phase] = old_required; + } + + /* try to drop one phase */ + match_drop_phase( *it ); + + /* try a multi-output match */ /* TODO: fix the required time*/ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index ) + { + bool mapped = match_multioutput_exact( *it, true ); + + /* propagate required time for the selected gates */ + if ( mapped ) + { + match_multioutput_propagate_required( *it ); + } + else + { + match_propagate_required( index ); + } + } + else + { + match_propagate_required( index ); + } + } + + double area_old = area; + + propagate_arrival_times(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + if constexpr ( SwitchActivity ) + stats << fmt::format( "[i] Switching: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + else + stats << fmt::format( "[i] Area Rev : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + st.round_stats.push_back( stats.str() ); + } + + return true; + } + + inline void match_propagate_required( uint32_t index ) + { + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_backward_white_box( n ); + } + return; + } + } + + auto& node_data = node_match[index]; + + /* propagate required time through the leaves */ + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + unsigned other_phase = use_phase ^ 1; + + assert( node_data.best_gate[0] != nullptr || node_data.best_gate[1] != nullptr ); + // assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay ); + } + + if ( node_data.map_refs[0] ) + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + if ( node_data.map_refs[1] ) + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + auto ctr = 0u; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto const& supergate = node_data.best_gate[use_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[use_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + + if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + auto ctr = 0u; + auto const& best_cut = cuts[index][node_data.best_cut[other_phase]]; + auto const& supergate = node_data.best_gate[other_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[other_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + } + + template + bool set_mapping_refs() + { + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + inv = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + { + std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_pi( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( *it ) ) + { + set_mapping_refs_dont_touch( *it ); + continue; + } + } + + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + + if ( node_data.best_gate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_data.map_refs[use_phase]; + } + area += lib_inv_area; + ++inv; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + } + + ++iteration; + + if constexpr ( ELA ) + { + return true; + } + + /* blend estimated references */ + float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) ); + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] ); + node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] ); + } + + return true; + } + + template + bool set_mapping_refs_and_req() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + set_output_required_time( iteration == 0 ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + inv = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + { + std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_pi( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( *it ) ) + { + set_mapping_refs_dont_touch( *it ); + continue; + } + } + + /* refine best matches with alternatives */ + if constexpr ( !DO_AREA ) + { + if ( ps.use_match_alternatives ) + refine_best_matches( *it ); + } + + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + if ( node_data.best_gate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_data.map_refs[use_phase]; + } + area += lib_inv_area; + ++inv; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + + if ( !ps.area_oriented_mapping ) + { + match_propagate_required( index ); + } + } + + ++iteration; + + if constexpr ( ELA ) + { + return true; + } + + /* blend estimated references */ + float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) ); + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] ); + node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] ); + } + + return true; + } + + template + inline void set_mapping_refs_dont_touch( node const& n ) + { + if constexpr ( !ELA ) + { + /* reference node */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + uint32_t leaf = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + node_match[leaf].map_refs[phase]++; + } ); + } + + const auto index = ntk.node_to_index( n ); + + if constexpr ( has_has_binding_v ) + { + /* increase area */ + area += node_match[index].area[0]; + if ( node_match[index].map_refs[1] ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_match[index].map_refs[0]; + } + area += lib_inv_area; + ++inv; + } + } + } + + void set_output_required_time( bool warning ) + { + double required = delay; + /* relax delay constraints */ + if ( iteration == 0 && ps.required_time == 0.0f && ps.required_times.empty() && ps.relax_required > 0.0f ) + { + required *= ( 100.0 + ps.relax_required ) / 100.0; + } + + /* Global target time constraint */ + if ( ps.required_times.empty() ) + { + if ( ps.required_time != 0.0f ) + { + if ( ps.required_time < delay - epsilon ) + { + if ( warning ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl; + } + else + { + required = ps.required_time; + } + } + + /* set the required time at POs */ + ntk.foreach_po( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = required; + else + node_match[index].required[0] = required; + } ); + + return; + } + + /* Output-specific target time constraint */ + ntk.foreach_po( [&]( auto const& s, uint32_t i ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + uint8_t phase = ntk.is_complemented( s ) ? 1 : 0; + if ( node_match[index].arrival[phase] > ps.required_times[i] + epsilon ) + { + /* maintain the same delay */ + node_match[index].required[phase] = node_match[index].arrival[phase]; + if ( warning ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f} at output {}", ps.required_times[i], i ) << std::endl; + } + else + { + node_match[index].required[phase] = ps.required_times[i]; + } + } ); + } + + void compute_required_time( bool exit_early = false ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* return if mapping is area oriented */ + if ( ps.area_oriented_mapping ) + return; + + set_output_required_time( iteration == 1 ); + + if ( exit_early ) + return; + + /* propagate required time to the PIs */ + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_pi( *it ) || ntk.is_constant( *it ) ) + break; + + const auto index = ntk.node_to_index( *it ); + + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + match_propagate_required( index ); + } + } + + void propagate_arrival_times() + { + area = 0.0f; + inv = 0; + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* measure area */ + if ( ntk.is_constant( n ) ) + { + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* reset required time */ + node_data.required[0] = std::numeric_limits::max(); + node_data.required[1] = std::numeric_limits::max(); + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) + area += node_data.area[0]; + if ( node_data.map_refs[1] ) + { + area += lib_inv_area; + ++inv; + } + } + continue; + } + } + + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + + /* compute arrival of use_phase */ + supergate const* best_gate = node_data.best_gate[use_phase]; + double worst_arrival = 0; + uint16_t best_phase = node_data.phase[use_phase]; + auto ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + + /* compute area */ + if ( node_data.map_refs[use_phase] > 0 || ( node_data.same_match && ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) ) ) + { + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + area += lib_inv_area; + ++inv; + } + } + + /* compute arrival of the other phase */ + use_phase ^= 1; + if ( node_data.same_match ) + { + node_data.arrival[use_phase] = worst_arrival + lib_inv_delay; + continue; + } + + assert( node_data.best_gate[use_phase] != nullptr ); + + best_gate = node_data.best_gate[use_phase]; + worst_arrival = 0; + best_phase = node_data.phase[use_phase]; + ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + + if ( node_data.map_refs[use_phase] > 0 ) + { + area += node_data.area[use_phase]; + } + } + + /* compute the current worst delay */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + } ); + + /* return if mapping is area oriented */ + ++iteration; + if ( ps.area_oriented_mapping ) + return; + + /* set the required time at POs */ + ntk.foreach_po( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = delay; + else + node_match[index].required[0] = delay; + } ); + } + + void propagate_arrival_node( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + + /* compute arrival of use_phase */ + supergate const* best_gate = node_data.best_gate[use_phase]; + double worst_arrival = 0; + uint16_t best_phase = node_data.phase[use_phase]; + auto ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + node_data.arrival[use_phase] = worst_arrival; + + /* compute arrival of the other phase */ + use_phase ^= 1; + if ( node_data.same_match ) + { + node_data.arrival[use_phase] = worst_arrival + lib_inv_delay; + return; + } + + assert( node_data.best_gate[0] != nullptr ); + + best_gate = node_data.best_gate[use_phase]; + worst_arrival = 0; + best_phase = node_data.phase[use_phase]; + ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + } + + template + void match_phase( node const& n, uint8_t phase ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + uint32_t cut_index = 0u; + + node_data.best_gate[phase] = nullptr; + node_data.arrival[phase] = std::numeric_limits::max(); + node_data.flows[phase] = std::numeric_limits::max(); + node_data.area[phase] = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + + best_gate_emap& gA = node_data.best_alternative[phase]; + gA.gate = nullptr; + gA.arrival = std::numeric_limits::max(); + gA.flow = std::numeric_limits::max(); + uint32_t best_sizeA = UINT32_MAX; + + /* unmap multioutput */ + node_data.multioutput_match[phase] = false; + + /* foreach cut */ + for ( auto& cut : cuts[index] ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = ( *cut )->supergates; + auto const negation = ( *cut )->negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint16_t gate_polarity = gate.polarity ^ negation; + double worst_arrival = 0.0f; + double worst_arrivalA = 0.0f; + float area_local = gate.area; + float area_localA = gate.area; + + auto ctr = 0u; + for ( auto l : *cut ) + { + uint8_t leaf_phase = ( gate_polarity >> ctr ) & 1; + + double arrival_pinA = node_match[l].best_alternative[leaf_phase].arrival + gate.tdelay[ctr]; + worst_arrivalA = std::max( worst_arrivalA, arrival_pinA ); + + // if constexpr ( DO_AREA ) + // { + // if ( worst_arrivalA > node_data.required[phase] + epsilon || worst_arrivalA >= std::numeric_limits::max() ) + // break; + // } + + double arrival_pin = node_match[l].arrival[leaf_phase] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + + area_local += node_match[l].flows[leaf_phase]; + area_localA += node_match[l].best_alternative[leaf_phase].flow; + ++ctr; + } + + bool skip = false; + if constexpr ( DO_AREA ) + { + if ( ctr < cut->size() ) + continue; + if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits::max() ) + skip = true; + } + + if ( !skip && compare_map( worst_arrival, node_data.arrival[phase], area_local, node_data.flows[phase], cut->size(), best_size ) ) + { + node_data.best_gate[phase] = &gate; + node_data.arrival[phase] = worst_arrival; + node_data.flows[phase] = area_local; + node_data.best_cut[phase] = cut_index; + node_data.area[phase] = gate.area; + node_data.phase[phase] = gate_polarity; + best_size = cut->size(); + } + + /* compute the alternative */ + if ( compare_map( worst_arrivalA, gA.arrival, area_localA, gA.flow, cut->size(), best_sizeA ) ) + { + gA.gate = &gate; + gA.arrival = worst_arrivalA; + gA.area = gate.area; + gA.flow = area_localA; + gA.phase = gate_polarity; + gA.cut = cut_index; + best_sizeA = cut->size(); + gA.size = cut->size(); + } + } + + ++cut_index; + } + } + + template + void match_phase_exact( node const& n, uint8_t phase ) + { + double best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint16_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + supergate const* best_gate = node_data.best_gate[phase]; + + /* unmap multioutput */ + if ( node_data.multioutput_match[phase] ) + { + /* dereference multi-output */ + if ( !node_data.same_match && best_gate != nullptr && node_data.map_refs[phase] ) + { + auto const& cut = multi_cut_set[node_data.best_cut[phase]][0]; + cut_deref( cut, n, phase ); + } + best_gate = nullptr; + node_data.multioutput_match[phase] = false; + } + + /* recompute best match info */ + if ( best_gate != nullptr ) + { + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + auto const& cut = cuts[index][node_data.best_cut[phase]]; + cut_deref( cut, n, phase ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts[index] ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = ( *cut )->supergates; + auto const negation = ( *cut )->negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint16_t gate_polarity = gate.polarity ^ negation; + double worst_arrival = 0.0f; + + auto ctr = 0u; + for ( auto l : *cut ) + { + double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits::max() ) + continue; + + node_data.phase[phase] = gate_polarity; + node_data.area[phase] = gate.area; + float area_exact = cut_measure_mffc( *cut, n, phase ); + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = gate.area; + best_size = cut->size(); + best_cut = cut_index; + best_phase = gate_polarity; + best_gate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_gate[phase] = best_gate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_ref( cuts[index][best_cut], n, phase ); + } + } + + template + void match_drop_phase( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* compute arrival adding an inverter to the other match phase */ + double worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + double worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + + /* only one phase is matched */ + if ( node_data.best_gate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + return; + } + else if ( node_data.best_gate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + return; + } + + /* try to use only one match to cover both phases */ + if constexpr ( !DO_AREA ) + { + /* if arrival improves matching the other phase and inserting an inverter */ + if ( worst_arrival_npos < node_data.arrival[0] + epsilon ) + { + use_one = true; + } + if ( worst_arrival_nneg < node_data.arrival[1] + epsilon ) + { + use_zero = true; + } + } + else + { + /* check if both phases + inverter meet the required time */ + use_zero = worst_arrival_nneg < ( node_data.required[1] + epsilon ); + use_one = worst_arrival_npos < ( node_data.required[0] + epsilon ); + } + + /* condition on not used phases, evaluate a substitution during exact area recovery */ + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] == 0 || node_data.map_refs[1] == 0 ) + { + /* select the used match */ + auto phase = 0; + auto nphase = 0; + if ( node_data.map_refs[0] == 0 ) + { + phase = 1; + use_one = true; + use_zero = false; + } + else + { + nphase = 1; + use_one = false; + use_zero = true; + } + /* select the not used match instead if it leads to area improvement and doesn't violate the required time */ + if ( node_data.arrival[nphase] + lib_inv_delay < node_data.required[phase] + epsilon ) + { + auto size_phase = cuts[index][node_data.best_cut[phase]].size(); + auto size_nphase = cuts[index][node_data.best_cut[nphase]].size(); + + if ( compare_map( node_data.arrival[nphase] + lib_inv_delay, node_data.arrival[phase], node_data.flows[nphase] + lib_inv_area, node_data.flows[phase], size_nphase, size_phase ) ) + { + /* invert the choice */ + use_zero = !use_zero; + use_one = !use_one; + } + } + } + } + + if ( ( !use_zero && !use_one ) ) + { + /* use both phases */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.same_match = false; + return; + } + + /* use area flow as a tiebreaker */ + if ( use_zero && use_one ) + { + auto size_zero = cuts[index][node_data.best_cut[0]].size(); + auto size_one = cuts[index][node_data.best_cut[1]].size(); + + if constexpr ( ELA ) + { + if ( !node_data.same_match ) + { + /* both phases were implemented --> evaluate substitution */ + cut_deref( cuts[index][node_data.best_cut[0]], n, 0 ); + node_data.flows[1] = cut_deref( cuts[index][node_data.best_cut[1]], n, 1 ); + node_data.flows[0] = cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + /* evaluate based on inverter cost */ + if constexpr ( !SwitchActivity ) + { + use_zero = lib_inv_area < node_data.flows[1] + epsilon; + use_one = lib_inv_area < node_data.flows[0] + epsilon; + } + + if ( use_one && use_zero ) + { + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + else if ( !use_one && !use_zero && node_data.same_match ) + { + node_data.same_match = false; + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + return; + } + } + else + { + /* compare flows by looking at the most convinient and referenced */ + if ( node_data.flows[0] / node_data.est_refs[0] + lib_inv_area < node_data.flows[1] / node_data.est_refs[1] + epsilon ) + { + use_one = false; + } + else if ( node_data.flows[1] / node_data.est_refs[1] + lib_inv_area < node_data.flows[0] / node_data.est_refs[0] + epsilon ) + { + use_zero = false; + } + else + { + /* delay the decision on what to keep --> wait for better estimations */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.same_match = false; + return; + } + } + } + + if ( use_zero ) + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the negative phase cut if in use */ + if ( node_data.map_refs[1] > 0 ) + cut_deref( cuts[index][node_data.best_cut[1]], n, 1 ); + /* reference the positive cut if not in use before */ + if ( node_data.map_refs[0] == 0 && node_data.map_refs[1] > 0 ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + else if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + } + else + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the positive phase cut if in use */ + if ( node_data.map_refs[0] > 0 ) + cut_deref( cuts[index][node_data.best_cut[0]], n, 0 ); + /* reference the negative cut if not in use before */ + if ( node_data.map_refs[1] == 0 && node_data.map_refs[0] > 0 ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + else if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + } + } + + inline void set_match_complemented_phase( uint32_t index, uint8_t phase, double worst_arrival_n ) + { + auto& node_data = node_match[index]; + auto phase_n = phase ^ 1; + node_data.same_match = true; + node_data.best_gate[phase_n] = nullptr; + node_data.best_cut[phase_n] = node_data.best_cut[phase]; + node_data.phase[phase_n] = node_data.phase[phase]; + node_data.arrival[phase_n] = worst_arrival_n; + node_data.area[phase_n] = node_data.area[phase]; + node_data.flows[phase_n] = ( node_data.flows[phase] + lib_inv_area ) / node_data.est_refs[phase_n]; + node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[phase]; + } + + template + inline void select_alternatives( node const& n ) + { + if constexpr ( DO_AREA ) + return; + + if ( !ps.use_match_alternatives ) + return; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + best_gate_emap& g0 = node_data.best_alternative[0]; + best_gate_emap& g1 = node_data.best_alternative[1]; + float g0flow = g0.flow / node_data.est_refs[0]; + float g1flow = g1.flow / node_data.est_refs[1]; + + /* process for best area */ /* removed check on required since this is executed only during a delay pass */ + if ( g0.gate != nullptr && g0flow + lib_inv_area < g1flow + epsilon ) + { + g1 = g0; + g1.gate = nullptr; + g1.arrival += lib_inv_delay; + g1.flow = ( g1.flow + lib_inv_area ) / node_data.est_refs[1]; + g0.flow = g0flow; + return; + } + else if ( g1.gate != nullptr && g1flow + lib_inv_area < g0flow + epsilon ) + { + g0 = g1; + g0.gate = nullptr; + g0.arrival += lib_inv_delay; + g0.flow = ( g0.flow + lib_inv_area ) / node_data.est_refs[0]; + g1.flow = g1flow; + return; + } + + g0.flow = g0flow; + g1.flow = g1flow; + } + + inline void refine_best_matches( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* evaluate to change the best matches with the best alternative */ + best_gate_emap& g0 = node_data.best_alternative[0]; + best_gate_emap& g1 = node_data.best_alternative[1]; + + if ( node_data.map_refs[0] && node_data.map_refs[1] ) + { + if ( node_data.same_match ) + { + /* pick best implementation between the two alternatives */ + unsigned best_match_phase = node_data.best_gate[0] == nullptr ? 1 : 0; + unsigned use_phase = g0.gate == nullptr ? 1 : 0; + if ( g0.gate != nullptr && g1.gate != nullptr ) + { + if ( g0.arrival > node_data.required[0] + epsilon || g1.arrival > node_data.required[1] + epsilon ) + return; + + refine_best_matches_copy_refinement( n, 0, false ); + refine_best_matches_copy_refinement( n, 1, false ); + node_data.same_match = false; + return; + } + else + { + best_gate_emap& gUse = node_data.best_alternative[use_phase]; + if ( gUse.arrival > node_data.required[use_phase] + epsilon || gUse.arrival + lib_inv_delay > node_data.required[use_phase ^ 1] + epsilon ) + { + return; + } + refine_best_matches_copy_refinement( n, use_phase, true ); + return; + } + } + else + { + /* not same match: evaluate both zero and one phase */ + if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 0, g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon ); + } + if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 1, g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon ); + } + } + } + else if ( node_data.map_refs[0] ) + { + if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 0, false ); + } + else if ( g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon ) + { + refine_best_matches_copy_refinement( n, 1, true ); + } + } + else + { + if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 1, false ); + } + else if ( g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon ) + { + refine_best_matches_copy_refinement( n, 0, true ); + } + } + } + + inline void refine_best_matches_copy_refinement( node const& n, unsigned phase, bool both_phases ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + best_gate_emap& bg = node_data.best_alternative[phase]; + + node_data.best_gate[phase] = bg.gate; + node_data.phase[phase] = bg.phase; + node_data.best_cut[phase] = bg.cut; + node_data.arrival[phase] = bg.arrival; + node_data.area[phase] = bg.area; + node_data.flows[phase] = bg.flow; + + if ( !both_phases ) + return; + + node_data.same_match = true; + phase ^= 1; + node_data.best_gate[phase] = nullptr; + node_data.phase[phase] = bg.phase; + node_data.best_cut[phase] = bg.cut; + node_data.arrival[phase] = bg.arrival + lib_inv_delay; + node_data.area[phase] = bg.area; + node_data.flows[phase] = ( bg.flow * node_data.est_refs[phase ^ 1] + lib_inv_area ) / node_data.est_refs[phase]; + } + + bool initialize_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( cuts[index].size() == 0 ) + add_unit_cut( index ); + + auto& node_data = node_match[index]; + node_data.same_match = true; + + /* if it has mapping data propagate the delays and measure the data */ + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + return false; + } + + /* consider as a black box */ + node_data.flows[0] = 0.0f; + node_data.flows[1] = lib_inv_area / node_data.est_ref[1]; + node_data.arrival[0] = 0.0f; + node_data.arrival[1] = lib_inv_delay; + node_data.area[0] = node_data.area[1] = 0; + + return true; + } + + void propagate_data_forward_white_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto const& gate = ntk.get_binding( n ); + + /* propagate arrival time */ + double arrival = 0; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay ); + arrival = std::max( arrival, node_match[f_index].arrival[phase] + propagation_delay ); + } ); + + /* set data */ + node_data.arrival[0] = arrival; + node_data.arrival[1] = arrival + lib_inv_delay; + node_data.area[0] = node_data.area[1] = gate.area; + node_data.flows[1] = ( node_data.flows[0] + lib_inv_area ) / node_data.est_refs[1]; + node_data.flows[0] = node_data.area[0] / node_data.est_refs[0]; + } + + void propagate_data_backward_white_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto const& gate = ntk.get_binding( n ); + + assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.map_refs[1] > 0 ) + { + node_data.required[0] = std::min( node_data.required[0], node_data.required[1] - lib_inv_delay ); + } + + if ( node_data.map_refs[0] ) + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + if ( node_data.map_refs[1] ) + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay ); + node_match[f_index].required[phase] = std::min( node_match[f_index].required[phase], node_data.required[0] - propagation_delay ); + } ); + } + + void match_constants( uint32_t index ) + { + auto& node_data = node_match[index]; + + kitty::static_truth_table<6> zero_tt; + auto const supergates_zero = library.get_supergates( zero_tt ); + auto const supergates_one = library.get_supergates( ~zero_tt ); + + /* Not available in the library */ + if ( supergates_zero == nullptr && supergates_one == nullptr ) + { + return; + } + /* if only one is available, the other is obtained using an inverter */ + if ( supergates_zero != nullptr ) + { + node_data.best_gate[0] = &( ( *supergates_zero )[0] ); + node_data.arrival[0] = node_data.best_gate[0]->tdelay[0]; + node_data.area[0] = node_data.best_gate[0]->area; + node_data.phase[0] = 0; + } + if ( supergates_one != nullptr ) + { + node_data.best_gate[1] = &( ( *supergates_one )[0] ); + node_data.arrival[1] = node_data.best_gate[1]->tdelay[0]; + node_data.area[1] = node_data.best_gate[1]->area; + node_data.phase[1] = 0; + } + else + { + node_data.same_match = true; + node_data.arrival[1] = node_data.arrival[0] + lib_inv_delay; + node_data.area[1] = node_data.area[0] + lib_inv_area; + node_data.phase[1] = 1; + } + if ( supergates_zero == nullptr ) + { + node_data.same_match = true; + node_data.arrival[0] = node_data.arrival[1] + lib_inv_delay; + node_data.area[0] = node_data.area[1] + lib_inv_area; + node_data.phase[0] = 1; + } + } + + template + bool match_multioutput( node const& n ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + /* get the cut */ + auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index]; + + /* local values storage */ + std::array arrival; + std::array area_flow; + std::array area; + std::array phase; + std::array pin_phase; + std::array est_refs; + std::array cut_index; + bool mapped_multioutput = false; + + uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0; + + /* iterate for each possible match */ + for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i ) + { + /* store local validity and comparison info */ + bool valid = true; + bool is_best = true; + bool respects_required = true; + double old_flow_sum = 0; + + /* iterate for each output of the multi-output gate */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + cut_index[j] = tuple_data[j].cut_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + /* protection on complicated duplicated nodes to remap to multioutput */ + if ( !node_data.same_match ) + return false; + + /* get the output phase */ + pin_phase[j] = gate.polarity; + phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted; + + /* compute arrival */ + arrival[j] = 0.0; + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr]; + arrival[j] = std::max( arrival[j], arrival_pin ); + ++ctr; + } + + /* check required time: same_match is true */ + if constexpr ( DO_AREA ) + { + if ( arrival[j] > node_data.required[phase[j]] + epsilon ) + { + valid = false; + break; + } + if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon ) + { + valid = false; + break; + } + } + + /* check required time of the current solution */ + if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] ) + respects_required = false; + if ( node_data.same_match && node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] ) + respects_required = false; + + /* compute area flow */ + if ( j == 0 || !node_data.multioutput_match[0] ) + { + uint8_t current_phase = node_data.best_gate[0] == nullptr ? 1 : 0; + old_flow_sum += node_data.flows[current_phase]; + } + uint8_t old_phase = node_data.phase[phase[j]]; + node_data.phase[phase[j]] = gate.polarity; + area[j] = gate.area; + area_flow[j] = gate.area + cut_leaves_flow( cut, n, phase[j] ); + node_data.phase[phase[j]] = old_phase; + + /* current version may lead to delay increase */ + est_refs[j] = node_data.est_refs[phase[j]]; + } + + /* not better than individual gates */ + if ( !valid ) + continue; + + if constexpr ( !DO_AREA ) + { + if ( !is_best ) + continue; + } + + /* combine evaluation for precise area flow estimantion */ + /* compute equation AF(n) = ( Area(G) + |roots| * SUM_{l in leaves} AF(l) ) / SUM_{p in roots} est_refs( p ) */ + float flow_sum_pos = 0, flow_sum_neg; + float combined_est_refs = 0; + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + flow_sum_pos += area_flow[j]; + combined_est_refs += est_refs[j]; + } + flow_sum_neg = flow_sum_pos; + flow_sum_pos /= combined_est_refs; + + /* not better than individual gates */ + if ( respects_required && ( flow_sum_pos > old_flow_sum + epsilon ) ) + continue; + + mapped_multioutput = true; + flow_sum_neg = ( flow_sum_neg + lib_inv_area ) / combined_est_refs; + + /* commit multi-output gate */ + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + uint8_t mapped_phase = phase[j]; + node_data.multioutput_match[mapped_phase] = true; + + node_data.best_gate[mapped_phase] = &gate; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j]; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = flow_sum_pos; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + + /* select opposite phase */ + mapped_phase ^= 1; + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = nullptr; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = flow_sum_neg; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + } + } + + return mapped_multioutput; + } + + template + bool match_multioutput_exact( node const& n, bool last_round ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + /* local values storage */ + std::array best_exact_area; + + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + /* protection on complicated duplicated nodes to remap to multioutput */ + if ( !node_match[tuple_data[j].node_index].same_match ) + return false; + } + + /* if one of the outputs is not referenced, do not use multi-output gate */ + if ( last_round ) + { + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + if ( !node_match[node_index].map_refs[0] && !node_match[node_index].map_refs[1] ) + { + return false; + } + } + } + + /* if "same match" and used in the cover dereference the leaves (reverse topo order) */ + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + uint32_t node_index = tuple_data[j].node_index; + uint8_t selected_phase = node_match[node_index].best_gate[0] == nullptr ? 1 : 0; + + if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] ) + { + /* match is always single output here */ + auto const& cut = cuts[node_index][node_match[node_index].best_cut[0]]; + uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + best_exact_area[j] = cut_deref( cut, ntk.index_to_node( node_index ), use_phase ); + + /* mapping a non referenced phase */ + if ( node_match[node_index].map_refs[selected_phase] == 0 ) + best_exact_area[j] += lib_inv_area; + } + } + + /* perform mapping */ + bool mapped_multioutput = false; + mapped_multioutput = match_multioutput_exact_core( tuple_data, best_exact_area ); + + /* if "same match" and used in the cover reference the leaves (topo order) */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + + if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] ) + { + uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + auto const& best_cut = cuts[node_index][node_match[node_index].best_cut[use_phase]]; + cut_ref( best_cut, ntk.index_to_node( node_index ), use_phase ); + } + } + + return mapped_multioutput; + } + + template + inline bool match_multioutput_exact_core( multi_match_t const& tuple_data, std::array& best_exact_area ) + { + /* get the cut representative */ + auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index]; + + /* local values storage */ + std::array arrival; + std::array area_exact; + std::array area; + std::array phase; + std::array pin_phase; + std::array cut_index; + + uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0; + + bool mapped_multioutput = false; + + /* iterate for each possible match */ + for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i ) + { + /* store local validity and comparison info */ + bool valid = true; + bool is_best = true; + bool respects_required = true; + uint32_t it_counter = 0; + + /* iterate for each output of the multi-output gate (reverse topo order) */ + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + uint32_t node_index = tuple_data[j].node_index; + cut_index[j] = tuple_data[j].cut_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + ++it_counter; + + /* get the output phase and area */ + pin_phase[j] = gate.polarity; + phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted; + area[j] = gate.area; + + /* compute arrival */ + arrival[j] = 0.0; + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr]; + arrival[j] = std::max( arrival[j], arrival_pin ); + ++ctr; + } + + /* check required time */ + if ( arrival[j] > node_data.required[phase[j]] + epsilon ) + { + valid = false; + break; + } + if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon ) + { + valid = false; + break; + } + + /* check required time of current solution */ + if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] ) + respects_required = false; + if ( node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] ) + respects_required = false; + + /* compute exact area for match: needed only for the first node (leaves are shared) */ + if ( it_counter == 1 ) + { + auto old_phase = node_data.phase[phase[j]]; + auto old_area = node_data.area[phase[j]]; + node_data.phase[phase[j]] = pin_phase[j]; + node_data.area[phase[j]] = area[j]; + area_exact[j] = cut_measure_mffc( cut, ntk.index_to_node( node_index ), phase[j] ); + node_data.phase[phase[j]] = old_phase; + node_data.area[phase[j]] = old_area; + } + else + { + area_exact[j] = area[j]; + } + + /* Add output inverter cost if mapping a non referenced phase */ + if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 ) + { + area_exact[j] += lib_inv_area; + } + } + + /* check quality: TODO add output inverter in the cost if necessary */ + float best_exact_area_total = 0; + float area_exact_total = 0; + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + best_exact_area_total += best_exact_area[j]; + area_exact_total += area_exact[j]; + } + + /* not better than individual gates */ + if ( !valid || ( area_exact_total > best_exact_area_total - epsilon && respects_required ) ) + { + continue; + } + + mapped_multioutput = true; + + /* commit multi-output gate (topo order) */ + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + uint8_t mapped_phase = phase[j]; + best_exact_area[j] = area_exact[j]; + + if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 ) + { + best_exact_area[j] += lib_inv_area; + } + + /* write data */ + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = &gate; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j]; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + + node_data.flows[mapped_phase] = area_exact[j]; /* partial exact area contribution */ + /* select opposite phase */ + mapped_phase ^= 1; + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = nullptr; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = area_exact[j]; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + } + } + + return mapped_multioutput; + } + + template + void multi_node_update( node const& n ) + { + uint32_t check_index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0]; + uint64_t signature = 0; + + /* check if a node is in TFI: there is a path of length > 1 */ + bool in_tfi = false; + node min_node = n; + for ( auto j = 0; j < max_multioutput_output_size - 1; ++j ) + { + if ( tuple_data[j].in_tfi ) + { + min_node = ntk.index_to_node( tuple_data[j].node_index ); + in_tfi = true; + signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f ); + } + } + + if ( !in_tfi ) + return; + + /* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */ + ntk.incr_trav_id(); + ntk.foreach_fanin( n, [&]( auto const& f ) { + /* TODO: this recursion works as it is for a maximum multioutput value of 2 */ + multi_node_update_rec( ntk.get_node( f ), min_node + 1, signature ); + } ); + } + + template + void multi_node_update_rec( node const& n, uint32_t min_index, uint64_t& signature ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( index < min_index ) + return; + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_node_update_rec( ntk.get_node( f ), min_index, signature ); + } ); + + /* update the node if uses an updated leaf */ + auto& node_data = node_match[index]; + bool leaf_used = multi_node_update_cut_check( index, signature, 0 ); + + if ( !node_data.same_match ) + leaf_used |= multi_node_update_cut_check( index, signature, 1 ); + + if ( !leaf_used ) + return; + + signature |= UINT64_C( 1 ) << ( index & 0x3f ); + + /* avoid cycles by recomputing arrival times for multi-output gates or decomposing them */ + if ( node_data.same_match && node_data.multioutput_match[0] ) + { + propagate_arrival_node( n ); + /* check required time */ + if ( node_data.arrival[0] < node_data.required[0] + epsilon && node_data.arrival[1] < node_data.required[1] + epsilon ) + return; + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + } + + template + void multi_node_update_exact( node const& n ) + { + uint32_t check_index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0]; + uint64_t signature = 0; + + /* check if a node is in TFI: there is a path of length > 1 */ + bool in_tfi = false; + node min_node = n; + for ( auto j = 0; j < max_multioutput_output_size - 1; ++j ) + { + if ( tuple_data[j].in_tfi ) + { + min_node = ntk.index_to_node( tuple_data[j].node_index ); + in_tfi = true; + signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f ); + } + } + + if ( !in_tfi ) + return; + + /* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */ + ntk.incr_trav_id(); + ntk.foreach_fanin( n, [&]( auto const& f ) { + /* TODO: this recursion works as it is for a maximum multioutput value of 2 */ + multi_node_update_exact_rec( ntk.get_node( f ), min_node + 1, signature ); + } ); + } + + template + void multi_node_update_exact_rec( node const& n, uint32_t min_index, uint64_t& signature ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( index < min_index ) + return; + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_node_update_exact_rec( ntk.get_node( f ), min_index, signature ); + } ); + + /* update the node if uses an updated leaf */ + auto& node_data = node_match[index]; + bool leaf_used = multi_node_update_cut_check( index, signature, 0 ); + + if ( !node_data.same_match ) + leaf_used |= multi_node_update_cut_check( index, signature, 1 ); + + if ( !leaf_used ) + return; + + signature |= UINT64_C( 1 ) << ( index & 0x3f ); + + assert( !node_data.multioutput_match[0] ); + assert( !node_data.multioutput_match[1] ); + + if ( node_data.same_match && ( node_data.map_refs[0] || node_data.map_refs[1] ) ) + { + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, n, use_phase ); + } + + /* match positive phase */ + match_phase_exact( n, 0u ); + + /* match negative phase */ + match_phase_exact( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + assert( node_data.arrival[0] < std::numeric_limits::max() ); + assert( node_data.arrival[1] < std::numeric_limits::max() ); + } + + inline void match_multioutput_propagate_required( node const& n ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + const auto node_index = tuple_data[j].node_index; + match_propagate_required( node_index ); + } + } + + void match_multi_add_cuts( node const& n ) + { + /* assume a single cut (current version) */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t& matches = multi_node_match[node_tuple_match[index].index][0]; + + /* find the corresponding cut */ + uint32_t cut_p = 0; + while ( matches[cut_p].node_index != index ) + ++cut_p; + + assert( cut_p < matches.size() ); + uint32_t cut_index = matches[cut_p].cut_index; + auto& cut = multi_cut_set[cut_index][cut_p]; + auto single_cut = multi_cut_set[cut_index][cut_p]; + auto& rcuts = cuts[index]; + + /* not enough space in the data structure: abort */ + if ( rcuts.size() == max_cut_num ) + { + match_multi_add_cuts_remove_entry( matches ); + return; + } + + /* insert single cut variation if unique (for delay preservation) */ + if ( !rcuts.is_contained( single_cut ) ) + { + single_cut->pattern_index = 0; + compute_cut_data( single_cut, ntk.index_to_node( index ) ); + rcuts.append_cut( single_cut ); + + /* not enough space in the data structure: abort */ + if ( rcuts.size() == max_cut_num ) + { + rcuts.limit( rcuts.size() - 1 ); + match_multi_add_cuts_remove_entry( matches ); + return; + } + } + + /* add multi-output cut */ + uint32_t num_cuts_pre = rcuts.size(); + cut->ignore = true; + rcuts.append_cut( cut ); + + uint32_t num_cuts_after = rcuts.size(); + assert( num_cuts_after == num_cuts_pre + 1 ); + + rcuts.limit( num_cuts_pre ); + + /* update tuple data */ + matches[cut_p].cut_index = num_cuts_pre; + } + + inline void match_multi_add_cuts_remove_entry( multi_match_t const& matches ) + { + /* reset matches */ + for ( multi_match_data const& entry : matches ) + { + node_tuple_match[entry.node_index].data = 0; + } + } + + inline bool multi_node_update_cut_check( uint32_t index, uint64_t signature, uint8_t phase ) + { + auto const& cut = cuts[index][node_match[index].best_cut[phase]]; + + if ( ( signature & cut.signature() ) > 0 ) + return true; + + return false; + } +#pragma endregion + +#pragma region Mapping utils + inline double cut_leaves_flow( cut_t const& cut, node const& n, uint8_t phase ) + { + double flow{ 0.0f }; + auto const& node_data = node_match[ntk.node_to_index( n )]; + + uint8_t ctr = 0u; + for ( auto leaf : cut ) + { + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + flow += node_match[leaf].flows[leaf_phase]; + } + + return flow; + } + + template + float cut_ref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Recursive referencing if leaf was not referenced */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_deref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* dereference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( --node_match[leaf].map_refs[1] == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + --node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */ + if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + /* Recursive dereferencing */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_deref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + if ( --node_match[leaf].map_refs[leaf_phase] == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_deref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_measure_mffc( cut_t const& cut, node const& n, uint8_t phase ) + { + tmp_visited.clear(); + + float count = cut_ref_visit( cut, n, phase ); + + /* dereference visited */ + for ( auto s : tmp_visited ) + { + uint32_t leaf = s >> 1; + --node_match[leaf].map_refs[s & 1]; + } + + return count; + } + + template + float cut_ref_visit( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* add to visited */ + tmp_visited.push_back( ( static_cast( leaf ) << 1 ) | leaf_phase ); + + if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Recursive referencing if leaf was not referenced */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref_visit( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref_visit( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } +#pragma endregion + +#pragma region Initialize and dump the mapped network + void insert_buffers() + { + if ( lib_buf_id != UINT32_MAX ) + { + double area_old = area; + bool buffers = false; + + ntk.foreach_po( [&]( auto const& f ) { + auto const& n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) && ntk.is_pi( n ) && !ntk.is_complemented( f ) ) + { + area += lib_buf_area; + delay = std::max( delay, node_match[ntk.node_to_index( n )].arrival[0] + lib_inv_delay ); + buffers = true; + } + } ); + + /* round stats */ + if ( ps.verbose && buffers ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + area_gain = float( ( area_old - area ) / area_old * 100 ); + + stats << fmt::format( "[i] Buffering: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + st.round_stats.push_back( stats.str() ); + } + } + } + + std::pair, klut_map> initialize_map_network() + { + binding_view dest( library.get_gates() ); + klut_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + std::pair, block_map> initialize_block_network() + { + cell_view dest( library.get_cells() ); + block_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + void init_topo_order() + { + topo_order.reserve( ntk.size() ); + + if ( multi_node_match.size() > 0 ) + { + multi_init_topo_order(); + return; + } + + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + } + + bool init_arrivals() + { + if ( ps.required_times.size() && ps.required_times.size() != ntk.num_pos() ) + { + std::cerr << "[e] MAP ERROR: required time vector does not match the output size of the network" << std::endl; + st.mapping_error = true; + return false; + } + + if ( ps.arrival_times.empty() ) + { + ntk.foreach_pi( [&]( auto const& n ) { + auto& node_data = node_match[ntk.node_to_index( n )]; + node_data.arrival[0] = node_data.best_alternative[0].arrival = 0; + node_data.arrival[1] = node_data.best_alternative[1].arrival = lib_inv_delay; + } ); + return true; + } + + if ( ps.arrival_times.size() != ntk.num_pis() ) + { + std::cerr << "[e] MAP ERROR: arrival time vector does not match the input size of the network" << std::endl; + st.mapping_error = true; + return false; + } + + ntk.foreach_pi( [&]( auto const& n, uint32_t i ) { + auto& node_data = node_match[ntk.node_to_index( n )]; + node_data.arrival[0] = node_data.best_alternative[0].arrival = ps.arrival_times[i]; + node_data.arrival[1] = node_data.best_alternative[1].arrival = ps.arrival_times[i] + lib_inv_delay; + } ); + + return true; + } + + void finalize_cover( binding_view& res, klut_map& old2new ) + { + uint32_t multioutput_count = 0; + + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[n][0] ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + clone_box( res, old2new, index ); + continue; + } + } + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_binding( res.get_node( old2new[index][phase ^ 1] ), lib_inv_id ); + } + + /* count multioutput gates */ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] ) + { + ++multioutput_count; + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* count multioutput gates */ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] ) + { + ++multioutput_count; + } + } + + st.multioutput_gates = multioutput_count; + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_binding( res.get_node( buf ), lib_buf_id ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + /* write final results */ + st.area = area; + st.delay = delay; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + void finalize_cover_block( cell_view& res, block_map& old2new ) + { + uint32_t multioutput_count = 0; + + /* get standard cells */ + std::vector const& lib = res.get_library(); + + /* get translation ID from GENLIB to STD_CELL */ + std::vector genlib_to_cell( library.get_gates().size() ); + for ( standard_cell const& cell : lib ) + { + for ( gate const& g : cell.gates ) + { + genlib_to_cell[g.id] = cell.id; + } + } + + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[n][0] ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell[lib_inv_id] ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + clone_box2( res, old2new, index, genlib_to_cell ); + continue; + } + } + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + /* create multioutput gates */ + if ( ps.map_multioutput && node_data.multioutput_match[phase] ) + { + assert( node_data.same_match == true ); + + if ( node_tuple_match[index].has_info && node_tuple_match[index].lowest_index ) + { + ++multioutput_count; + create_block_for_gate( res, old2new, index, phase, genlib_to_cell ); + } + continue; + } + + create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_cell( res.get_node( old2new[index][phase ^ 1] ), genlib_to_cell[lib_inv_id] ); + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + assert( !ps.map_multioutput || !node_data.multioutput_match[phase] ); + create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell ); + } + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_cell( res.get_node( buf ), genlib_to_cell[lib_buf_id] ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + /* write final results */ + st.area = area; + st.delay = delay; + st.multioutput_gates = multioutput_count; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + void create_lut_for_gate( binding_view& res, klut_map& old2new, uint32_t index, unsigned phase ) + { + auto const& node_data = node_match[index]; + auto const& best_cut = cuts[index][node_data.best_cut[phase]]; + auto const& gate = node_data.best_gate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_binding( res.get_node( f ), gate->root->id ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate_rec( res, *gate, children ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + signal create_lut_for_gate_rec( binding_view& res, composed_gate const& gate, std::vector> const& children ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate_rec( res, *fanin, children ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_binding( res.get_node( f ), gate.root->id ); + return f; + } + + void create_lut_for_gate2( cell_view& res, block_map& old2new, uint32_t index, unsigned phase, std::vector const& genlib_to_cell ) + { + auto const& node_data = node_match[index]; + auto const& best_cut = cuts[index][node_data.best_cut[phase]]; + auto const& gate = node_data.best_gate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( gate->root->id ) ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate2_rec( res, *gate, children, genlib_to_cell ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + signal create_lut_for_gate2_rec( cell_view& res, composed_gate const& gate, std::vector> const& children, std::vector const& genlib_to_cell ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate2_rec( res, *fanin, children, genlib_to_cell ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( gate.root->id ) ); + return f; + } + + void create_block_for_gate( cell_view& res, block_map& old2new, uint32_t index, unsigned phase, std::vector const& genlib_to_cell ) + { + std::vector const& lib = res.get_library(); + composed_gate const* local_gate = node_match[index].best_gate[phase]->root; + standard_cell const& cell = lib[genlib_to_cell.at( local_gate->root->id )]; + + assert( !local_gate->is_super ); + auto const& best_cut = cuts[index][node_match[index].best_cut[phase]]; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( cell.gates.front().num_vars ); + + /* output negations have already been assigned by the mapper */ + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= local_gate->num_vars ) + break; + children[node_match[index].best_gate[phase]->permutation[ctr]] = old2new[l][( node_match[index].phase[phase] >> ctr ) & 1]; + ++ctr; + } + + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + std::vector outputs; + std::vector functions; + + /* re-order outputs to match the ones of the cell */ + for ( gate const& g : cell.gates ) + { + /* find the correct node */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + assert( node_match[node_index].same_match ); + uint8_t node_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + assert( node_match[node_index].multioutput_match[node_phase] ); + + gate const* node_gate = node_match[node_index].best_gate[node_phase]->root->root; + + /* wrong output */ + if ( node_gate->id != g.id ) + continue; + + outputs.push_back( node_index ); + functions.push_back( g.function ); + } + } + + assert( outputs.size() == cell.gates.size() ); + + /* create the block */ + auto f = res.create_node( children, functions ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( local_gate->root->id ) ); + + for ( uint32_t s : outputs ) + { + /* add inverted version if used */ + uint8_t node_phase = node_match[s].best_gate[0] != nullptr ? 0 : 1; + assert( node_match[s].same_match ); + + /* add the node in the data structure */ + old2new[s][node_phase] = f; + + if ( node_match[s].map_refs[node_phase ^ 1] > 0 ) + { + old2new[s][node_phase ^ 1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[s][node_phase ^ 1] ), genlib_to_cell.at( lib_inv_id ) ); + } + + f = res.next_output_pin( f ); + } + } + + void clone_box( binding_view& res, klut_map& old2new, uint32_t index ) + { + node n = ntk.index_to_node( index ); + std::vector> children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] ); + } ); + + /* create the node */ + auto const& tt = ntk.node_function( n ); + auto f = res.create_node( children, tt ); + + /* add the node in the data structure */ + old2new[index][0] = f; + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + + if constexpr ( has_has_binding_v ) + { + if ( ntk.has_binding( n ) ) + res.add_binding( res.get_node( f ), ntk.get_binding_index( n ) ); + } + } + + void clone_box2( cell_view& res, klut_map& old2new, uint32_t index, std::vector const& genlib_to_cell ) + { + node n = ntk.index_to_node( index ); + std::vector> children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] ); + } ); + + /* check if multi-output */ + std::vector const& lib = res.get_library(); + if constexpr ( has_has_binding_v ) + { + bool is_multioutput = false; + if ( ntk.has_binding( n ) ) + { + uint32_t cell_id = genlib_to_cell.at( ntk.get_binding_index( n ) ); + if ( lib.at( cell_id ).gates.size() > 1 ) + is_multioutput = true; + } + + /* create the multioutput node (partially dangling) */ + if ( is_multioutput ) + { + standard_cell const& cell = lib.at( genlib_to_cell.at( ntk.get_binding_index( n ) ) ); + std::vector functions; + for ( auto const& g : cell.gates ) + { + functions.push_back( g.function ); + } + + auto f = res.create_node( children, functions ); + + /* find and connect the correct pin */ + for ( auto const& g : cell.gates ) + { + if ( g.id == cell.id ) + break; + res.next_output_pin( f ); + } + + old2new[index][0] = f; + res.add_cell( res.get_node( f ), cell.id ); + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) ); + } + return; + } + } + + /* create the single-output node */ + auto const& tt = ntk.node_function( n ); + auto f = res.create_node( children, tt ); + + /* add the node in the data structure */ + old2new[index][0] = f; + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) ); + } + + if constexpr ( has_has_binding_v ) + { + if ( ntk.has_binding( n ) ) + res.add_cell( res.get_node( f ), genlib_to_cell.at( ntk.get_binding_index( n ) ) ); + } + } +#pragma endregion + +#pragma region Cuts and matching utils + void compute_cut_data( cut_t& cut, node const& n ) + { + cut->delay = std::numeric_limits::max(); + cut->flow = std::numeric_limits::max(); + cut->ignore = false; + + if ( cut.size() > NInputs || cut.size() > 6 ) + { + /* Ignore cuts too big to be mapped using the library */ + cut->ignore = true; + return; + } + + const auto tt = cut->function; + const kitty::static_truth_table<6> fe = kitty::extend_to<6>( tt ); + auto fe_canon = fe; + + uint16_t negations_pos = 0; + uint16_t negations_neg = 0; + + /* match positive polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( fe, cut.size() ); + fe_canon = std::get<0>( canon ); + negations_pos = std::get<1>( canon ); + } + + auto const supergates_pos = library.get_supergates( fe_canon ); + + /* match negative polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( ~fe, cut.size() ); + fe_canon = std::get<0>( canon ); + negations_neg = std::get<1>( canon ); + } + else + { + fe_canon = ~fe; + } + + auto const supergates_neg = library.get_supergates( fe_canon ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut->supergates = { supergates_pos, supergates_neg }; + cut->negations = { negations_pos, negations_neg }; + } + else + { + /* Ignore not matched cuts */ + cut->ignore = true; + return; + } + + /* compute cut cost based on LUT area */ + recompute_cut_data( cut, n ); + } + + void compute_cut_data_structural( cut_t& cut, node const& n ) + { + cut->delay = std::numeric_limits::max(); + cut->flow = std::numeric_limits::max(); + cut->ignore = false; + + assert( cut.size() <= NInputs ); + + const auto supergates_pos = library.get_supergates_pattern( cut->pattern_index, false ); + const auto supergates_neg = library.get_supergates_pattern( cut->pattern_index, true ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut->supergates = { supergates_pos, supergates_neg }; + } + else + { + /* Ignore not matched cuts */ + cut->ignore = true; + return; + } + + /* compute cut cost based on LUT area */ + recompute_cut_data( cut, n ); + } + + void recompute_cut_data( cut_t& cut, node const& n ) + { + /* compute cut cost based on LUT area */ + uint32_t best_arrival = 0; + float best_area_flow = cut.size() > 1 ? cut.size() : 0; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + best_arrival = std::max( best_arrival, best_leaf_cut->delay ); + best_area_flow += best_leaf_cut->flow; + } + + cut->delay = best_arrival + ( cut.size() > 1 ) ? 1 : 0; + cut->flow = best_area_flow / ntk.fanout_size( n ); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + void compute_truth_table_support( cut_t const& sub, cut_t const& sup, TT& tt ) + { + size_t j = 0; + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + lsupport[j++] = static_cast( std::distance( sup.begin(), itp ) ); + } + + /* swap variables in the truth table */ + for ( int i = j - 1; i >= 0; --i ) + { + assert( i <= lsupport[i] ); + kitty::swap_inplace( tt, i, lsupport[i] ); + } + } + + void add_zero_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + cut->ignore = true; + cut->delay = 0; + cut->flow = 0; + cut->pattern_index = 0; + cut->negations[0] = cut->negations[1] = 0; + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index + 1 ); + + kitty::create_nth_var( cut->function, 0 ); + cut->ignore = true; + cut->delay = 0; + cut->flow = 0; + cut->pattern_index = 1; + cut->negations[0] = cut->negations[1] = 0; + } + + inline void create_structural_cut( cut_t& new_cut, std::vector const& vcuts, uint32_t new_pattern, uint32_t pattern_id1, uint32_t pattern_id2 ) + { + new_cut.set_leaves( *vcuts[0] ); + new_cut.add_leaves( vcuts[1]->begin(), vcuts[1]->end() ); + new_cut->pattern_index = new_pattern; + + /* get the polarity of the leaves of the new cut */ + uint16_t neg_l = 0, neg_r = 0; + if ( ( *vcuts[0] )->pattern_index == 1 ) + { + neg_r = static_cast( pattern_id1 & 1 ); + } + else + { + neg_r = ( *vcuts[0] )->negations[0]; + } + if ( ( *vcuts[1] )->pattern_index == 1 ) + { + neg_l = static_cast( pattern_id2 & 1 ); + } + else + { + neg_l = ( *vcuts[1] )->negations[0]; + } + + new_cut->negations[0] = ( neg_l << vcuts[0]->size() ) | neg_r; + new_cut->negations[1] = new_cut->negations[0]; + } + + inline bool fast_support_minimization( TT const& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + } + + return true; + } + + void compute_truth_table( uint32_t index, fanin_cut_t const& vcuts, uint32_t fanin, cut_t& res ) + { + for ( uint32_t i = 0; i < fanin; ++i ) + { + cut_t const* cut = vcuts[i]; + ltruth[i] = ( *cut )->function; + compute_truth_table_support( *cut, res, ltruth[i] ); + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), ltruth.begin(), ltruth.begin() + fanin ); + + if ( ps.cut_enumeration_ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + + res->function = tt_res; + } +#pragma endregion + + template + inline bool compare_map( double arrival, double best_arrival, float area_flow, float best_area_flow, uint32_t size, uint32_t best_size ) + { + if constexpr ( DO_AREA ) + { + if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + else if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + return size < best_size; + } + else + { + if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + else if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + return size < best_size; + } + } + + double compute_switching_power() + { + double power = 0.0f; + + for ( auto const& n : topo_order ) + { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + } + + phase = phase ^ 1; + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + } + } + + return power; + } + +#pragma region multioutput + /* Experimental code */ + void compute_multioutput_match() + { + stopwatch t( st.time_multioutput ); + + if ( library.num_multioutput_gates() == 0 ) + return; + + /* compute cuts: first simple method without proper matching */ + cut_enumeration_params multi_ps; + multi_ps.minimize_truth_table = false; + multi_cuts_t multi_cuts = fast_cut_enumeration( ntk, multi_ps ); + + /* cuts leaves classes */ + multi_hash_t multi_cuts_classes; + multi_cuts_classes.reserve( 2000 ); + + /* Multi-output matching */ + multi_enumerate_matches( multi_cuts, multi_cuts_classes ); + + multi_single_matches_t multi_node_match_local; + multi_node_match_local.reserve( multi_cuts_classes.size() ); + + multi_compute_matches( multi_cuts, multi_cuts_classes, multi_node_match_local ); + + if ( ps.remove_overlapping_multicuts ) + multi_filter_and_match( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */ + else + multi_filter_and_match( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */ + } + + void multi_init_topo_order() + { + /* create and initialize a choice view to store the tuples */ + choice_view choice_ntk{ ntk }; + multi_add_choices( choice_ntk ); + + ntk.incr_trav_id(); + ntk.incr_trav_id(); + + /* add constants and CIs */ + const auto c0 = ntk.get_node( ntk.get_constant( false ) ); + topo_order.push_back( c0 ); + ntk.set_visited( c0, ntk.trav_id() ); + + if ( const auto c1 = ntk.get_node( ntk.get_constant( true ) ); ntk.visited( c1 ) != ntk.trav_id() ) + { + topo_order.push_back( c1 ); + ntk.set_visited( c1, ntk.trav_id() ); + } + + ntk.foreach_ci( [&]( auto const& n ) { + if ( ntk.visited( n ) != ntk.trav_id() ) + { + topo_order.push_back( n ); + ntk.set_visited( n, ntk.trav_id() ); + } + } ); + + /* sort topologically */ + ntk.foreach_co( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + return; + multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + } ); + } + + /* Experimental code resticted to only half adders and full adders */ + void multi_enumerate_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes ) + { + static_assert( max_multioutput_cut_size > 1 && max_multioutput_cut_size < 7 ); + + uint32_t counter = 0; + multi_leaves_set_t leaves = { 0 }; + + ntk.foreach_gate( [&]( auto const& n ) { + uint32_t cut_index = 0; + for ( auto& cut : multi_cuts.cuts( ntk.node_to_index( n ) ) ) + { + kitty::static_truth_table tt = multi_cuts.truth_table( *cut ); + /* reduce support for matching ID */ + uint64_t tt_id = ( cut->size() < 3 ) ? ( tt._bits & 0xF ) : tt._bits; + uint64_t id = library.get_multi_function_id( tt_id ); + + if ( !id ) + { + ++cut_index; + continue; + } + + ( *cut )->data.id = id; + + multi_match_data data; + data.node_index = ntk.node_to_index( n ); + data.cut_index = cut_index; + leaves[2] = 0; + uint32_t i = 0; + for ( auto l : *cut ) + leaves[i++] = l; + + /* add to hash table */ + multi_cuts_classes[leaves].push_back( data ); + + ++cut_index; + } + } ); + } + + /* Experimental code */ + void multi_compute_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes, multi_single_matches_t& multi_node_match_local ) + { + ntk.clear_values(); + + /* copy set and sort by gate size: improve, too slow */ + std::vector> class_list; + class_list.reserve( multi_cuts_classes.size() ); + for ( auto& it : multi_cuts_classes ) + { + /* insert multiple occurring cuts */ + if ( it.second.size() > 1 ) + class_list.push_back( it ); + } + + std::stable_sort( class_list.begin(), class_list.end(), [&]( auto const& a, auto const& b ) { + return a.first[2] > b.first[2]; + } ); + + /* combine and match: specific code for 2-output cells */ + for ( auto it : class_list ) + { + for ( uint32_t i = 0; i < it.second.size() - 1; ++i ) + { + multi_match_data data_i = it.second[i]; + uint32_t index_i = data_i.node_index; + uint32_t cut_index_i = data_i.cut_index; + auto const& cut_i = multi_cuts.cuts( index_i )[cut_index_i]; + + for ( uint32_t j = i + 1; j < it.second.size(); ++j ) + { + multi_match_data data_j = it.second[j]; + uint32_t index_j = data_j.node_index; + uint32_t cut_index_j = data_j.cut_index; + auto const& cut_j = multi_cuts.cuts( index_j )[cut_index_j]; + + /* not compatible -> TODO: change */ + if ( cut_i->data.id == cut_j->data.id ) + continue; + + /* check compatibility */ + if ( !multi_check_partally_dangling( index_i, index_j, cut_i ) ) + continue; + + multi_node_match_local.push_back( { data_i, data_j } ); + } + } + } + } + + /* Experimental code */ + template + void multi_filter_and_match( multi_cuts_t const& multi_cuts, multi_single_matches_t const& multi_node_match_local ) + { + multi_cut_set.reserve( multi_node_match_local.size() ); + multi_node_match.reserve( multi_node_match_local.size() ); + + ntk.incr_trav_id(); + + for ( auto& pair : multi_node_match_local ) + { + uint32_t index1 = pair[0].node_index; + uint32_t index2 = pair[1].node_index; + uint32_t cut_index1 = pair[0].cut_index; + uint32_t cut_index2 = pair[1].cut_index; + multi_cut_t const& cut1 = multi_cuts.cuts( index1 )[cut_index1]; + multi_cut_t const& cut2 = multi_cuts.cuts( index2 )[cut_index2]; + + assert( index1 < index2 ); + + /* remove incompatible multi-output cuts */ + bool is_new = true; + uint32_t insertion_index = multi_node_match.size(); + if constexpr ( OverlapFilter ) + { + if ( multi_gate_check_overlapping( index1, index2, cut1 ) ) + continue; + } + else + { + if ( multi_gate_check_incompatible( index1, index2, is_new, insertion_index ) ) + continue; + // if ( is_new && multi_gate_check_overlapping( index1, index2, cut1 ) ) + // continue; + } + + /* copy cuts */ + cut_t new_cut1, new_cut2; + new_cut1.set_leaves( cut1.begin(), cut1.end() ); + new_cut2.set_leaves( cut2.begin(), cut2.end() ); + new_cut1->function = kitty::extend_to<6>( multi_cuts.truth_table( cut1 ) ); + new_cut2->function = kitty::extend_to<6>( multi_cuts.truth_table( cut2 ) ); + + /* Multi-output Boolean matching, continue if no match */ + std::array cut_pair = { new_cut1, new_cut2 }; + if ( !multi_compute_cut_data( cut_pair ) ) + continue; + + /* mark multioutput gate */ + if constexpr ( OverlapFilter ) + { + multi_gate_mark_visited( index1, index2, cut1 ); + node_tuple_match[index1].has_info = 1; + node_tuple_match[index1].lowest_index = 1; + node_tuple_match[index1].index = multi_node_match.size(); + node_tuple_match[index2].has_info = 1; + node_tuple_match[index2].highest_index = 1; + node_tuple_match[index2].index = multi_node_match.size(); + } + else + { + // multi_gate_mark_visited( index1, index2, cut1 ); + multi_gate_mark_compatibility( index1, index2, insertion_index ); + } + + /* add cut */ + multi_cut_set.push_back( cut_pair ); + + /* re-index data */ + multi_match_data new_data1, new_data2; + new_data1.node_index = index1; + new_data1.cut_index = multi_cut_set.size() - 1; + new_data2.node_index = index2; + new_data2.cut_index = multi_cut_set.size() - 1; + multi_match_t p = { new_data1, new_data2 }; + + /* add cuts to the correct bucket */ + if ( is_new ) + { + multi_node_match.push_back( { p } ); + } + else + { + multi_node_match[insertion_index].push_back( p ); + } + } + } + + bool multi_compute_cut_data( std::array& cut_tuple ) + { + std::array, max_multioutput_output_size> tts; + std::array, max_multioutput_output_size> tts_order; + std::array order = {}; + std::array phase = { 0 }; + std::array phase_order; + + std::iota( order.begin(), order.end(), 0 ); + + for ( auto i = 0; i < max_multioutput_output_size; ++i ) + { + tts[i] = kitty::extend_to<6>( cut_tuple[i]->function ); + if ( ( tts[i]._bits & 1 ) == 1 ) + { + tts[i] = ~tts[i]; + phase[i] = 1; + } + } + + std::stable_sort( order.begin(), order.end(), [&]( size_t a, size_t b ) { + return tts[a] < tts[b]; + } ); + + std::transform( order.begin(), order.end(), tts_order.begin(), [&]( size_t a ) { + return tts[a]; + } ); + + std::transform( order.begin(), order.end(), phase_order.begin(), [&]( uint8_t a ) { + return phase[a]; + } ); + + auto const multigates_match = library.get_multi_supergates( tts_order ); + + /* Ignore not matched cuts */ + if ( multigates_match == nullptr ) + return false; + + /* add cut matches */ + for ( auto i = 0; i < max_multioutput_output_size; ++i ) + { + cut_tuple[order[i]]->supergates[0] = nullptr; + cut_tuple[order[i]]->supergates[1] = nullptr; + cut_tuple[order[i]]->ignore = false; + std::vector> const* multigate = &( ( *multigates_match )[i] ); + cut_tuple[order[i]]->supergates[phase_order[i]] = multigate; + } + + return true; + } + + inline bool multi_check_partally_dangling( uint32_t index1, uint32_t index2, multi_cut_t const& cut1 ) + { + bool valid = true; + + /* check containment of cut1 in cut2 and viceversa */ + if ( index1 > index2 ) + { + std::swap( index1, index2 ); + } + + ntk.foreach_fanin( ntk.index_to_node( index2 ), [&]( auto const& f ) { + auto g = ntk.get_node( f ); + if ( ntk.node_to_index( g ) == index1 && ntk.fanout_size( g ) == 1 ) + { + valid = false; + } + return valid; + } ); + + if ( !valid ) + return false; + + if ( !is_contained_mffc( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut1 ) ) + return false; + + return true; + } + + inline bool multi_gate_check_overlapping( uint32_t index1, uint32_t index2, multi_cut_t const& cut ) + { + bool contained = false; + + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + contained = multi_mark_visited_rec( ntk.index_to_node( index1 ) ); + contained |= multi_mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + inline bool multi_gate_check_incompatible( uint32_t index1, uint32_t index2, bool& is_new, uint32_t& data_index ) + { + /* check cut assigned cut outputs, specialized code for 2 outputs */ + if ( !node_tuple_match[index1].has_info && !node_tuple_match[index2].has_info ) + return false; + + if ( node_tuple_match[index1].has_info && node_tuple_match[index2].has_info ) + { + uint32_t current_assignment = node_tuple_match[index1].index; + if ( current_assignment != node_tuple_match[index2].index ) + return true; + is_new = false; + data_index = current_assignment; + return false; + } + + return true; + } + + inline void multi_gate_mark_compatibility( uint32_t index1, uint32_t index2, uint32_t mark_value ) + { + node_tuple_match[index1].has_info = 1; + node_tuple_match[index1].lowest_index = 1; + node_tuple_match[index1].index = mark_value; + node_tuple_match[index2].has_info = 1; + node_tuple_match[index2].highest_index = 1; + node_tuple_match[index2].index = mark_value; + } + + inline void multi_gate_mark_visited( uint32_t index1, uint32_t index2, multi_cut_t const& cut ) + { + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + /* mark */ + multi_mark_visited_rec( ntk.index_to_node( index1 ) ); + multi_mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + } + + template + bool multi_mark_visited_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return false; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + if constexpr ( MARK ) + { + ntk.set_visited( n, ntk.trav_id() ); + } + + bool contained = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + contained |= multi_mark_visited_rec( ntk.get_node( f ) ); + + if constexpr ( !MARK ) + { + if ( contained ) + return false; + } + + return true; + } ); + + return contained; + } + + bool is_contained_mffc( node root, node n, multi_cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + bool valid = true; + tmp_visited.clear(); + dereference_node_rec( root ); + + if ( ntk.fanout_size( n ) == 0 ) + valid = false; + + for ( uint64_t g : tmp_visited ) + ntk.incr_fanout_size( ntk.index_to_node( g ) ); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return valid; + } + + void dereference_node_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + node g = ntk.get_node( f ); + if ( ntk.decr_fanout_size( g ) == 0 ) + { + dereference_node_rec( g ); + } + tmp_visited.push_back( ntk.node_to_index( g ) ); + } ); + } + + void multi_add_choices( choice_view& choice_ntk ) + { + for ( auto& field : multi_node_match ) + { + auto& pair = field.front(); + uint32_t index1 = pair[0].node_index; + uint32_t index2 = pair[1].node_index; + uint32_t cut_index1 = pair[0].cut_index; + cut_t const& cut = multi_cut_set[cut_index1][0]; + + /* don't add choice if in TFI, set TFI bit */ + if ( multi_is_in_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ) ) + { + /* if there is a path of length > 1 linking node 1 and 2, save as TFI node */ + uint32_t in_tfi = multi_is_in_direct_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ) ) ? 0 : 1; + for ( auto& match : field ) + match[0].in_tfi = in_tfi; + /* add a TFI dependency */ + ntk.set_value( ntk.index_to_node( index1 ), index2 ); + // multi_set_tfi_dependency( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ); + continue; + } + + choice_ntk.add_choice( ntk.index_to_node( index1 ), ntk.index_to_node( index2 ) ); + + assert( choice_ntk.count_choices( ntk.index_to_node( index1 ) ) == 2 ); + } + } + + bool multi_topo_sort_rec( choice_view& choice_ntk, node const& n ) + { + /* is permanently marked? */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + /* loop detected: backtrack to remove the cause */ + if ( ntk.visited( n ) == ntk.trav_id() - 1 ) + return false; + + /* get the representative (smallest index) */ + node repr = choice_ntk.get_choice_representative( n ); + + /* loop detected: backtrack to remove the cause */ + if ( ntk.visited( repr ) == ntk.trav_id() - 1 ) + return false; + + /* solve the TFI dependency first */ + node dependency_node = ntk.index_to_node( ntk.value( n ) ); + if ( dependency_node > 0 && ntk.visited( dependency_node ) != ntk.trav_id() - 1 ) + { + if ( !multi_topo_sort_rec( choice_ntk, dependency_node ) ) + return false; + assert( ntk.visited( n ) == ntk.trav_id() ); + return true; + } + + /* for all the choices */ + uint32_t i = 0; + bool check = true; + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited or temporarily marked */ + assert( ntk.visited( g ) != ntk.trav_id() ); + assert( ntk.visited( g ) != ntk.trav_id() - 1 ); + + /* mark node temporarily */ + ntk.set_visited( g, ntk.trav_id() - 1 ); + + /* mark children */ + ntk.foreach_fanin( g, [&]( auto const& f ) { + check = multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + return check; + } ); + + /* cycle detected: backtrack to the last choice jump */ + if ( !check ) + { + /* revert visited */ + ntk.set_visited( g, ntk.trav_id() - 2 ); + if ( i > 0 && n == repr ) + { + /* fix cycle: remove multi-output match */ + choice_ntk.foreach_choice( repr, [&]( auto const& p ) { + node_tuple_match[ntk.node_to_index( p )].data = 0; + return true; + } ); + choice_ntk.remove_choice( g ); + check = true; + } + return false; + } + + ++i; + return true; + } ); + + if ( !check ) + { + return false; + } + + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited */ + assert( ntk.visited( g ) != ntk.trav_id() ); + + /* mark node n permanently */ + ntk.set_visited( g, ntk.trav_id() ); + + /* visit node */ + topo_order.push_back( g ); + + return true; + } ); + + return true; + } + + inline bool multi_is_in_tfi( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + multi_mark_visited_rec( root ); + bool contained = ntk.visited( n ) == ntk.trav_id(); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + inline bool multi_is_in_direct_tfi( node const& root, node const& n ) + { + bool contained = false; + + ntk.foreach_fanin( root, [&]( auto const& f ) { + if ( ntk.get_node( f ) == n ) + contained = true; + } ); + + return contained; + } + + inline void multi_set_tfi_dependency( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + + /* add a TFI dependencies */ + ntk.set_value( n, ntk.node_to_index( root ) ); + ntk.set_visited( n, ntk.trav_id() ); + multi_set_tfi_dependency_rec( root, ntk.node_to_index( root ) ); + + /* reset root's dependency info */ + ntk.set_value( root, 0 ); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + } + + void multi_set_tfi_dependency_rec( node const& n, uint32_t const dependency_info ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.set_value( n, dependency_info ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_set_tfi_dependency_rec( ntk.get_node( f ), dependency_info ); + } ); + } +#pragma endregion + +private: + Ntk const& ntk; + tech_library const& library; + emap_params const& ps; + emap_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + double delay{ 0.0f }; /* current delay of the mapping */ + double area{ 0.0f }; /* current area of the mapping */ + uint32_t inv{ 0 }; /* current inverter count */ + + /* lib inverter info */ + float lib_inv_area; + float lib_inv_delay; + uint32_t lib_inv_id; + + /* lib buffer info */ + float lib_buf_area; + float lib_buf_delay; + uint32_t lib_buf_id; + + std::vector> topo_order; + node_match_t node_match; + std::vector node_tuple_match; + std::vector switch_activity; + std::vector tmp_visited; + + /* cut computation */ + std::vector cuts; /* compressed representation of cuts */ + cut_merge_t lcuts; /* cut merger container */ + cut_set_t temp_cuts; /* temporary cut set container */ + truth_compute_t ltruth; /* truth table merger container */ + support_t lsupport; /* support merger container */ + uint32_t cuts_total{ 0 }; /* current computed cuts */ + + /* multi-output matching */ + multi_cut_set_t multi_cut_set; /* set of multi-output cuts */ + multi_matches_t multi_node_match; /* matched multi-output gates */ + + time_point time_begin; +}; + +} /* namespace detail */ + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a block network that supports multi-output cells. + * + * The novelties of this mapper are contained in 2 publications: + * - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023. + * - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +cell_view emap( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_block(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a k-LUT network. Each LUT abstacts a gate of the technology library. + * + * The novelties of this mapper are contained in 2 publications: + * - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023. + * - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view emap_klut( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_klut(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology node mapping. + * + * This function implements a simple technology mapping algorithm. + * The algorithm maps each node to the best implementation in the technology library. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_binding` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view emap_node_map( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_node_map(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology node mapping. + * + * This function implements a simple technology mapping algorithm. + * The algorithm maps each node to the first implementation in the technology library. + * + * The input must be a binding_view with the gates correctly loaded. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_binding` + * + * \param ntk Network + * + */ +template +void emap_load_mapping( Ntk& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + + /* build the library map */ + using lib_t = std::unordered_map>; + lib_t tt_to_gate; + + for ( auto const& g : ntk.get_library() ) + { + tt_to_gate[g.function] = g.id; + } + + ntk.foreach_gate( [&]( auto const& n ) { + if ( auto it = tt_to_gate.find( ntk.node_function( n ) ); it != tt_to_gate.end() ) + { + ntk.add_binding( n, it->second ); + } + else + { + std::cout << fmt::format( "[e] node mapping for node {} failed: no match in the tech library\n", ntk.node_to_index( n ) ); + } + } ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/equivalence_checking.hpp b/third-party/mockturtle/include/mockturtle/algorithms/equivalence_checking.hpp new file mode 100644 index 00000000000..686cbfc32f9 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/equivalence_checking.hpp @@ -0,0 +1,325 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file equivalence_checking.hpp + \brief Combinational equivalence checking + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "cleanup.hpp" +#include "functional_reduction.hpp" +#include "../traits.hpp" +#include "../utils/include/percy.hpp" +#include "../utils/stopwatch.hpp" +#include "../networks/klut.hpp" +#include "cnf.hpp" + +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for equivalence_checking. + * + * The data structure `equivalence_checking_params` holds configurable + * parameters with default arguments for `equivalence_checking`. + */ +struct equivalence_checking_params +{ + /*! \brief Conflict limit for SAT solver. + * + * The default limit is 0, which means the number of conflicts is not used + * as a resource limit. + */ + uint32_t conflict_limit{ 0u }; + + /*! \brief Whether to apply functional reduction before SAT solving. */ + bool functional_reduction{ true }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for equivalence_checking. + * + * The data structure `equivalence_checking_stats` provides data collected by + * running `equivalence_checking`. + */ +struct equivalence_checking_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{}; + + /*! \brief Counter-example, in case miter is not equivalent. */ + std::vector counter_example; + + void report() const + { + if ( counter_example.size() > 0 ) + { + std::cout << "[i] Networks are not equivalent under input assignment: "; + for ( auto i = 0u; i < counter_example.size(); ++i ) + std::cout << "pi" << i << "=" << counter_example[i] << " "; + std::cout << "\n"; + } + + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class equivalence_checking_impl +{ +public: + equivalence_checking_impl( Ntk const& miter, equivalence_checking_params const& ps, equivalence_checking_stats& st ) + : miter_( miter ), + ps_( ps ), + st_( st ) + { + } + + std::optional run() + { + stopwatch<> t( st_.time_total ); + + percy::bsat_wrapper solver; + int output; + + if ( ps_.functional_reduction ) + { + Ntk opt = miter_.clone(); + if constexpr ( !std::is_same_v ) + { + functional_reduction( opt ); + opt = cleanup_dangling( opt ); + } + + if ( opt.num_gates() == 0 ) + { + return opt.po_at( 0 ) == opt.get_constant( false ); + } + + output = generate_cnf( opt, [&]( auto const& clause ) { + solver.add_clause( clause ); + } )[0]; + } + else + { + output = generate_cnf( miter_, [&]( auto const& clause ) { + solver.add_clause( clause ); + } )[0]; + } + + const auto res = solver.solve( &output, &output + 1, ps_.conflict_limit ); + + switch ( res ) + { + default: + return std::nullopt; + case percy::synth_result::success: + { + st_.counter_example.clear(); + for ( auto i = 1u; i <= miter_.num_pis(); ++i ) + { + st_.counter_example.push_back( solver.var_value( i ) ); + } + return false; + } + case percy::synth_result::failure: + return true; + } + } + +private: + Ntk const& miter_; + equivalence_checking_params const& ps_; + equivalence_checking_stats& st_; +}; + +template +class equivalence_checking_impl_bill +{ +public: + equivalence_checking_impl_bill( Ntk const& miter, equivalence_checking_params const& ps, equivalence_checking_stats& st ) + : miter_( miter ), + ps_( ps ), + st_( st ) + { + } + + std::optional run() + { + stopwatch<> t( st_.time_total ); + + bill::solver solver; + bill::lit_type output = convert_to_cnf( miter_, solver ); + + const auto res = solver.solve( {output}, ps_.conflict_limit ); + + switch ( res ) + { + default: + return std::nullopt; + case bill::result::states::satisfiable: + { + st_.counter_example.clear(); + for ( auto i = 1u; i <= miter_.num_pis(); ++i ) + { + st_.counter_example.push_back( solver.get_model().model().at( i ) == bill::lbool_type::true_ ); + } + return false; + } + case bill::result::states::unsatisfiable: + return true; + } + } + +private: + bill::lit_type convert_to_cnf( Ntk const& ntk, bill::solver& solver ) + { + node_map literals( ntk ); + + literals[ntk.get_constant( false )] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + literals[ntk.get_constant( true )] = lit_not( literals[ntk.get_constant( false )] ); + } + solver.add_clause( {~literals[ntk.get_constant( false )]} ); + + ntk.foreach_pi( [&]( auto const& n ) { + literals[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + } ); + ntk.foreach_gate( [&]( auto const& n ) { + literals[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + } ); + + if constexpr ( has_EXCDC_interface_v ) + { + ntk.add_EXCDC_clauses( solver ); + } + + return generate_cnf( ntk, [&]( bill::result::clause_type const& clause ) { + solver.add_clause( clause ); + }, literals )[0]; + } + +private: + Ntk const& miter_; + equivalence_checking_params const& ps_; + equivalence_checking_stats& st_; +}; + +} // namespace detail + +/*! \brief Combinational equivalence checking. + * + * This function expects as input a miter circuit that can be generated, e.g., + * with the function `miter`. It returns an optional which is `nullopt`, if no + * solution can be found (this happens when a resource limit is set using the + * function's parameters). Otherwise it returns `true`, if the miter is + * equivalent or `false`, if the miter is not equivalent. In the latter case + * the counter example is written to the statistics pointer as a + * `std::vector` following the same order as the primary inputs. + * + * \param miter Miter network + * \param ps Parameters + * \param st Statistics + */ +template +std::optional equivalence_checking( Ntk const& miter, equivalence_checking_params const& ps = {}, equivalence_checking_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + if ( miter.num_pos() != 1u ) + { + std::cout << "[e] miter network must have a single output\n"; + return std::nullopt; + } + + equivalence_checking_stats st; + detail::equivalence_checking_impl impl( miter, ps, st ); + const auto result = impl.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return result; +} + +template +std::optional equivalence_checking_bill( Ntk const& miter, equivalence_checking_params const& ps = {}, equivalence_checking_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + if ( miter.num_pos() != 1u ) + { + std::cout << "[e] miter network must have a single output\n"; + return std::nullopt; + } + + equivalence_checking_stats st; + detail::equivalence_checking_impl_bill impl( miter, ps, st ); + const auto result = impl.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return result; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/equivalence_classes.hpp b/third-party/mockturtle/include/mockturtle/algorithms/equivalence_classes.hpp new file mode 100644 index 00000000000..e13efa3765b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/equivalence_classes.hpp @@ -0,0 +1,156 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file equivalence_classes.hpp + \brief Synthesis routines based on equivalence classes + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Applies a sequence of transformations to a network + * + * A synthesis function `synthesis_fn` computes a network into `dest` and + * computes an output signal. Both the inputs and the outputs to that synthesis + * function are transformed according to `transformations`, a sequence of + * spectral transformations. The vector `leaves` are the original inputs to the + * output function, which is returned in terms of a signal into the network. + * + * The signature of `synthesis_fn` is `signal(Ntk&, std::vector> const&)`. + * + * \param dest Destination network for synthesis + * \param transformations Sequence of spectral operations (see kitty) + * \param leaves Original inputs, which might be transformed + * \param synthesis_fn Synthesis function to create the inner function (without transformations) + * + \verbatim embed:rst + + .. note:: + + An example on how to transform the AND function into the MAJ function is + provided as test. + \endverbatim + */ +template +signal apply_spectral_transformations( Ntk& dest, std::vector const& transformations, std::vector> const& leaves, SynthesisFn&& synthesis_fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_nary_xor_v, "Ntk does not implement the create_nary_xor method" ); + static_assert( std::is_invocable_r_v, SynthesisFn, Ntk&, std::vector> const&>, "SynthesisFn does not have expected signature" ); + + auto _leaves = leaves; + std::vector> _final_xors; + bool _output_neg = false; + + for ( auto const& t : transformations ) + { + switch ( t._kind ) + { + default: + assert( false ); + case kitty::detail::spectral_operation::kind::permutation: + { + const auto v1 = kitty::detail::log2[t._var1]; + const auto v2 = kitty::detail::log2[t._var2]; + std::swap( _leaves[v1], _leaves[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::input_negation: + { + const auto v1 = kitty::detail::log2[t._var1]; + _leaves[v1] = dest.create_not( _leaves[v1] ); + } + break; + case kitty::detail::spectral_operation::kind::output_negation: + _output_neg = !_output_neg; + break; + case kitty::detail::spectral_operation::kind::spectral_translation: + { + const auto v1 = kitty::detail::log2[t._var1]; + const auto v2 = kitty::detail::log2[t._var2]; + _leaves[v1] = dest.create_xor( _leaves[v1], _leaves[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::disjoint_translation: + { + const auto v1 = kitty::detail::log2[t._var1]; + _final_xors.push_back( _leaves[v1] ); + } + break; + } + } + + _final_xors.push_back( synthesis_fn( dest, _leaves ) ); + const auto output = dest.create_nary_xor( _final_xors ); + return _output_neg ? dest.create_not( output ) : output; +} + +/*! \brief Applies NPN transformations to a network + * + * A synthesis function `synthesis_fn` computes a network into `dest` and + * computes an output signal. Both the inputs and the outputs to that synthesis + * function are transformed according to `phase` and `perm`, based on NPN + * classification. The vector `leaves` are the original inputs to the + * output function, which is returned in terms of a signal into the network. + * + * The signature of `synthesis_fn` is `signal(Ntk&, std::vector> const&)`. + * + * \param dest Destination network for synthesis + * \param phase Input and output complementation + * \param perm Input permutation + * \param leaves Original inputs, which might be transformed + * \param synthesis_fn Synthesis function to create the inner function (without transformations) + */ +template +signal apply_npn_transformations( Ntk& dest, uint32_t phase, std::vector const& perm, std::vector> const& leaves, SynthesisFn&& synthesis_fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_nary_xor_v, "Ntk does not implement the create_nary_xor method" ); + static_assert( std::is_invocable_r_v, SynthesisFn, Ntk&, std::vector> const&>, "SynthesisFn does not have expected signature" ); + + std::vector> _leaves( perm.size() ); + std::transform( perm.begin(), perm.end(), _leaves.begin(), [&]( auto const& i ) { return ( phase >> i ) & 1 ? dest.create_not( leaves[i] ) : leaves[i]; } ); + + const auto f = synthesis_fn( dest, _leaves ); + return ( phase >> leaves.size() ) & 1 ? dest.create_not( f ) : f; +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/exact_mc_synthesis.hpp b/third-party/mockturtle/include/mockturtle/algorithms/exact_mc_synthesis.hpp new file mode 100644 index 00000000000..2c195c14747 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/exact_mc_synthesis.hpp @@ -0,0 +1,638 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file exact_mc_synthesis.hpp + \brief SAT-based XAG synthesis based on MC + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../algorithms/simulation.hpp" +#include "../generators/sorting.hpp" +#include "../io/write_verilog.hpp" +#include "../networks/xag.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cnf_view.hpp" +#include "cnf.hpp" + +namespace mockturtle +{ + +struct exact_mc_synthesis_params +{ + /* \brief Minimum number of AND gates. */ + uint32_t min_and_gates{ 0u }; + + /*! \brief Use CEGAR based solving strategy. */ + bool use_cegar{ false }; + + /*! \brief Use subset symmetry breaking. */ + bool break_subset_symmetries{ true }; + + /*! \brief Use multi-level subset symmetry breaking. */ + bool break_multi_level_subset_symmetries{ true }; + + /*! \brief Use symmetric variables. */ + bool break_symmetric_variables{ true }; + + /*! \brief User-specified variable symmetries. */ + std::vector> custom_symmetric_variables; + + /*! \brief Ensure to use all gates and essential variables. */ + bool ensure_to_use_gates{ true }; + + /*! \brief Heuristic XOR bound (based on sorter network). */ + std::optional heuristic_xor_bound{}; + + /*! \brief Updates XOR bound heuristic after each found solution. */ + bool auto_update_xor_bound{ false }; + + /*! \brief Conflict limit for the SAT solver. */ + uint32_t conflict_limit{ 0u }; + + /*! \brief Use conflict limit only when searching for multiple solutions + * + * The conflict limit will be ignored for the first call. + */ + bool ignore_conflict_limit_for_first_solution{ false }; + + /*! \brief Show progress (in CEGAR). */ + bool progress{ false }; + + /*! \brief Write DIMACS file, everytime solve is called. */ + std::optional write_dimacs{}; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose */ + bool very_verbose{ false }; +}; + +struct exact_mc_synthesis_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{}; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_solving{}; + + /*! \brief Total number of variables. */ + uint32_t num_vars{}; + + /*! \brief Total number of clauses. */ + uint32_t num_clauses{}; + + /*! \brief Prints report. */ + void report() const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] solving time = {:>5.2f} secs\n", to_seconds( time_solving ) ); + fmt::print( "[i] total vars = {}\n", num_vars ); + fmt::print( "[i] total clauses = {}\n", num_clauses ); + } +}; + +namespace detail +{ + +template +struct exact_mc_synthesis_impl +{ + using problem_network_t = cnf_view; + + exact_mc_synthesis_impl( kitty::dynamic_truth_table const& func, uint32_t num_solutions, exact_mc_synthesis_params const& ps, exact_mc_synthesis_stats& st ) + : num_vars_( func.num_vars() ), + func_( kitty::get_bit( func, 0 ) ? ~func : func ), + invert_( kitty::get_bit( func, 0 ) ), + heuristic_xor_bound_( ps.heuristic_xor_bound ), + num_solutions_( num_solutions ), + ps_( ps ), + st_( st ) + { + } + + std::vector run() + { + stopwatch<> t( st_.time_total ); + + std::vector ntks; + const auto degree = kitty::polynomial_degree( func_ ); + uint32_t num_ands = std::max( ps_.min_and_gates, degree == 0u ? degree : degree - 1u ); + + while ( true ) + { + if ( ps_.verbose ) + { + fmt::print( "try with {} AND gates\n", num_ands ); + } + + cnf_view_params cvps; + cvps.write_dimacs = ps_.write_dimacs; + problem_network_t pntk( cvps ); + reset( pntk ); + + for ( auto i = 0u; i < num_ands; ++i ) + { + add_gate( pntk ); + } + add_output( pntk ); + if ( ps_.heuristic_xor_bound || ps_.auto_update_xor_bound ) + { + add_xor_counter( pntk ); + } + + // TODO use LUT mapping before CNF generation + if ( const auto sol = ps_.use_cegar ? solve_with_cegar( pntk ) : solve_direct( pntk ); sol ) + { + ntks.push_back( *sol ); + if ( ps_.very_verbose ) + { + debug_solution( pntk ); + } + while ( ntks.size() < num_solutions_ ) + { + block( pntk ); + if ( const auto result = solve( pntk, false ); result && *result ) + { + ntks.push_back( extract_network( pntk ) ); + if ( ps_.very_verbose ) + { + debug_solution( pntk ); + fmt::print( "[i] found {} solutions so far\n", ntks.size() ); + } + } + else + { + break; + } + } + return ntks; + } + ++num_ands; + } + } + +private: + std::optional solve_direct( problem_network_t& pntk ) + { + prune_search_space( pntk ); + + for ( auto b = 1u; b < func_.num_bits(); ++b ) + { + constrain_assignment( pntk, b ); + } + + st_.num_vars += pntk.num_vars(); + st_.num_clauses += pntk.num_clauses(); + if ( const auto result = solve( pntk, true ); result && *result ) + { + return extract_network( pntk ); + } + else + { + return std::nullopt; + } + } + + std::optional solve_with_cegar( problem_network_t& pntk ) + { + prune_search_space( pntk ); + + uint32_t num_ands = static_cast( ltfi_vars_.size() ) / 2, bctr = 0u; + progress_bar pbar( static_cast( func_.num_bits() ), "exact_mc_synthesis |{}| ANDs = {} asserted bits = {} SAT solving time = {:.2f} secs", ps_.progress ); + while ( true ) + { + pbar( bctr, num_ands, bctr, to_seconds( st_.time_solving ) ); + if ( const auto result = solve( pntk, true ); result && *result ) + { + const auto sol = extract_network( pntk ); + default_simulator sim( num_vars_ ); + const auto simulated = simulate( sol, sim )[0u]; + if ( const auto bit = kitty::find_first_bit_difference( func_, simulated ); bit == -1 ) + { + st_.num_vars += pntk.num_vars(); + st_.num_clauses += pntk.num_clauses(); + return sol; + } + else + { + constrain_assignment( pntk, static_cast( bit ) ); + bctr++; + } + } + else + { + st_.num_vars += pntk.num_vars(); + st_.num_clauses += pntk.num_clauses(); + return std::nullopt; + } + } + } + + std::optional solve( problem_network_t& pntk, bool first ) + { + stopwatch<> t_sat( st_.time_solving ); + bill::result::clause_type assumptions; + pntk.foreach_po( [&]( auto const& f ) { + assumptions.push_back( pntk.lit( f ) ); + } ); + if ( heuristic_xor_bound_ ) + { + if ( int32_t pos = static_cast( xor_counter_.size() ) - *heuristic_xor_bound_ - 1; pos >= 0 ) + { + assumptions.push_back( pntk.lit( !xor_counter_[pos] ) ); + } + } + const auto res = pntk.solve( assumptions, ps_.ignore_conflict_limit_for_first_solution && first ? 0u : ps_.conflict_limit ); + + if ( ps_.auto_update_xor_bound && res && *res ) + { + heuristic_xor_bound_ = count_xors( pntk ) - 1u; + } + + return res; + } + +private: + Ntk extract_network( problem_network_t& pntk ) + { + Ntk xag; + std::vector> nodes( num_vars_ ); + std::generate( nodes.begin(), nodes.end(), [&]() { return xag.create_pi(); } ); + + const auto extract_ltfi = [&]( std::vector> const& ltfi_vars ) -> signal { + std::vector> ltfi; + for ( auto j = 0u; j < ltfi_vars.size(); ++j ) + { + if ( pntk.model_value( ltfi_vars[j] ) ) + { + ltfi.push_back( nodes[j] ); + } + } + return xag.create_nary_xor( ltfi ); + }; + + for ( auto i = 0u; i < ltfi_vars_.size() / 2; ++i ) + { + nodes.push_back( xag.create_and( extract_ltfi( ltfi_vars_[2 * i] ), extract_ltfi( ltfi_vars_[2 * i + 1] ) ) ); + } + + const auto c = extract_ltfi( ltfi_vars_.back() ); + xag.create_po( invert_ ? xag.create_not( c ) : c ); + + return xag; + } + + void block( problem_network_t& pntk ) + { + std::vector> blocked_lits; + for ( auto const& ltfi : ltfi_vars_ ) + { + for ( auto const& l : ltfi ) + { + blocked_lits.push_back( l ^ pntk.model_value( l ) ); + } + } + pntk.add_clause( blocked_lits ); + } + + void reset( problem_network_t const& pntk ) + { + // TODO: can we make this more iterative? + ltfi_vars_.clear(); + truth_vars_.clear(); + truth_vars_.resize( func_.num_bits() ); + + /* pre-assign truth_vars_ with primary inputs */ + for ( auto i = 0u; i < num_vars_; ++i ) + { + const auto var_tt = kitty::nth_var( num_vars_, i ); + for ( auto b = 0u; b < func_.num_bits(); ++b ) + { + truth_vars_[b].push_back( pntk.get_constant( kitty::get_bit( var_tt, b ) ) ); + } + } + } + + void add_gate( problem_network_t& pntk ) + { + uint32_t gate_index = static_cast( ltfi_vars_.size() ) / 2; + + // add select variables + for ( auto j = 0u; j < 2u; ++j ) + { + ltfi_vars_.push_back( std::vector>( num_vars_ + gate_index ) ); + std::generate( ltfi_vars_.back().begin(), ltfi_vars_.back().end(), [&]() { return pntk.create_pi(); } ); + } + } + + void add_output( problem_network_t& pntk ) + { + ltfi_vars_.push_back( std::vector>( num_vars_ + ltfi_vars_.size() / 2 ) ); + std::generate( ltfi_vars_.back().begin(), ltfi_vars_.back().end(), [&]() { return pntk.create_pi(); } ); + } + + void constrain_assignment( problem_network_t& pntk, uint32_t bit ) + { + const auto create_xor_clause = [&]( std::vector> const& ltfi_vars ) -> signal { + std::vector> ltfi( ltfi_vars.size() ); + for ( auto j = 0u; j < ltfi.size(); ++j ) + { + ltfi[j] = pntk.create_and( ltfi_vars[j], truth_vars_[bit][j] ); + } + return pntk.create_nary_xor( ltfi ); + }; + + for ( auto i = 0u; i < ltfi_vars_.size() / 2; ++i ) + { + truth_vars_[bit].push_back( pntk.create_and( create_xor_clause( ltfi_vars_[2 * i] ), create_xor_clause( ltfi_vars_[2 * i + 1] ) ) ); + } + + const auto po_signal = create_xor_clause( ltfi_vars_.back() ); + pntk.create_po( kitty::get_bit( func_, bit ) ? po_signal : pntk.create_not( po_signal ) ); + } + + void prune_search_space( problem_network_t& pntk ) + { + // At least one element in LTFI + for ( auto const& ltfi : ltfi_vars_ ) + { + pntk.add_clause( ltfi ); + } + + // linear TFIs are no subset of each other + if ( ps_.break_subset_symmetries ) + { + for ( auto i = 0u; i < ltfi_vars_.size() / 2u; ++i ) + { + auto const& ltfi1 = ltfi_vars_[2 * i]; + auto const& ltfi2 = ltfi_vars_[2 * i + 1]; + + std::vector> ands( ltfi1.size() ); + std::vector> ands2( ltfi1.size() ); + for ( auto j = 0u; j < ltfi1.size(); ++j ) + { + ands[j] = pntk.create_and( ltfi1[j], pntk.create_not( ltfi2[j] ) ); + ands2[j] = pntk.create_and( ltfi2[j], pntk.create_not( ltfi1[j] ) ); + } + pntk.add_clause( ands ); + pntk.add_clause( ands2 ); + } + } + + // left linear TFI is lexicographically smaller than right one + for ( auto i = 0u; i < ltfi_vars_.size() / 2u; ++i ) + { + auto const& ltfi2 = ltfi_vars_[2 * i]; + auto const& ltfi1 = ltfi_vars_[2 * i + 1]; + + auto n = ltfi1.size(); + std::vector> as( n - 1u ); + std::generate( as.begin(), as.end(), [&]() { return pntk.create_pi(); } ); + + pntk.add_clause( !ltfi1[0], ltfi2[0] ); + pntk.add_clause( !ltfi1[0], as[0] ); + pntk.add_clause( ltfi2[0], as[0] ); + + for ( auto k = 1u; k < n - 1; ++k ) + { + pntk.add_clause( !ltfi1[k], ltfi2[k], !as[k - 1] ); + pntk.add_clause( !ltfi1[k], as[k], !as[k - 1] ); + pntk.add_clause( ltfi2[k], as[k], !as[k - 1] ); + } + pntk.add_clause( !ltfi1.back(), !as.back() ); + pntk.add_clause( ltfi2.back(), !as.back() ); + } + + // break on multi-level subset relation + if ( ps_.break_multi_level_subset_symmetries ) + { + for ( auto ii = 0u; ii < ltfi_vars_.size(); ++ii ) + { + const auto& ltfi = ltfi_vars_[ii]; + for ( auto i = 0u; i < ii / 2u; ++i ) + { + const auto n = ltfi_vars_[2 * i].size(); + std::vector> ands_left, ands_right; + ands_left.push_back( ltfi[num_vars_ + i] ); + for ( auto k = 0u; k < n; ++k ) + { + ands_left.push_back( pntk.create_or( !ltfi[k], ltfi_vars_[2 * i][k] ) ); + ands_left.push_back( pntk.create_or( !ltfi[k], ltfi_vars_[2 * i + 1][k] ) ); + ands_right.push_back( pntk.create_xnor( ltfi[k], pntk.create_and( ltfi_vars_[2 * i][k], ltfi_vars_[2 * i + 1][k] ) ) ); + } + pntk.create_po( pntk.create_or( !pntk.create_nary_and( ands_left ), pntk.create_nary_and( ands_right ) ) ); + } + } + } + + // break on symmetric variables + if ( ps_.break_symmetric_variables ) + { + const auto break_symmetric_vars = [&]( auto j, auto jj ) { + if ( ps_.very_verbose ) + { + fmt::print( "[i] symmetry breaking based on symmetric variables {} and {}\n", j, jj ); + } + for ( auto ii = 0u; ii < ltfi_vars_.size(); ++ii ) + { + std::vector> clause; + clause.push_back( !ltfi_vars_[ii][jj] ); + for ( auto i = 0u; i <= ii; ++i ) + { + clause.push_back( ltfi_vars_[i][j] ); + } + pntk.add_clause( clause ); + } + }; + + for ( auto jj = 1u; jj < num_vars_; ++jj ) + { + for ( auto j = 0u; j < jj; ++j ) + { + if ( kitty::is_symmetric_in( func_, j, jj ) ) + { + break_symmetric_vars( j, jj ); + } + } + } + + for ( const auto& [j, jj] : ps_.custom_symmetric_variables ) + { + break_symmetric_vars( j, jj ); + } + } + + // ensure to use essential variables and gates + if ( ps_.ensure_to_use_gates ) + { + const auto num_ands = ltfi_vars_.size() / 2; + for ( auto j = 0u; j < num_vars_ + num_ands; ++j ) + { + if ( j < num_vars_ && !kitty::has_var( func_, j ) ) + { + continue; + } + + std::vector> clause; + for ( auto const& ltfi : ltfi_vars_ ) + { + if ( j < ltfi.size() ) + { + clause.push_back( ltfi[j] ); + } + } + pntk.add_clause( clause ); + } + } + } + + void add_xor_counter( problem_network_t& pntk ) + { + xor_counter_.clear(); + for ( auto const& ltfi : ltfi_vars_ ) + { + std::copy( ltfi.begin(), ltfi.end(), std::back_inserter( xor_counter_ ) ); + } + + insertion_sorting_network( static_cast( xor_counter_.size() ), [&]( auto a, auto b ) { + auto const aa = pntk.create_and( xor_counter_[a], xor_counter_[b] ); + auto const bb = pntk.create_or( xor_counter_[a], xor_counter_[b] ); + xor_counter_[a] = aa; + xor_counter_[b] = bb; + } ); + } + +private: + uint32_t count_xors( problem_network_t& pntk ) const + { + uint32_t ctr{}; + for ( auto const& ltfi : ltfi_vars_ ) + { + for ( auto const& l : ltfi ) + { + ctr += pntk.model_value( l ) ? 1u : 0u; + } + } + return ctr; + } + + void debug_solution( problem_network_t& pntk ) const + { + const auto num_ands = ltfi_vars_.size() / 2u; + const auto print_ltfi = [&]( std::vector> const& ltfi ) { + for ( auto const& f : ltfi ) + { + fmt::print( "{} ", (uint32_t)pntk.model_value( f ) ); + } + if ( auto padding = 2u * ( num_ands + num_vars_ - ltfi.size() ); padding > 0 ) + { + fmt::print( "{}", std::string( padding, ' ' ) ); + } + }; + + for ( auto i = 0u; i < ltfi_vars_.size() / 2u; ++i ) + { + fmt::print( "{:>2} = ", i + 1 ); + print_ltfi( ltfi_vars_[2 * i] ); + fmt::print( " " ); + print_ltfi( ltfi_vars_[2 * i + 1] ); + fmt::print( "\n" ); + } + fmt::print( " f = " ); + print_ltfi( ltfi_vars_.back() ); + fmt::print( "\n XORs = {}\n\n", count_xors( pntk ) ); + } + +private: + uint32_t num_vars_; + std::vector>> ltfi_vars_; + std::vector>> truth_vars_; + std::vector> xor_counter_; + kitty::dynamic_truth_table func_; + bool invert_{ false }; + std::optional heuristic_xor_bound_; + uint32_t num_solutions_; + exact_mc_synthesis_params const& ps_; + exact_mc_synthesis_stats& st_; +}; + +} // namespace detail + +template +Ntk exact_mc_synthesis( kitty::dynamic_truth_table const& func, exact_mc_synthesis_params const& ps = {}, exact_mc_synthesis_stats* pst = nullptr ) +{ + exact_mc_synthesis_stats st; + const auto xag = detail::exact_mc_synthesis_impl{ func, 1u, ps, st }.run().front(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return xag; +} + +template +std::vector exact_mc_synthesis_multiple( kitty::dynamic_truth_table const& func, uint32_t num_solutions, exact_mc_synthesis_params const& ps = {}, exact_mc_synthesis_stats* pst = nullptr ) +{ + exact_mc_synthesis_stats st; + const auto xags = detail::exact_mc_synthesis_impl{ func, num_solutions, ps, st }.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return xags; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/exorcism.hpp b/third-party/mockturtle/include/mockturtle/algorithms/exorcism.hpp new file mode 100644 index 00000000000..f91cbde1dfe --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/exorcism.hpp @@ -0,0 +1,82 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file exorcism.hpp + \brief Wrapper for ABC's exorcism + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace abc::exorcism +{ +extern int Abc_ExorcismMain( Vec_Wec_t* vEsop, int nIns, int nOuts, std::function const& onCube, int Quality, int Verbosity, int nCubesMax, int fUseQCost ); +} + +namespace mockturtle +{ + +inline std::vector exorcism( std::vector const& esop, uint32_t num_vars ) +{ + auto vesop = abc::exorcism::Vec_WecAlloc( esop.size() ); + + for ( auto const& cube : esop ) + { + auto vcube = abc::exorcism::Vec_WecPushLevel( vesop ); + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( !cube.get_mask( i ) ) + continue; + abc::exorcism::Vec_IntPush( vcube, cube.get_bit( i ) ? 2 * i : 2 * i + 1 ); + } + abc::exorcism::Vec_IntPush( vcube, -1 ); + } + + std::vector exorcism_esop; + abc::exorcism::Abc_ExorcismMain( + vesop, num_vars, 1, [&]( uint32_t bits, uint32_t mask ) { exorcism_esop.emplace_back( bits, mask ); }, 2, 0, 4 * esop.size(), 0 ); + + abc::exorcism::Vec_WecFree( vesop ); + + return exorcism_esop; +} + +inline std::vector exorcism( kitty::dynamic_truth_table const& func ) +{ + return exorcism( kitty::esop_from_optimum_pkrm( func ), func.num_vars() ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/experimental/boolean_optimization.hpp b/third-party/mockturtle/include/mockturtle/algorithms/experimental/boolean_optimization.hpp new file mode 100644 index 00000000000..3fde2ebe43f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/experimental/boolean_optimization.hpp @@ -0,0 +1,356 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file boolean_optimization.hpp + \brief A general logic optimization framework using Boolean methods + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../traits.hpp" +#include "../../utils/progress_bar.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../utils/null_utils.hpp" +#include "../../views/topo_view.hpp" + +#include + +namespace mockturtle::experimental +{ + +template +struct boolean_optimization_params +{ + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Whether to use new nodes as pivots. */ + bool optimize_new_nodes{ false }; + + /*! \brief Whether to run in dry-run mode (call `report` instead of `update_ntk`). */ + bool dry_run{ false }; + + /*! \brief Whether to print verbosely in dry-run mode. Ignored if `dry_run` is `false`. */ + bool dry_run_verbose{ true }; + + /*! \brief Parameter object for the windowing engine. */ + WinParams wps; + + /*! \brief Parameter object for the resynthesis engine. */ + ResynParams rps; +}; + +template +struct boolean_optimization_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime of structural analysis and simulation. */ + stopwatch<>::duration time_windowing{ 0 }; + + /*! \brief Accumulated runtime of resynthesis. */ + stopwatch<>::duration time_resynthesis{ 0 }; + + /*! \brief Accumulated runtime of updating network. */ + stopwatch<>::duration time_update{ 0 }; + + /*! \brief Total number of gain. */ + uint32_t estimated_gain{ 0 }; + + /*! \brief Initial network size (before resubstitution). */ + uint32_t initial_size{ 0 }; + + /*! \brief Number of constructed resynthesis problems. */ + uint32_t num_problems{ 0u }; + + /*! \brief Number of solutions found. */ + uint32_t num_solutions{ 0u }; + + /*! \brief Statistics object for the windowing engine. */ + WinStats wst; + + /*! \brief Statistics object for the resynthesis engine. */ + ResynStats rst; + + void report() const + { + // clang-format off + fmt::print( "[i] Boolean optimization top-level report\n" ); + fmt::print( "Estimated gain: {:8d} ({:.2f}%)\n", estimated_gain, ( 100.0 * estimated_gain ) / initial_size ); + fmt::print( "#problems = {}, #solutions = {} ({:.2f}%)\n", num_problems, num_solutions, float( num_solutions ) / float( num_problems ) ); + fmt::print( "======== Runtime Breakdown ========\n" ); + fmt::print( "Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Windowing : {:>5.2f} secs\n", to_seconds( time_windowing ) ); + fmt::print( " Resynthesis : {:>5.2f} secs\n", to_seconds( time_resynthesis ) ); + fmt::print( " Update ntk : {:>5.2f} secs\n", to_seconds( time_update ) ); + fmt::print( "========= Windowing Stats =========\n" ); + wst.report(); + fmt::print( "======== Resynthesis Stats ========\n" ); + rst.report(); + fmt::print( "===================================\n\n" ); + // clang-format on + } +}; + +namespace detail +{ + +/*! \brief Logic optimization using Boolean methods. + * + * \tparam Ntk Network type. + * \tparam Windowing Implementation of a windowing algorithm that creates + * a resynthesis problem to be solved. + * \tparam ResynSolver Implementation of a resynthesis algorithm that + * solves the resynthesis problem created by `Windowing`. + */ +template +class boolean_optimization_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using problem_t = typename Windowing::problem_t; + using res_t = typename ResynSolver::res_t; + using params_t = boolean_optimization_params; + using stats_t = boolean_optimization_stats; + + explicit boolean_optimization_impl( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), windowing( ntk, ps.wps, st.wst ), resyn( ntk, ps.rps, st.rst ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( std::is_same_v, "Types of resynthesis problem of Windowing and ResynSolver do not match" ); + } + + ~boolean_optimization_impl() + {} + + void run() + { + stopwatch t( st.time_total ); + progress_bar pbar{ ntk.size(), "B-opt |{0}| node = {1:>4} cand = {2:>4} est. gain = {3:>5}", ps.progress }; + + /* initialize */ + call_with_stopwatch( st.time_windowing, [&]() { + windowing.init(); + } ); + call_with_stopwatch( st.time_resynthesis, [&]() { + resyn.init(); + } ); + + st.initial_size = ntk.num_gates(); + topo_view{ ntk }.foreach_gate( [&]( auto const n, auto i ) { // TODO: maybe problematic + if ( !ps.optimize_new_nodes && i >= st.initial_size ) + { + return false; /* terminate */ + } + pbar( i, i, candidates, st.estimated_gain ); + + /* construct a resynthesis problem; usually by creating a window around the root node */ + auto prob = call_with_stopwatch( st.time_windowing, [&]() { + return windowing( n ); + } ); + if ( !prob ) + { + return true; /* next */ + } + ++st.num_problems; + + /* solve the resynthesis problem; usually by finding a (re)substitution */ + auto res = call_with_stopwatch( st.time_resynthesis, [&]() { + return resyn( *prob ); + } ); + if ( !res ) + { + return true; /* next */ + } + ++st.num_solutions; + + /* update progress bar */ + candidates++; + st.estimated_gain += windowing.gain( *prob, *res ); + + /* update network or report choice */ + bool cont = true; + if ( !ps.dry_run ) + { + cont = call_with_stopwatch( st.time_update, [&]() { + return windowing.update_ntk( *prob, *res ); + } ); + } + else if ( ps.dry_run_verbose ) + { + cont = windowing.report( *prob, *res ); + } + + return cont; + } ); + } + +private: + Ntk& ntk; + + params_t const& ps; + stats_t& st; + + Windowing windowing; + ResynSolver resyn; + + /* temporary statistics for progress bar */ + uint32_t candidates{ 0 }; +}; /* boolean_optimization_impl */ + +template +struct null_problem +{ + using node = typename Ntk::node; + node pivot; +}; + +/*! \brief A windowing implementation that creates windows of only the pivot node. + * + * This class is an example to demonstrate the interfaces required by + * the `Windowing` template argument of class `boolean_optimization_impl`. + * It is designed to be used together with `null_resynthesis`. + */ +template +class null_windowing +{ +public: + using problem_t = null_problem; + using res_t = typename Ntk::signal; + using params_t = null_params; + using stats_t = null_stats; + using node = typename Ntk::node; + + explicit null_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ) + { + (void)ps; + (void)st; + } + + void init() + {} + + std::optional operator()( node const& n ) + { + return problem_t{ n }; + } + + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + (void)prob; + (void)res; + return 0u; + } + + bool update_ntk( problem_t const& prob, res_t const& res ) + { + ntk.substitute_node( prob.pivot, res ); + return true; + } + + bool report( problem_t const& prob, res_t const& res ) + { + fmt::print( "[i] substitute node {} with signal {}{}\n", prob.pivot, ntk.is_complemented( res ) ? "!" : "", ntk.get_node( res ) ); + return true; + } + +private: + Ntk& ntk; +}; + +/*! \brief A resynthesis implementation that returns the pivot node itself. + * + * This class is an example to demonstrate the interfaces required by + * the `ResynSolver` template argument of class `boolean_optimization_impl`. + * It is designed to be used together with `null_windowing`. + */ +template +class null_resynthesis +{ +public: + using problem_t = null_problem; + using res_t = typename Ntk::signal; + using params_t = null_params; + using stats_t = null_stats; + + explicit null_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ) + { + (void)ps; + (void)st; + } + + void init() + {} + + std::optional operator()( problem_t& prob ) + { + return ntk.make_signal( prob.pivot ); + } + +private: + Ntk const& ntk; +}; + +} /* namespace detail */ + +template, typename stats_t = boolean_optimization_stats> +void null_optimization( Ntk& ntk, params_t const& ps = {}, stats_t* pst = nullptr ) +{ + stats_t st; + + using windowing_t = typename detail::null_windowing; + using resyn_t = typename detail::null_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + opt_t p( ntk, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle::experimental */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_generic_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_generic_resub.hpp new file mode 100644 index 00000000000..80902f1962d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_generic_resub.hpp @@ -0,0 +1,426 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cost_generic_resub.hpp + \brief generic widnowing algorithm with customized cost function + + \author Hanyu Wang +*/ + +#pragma once + +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/index_list/index_list.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/cost_view.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "../detail/resub_utils.hpp" +#include "../dont_cares.hpp" +#include "../reconv_cut.hpp" +#include "../simulation.hpp" +#include "boolean_optimization.hpp" +#include "cost_resyn.hpp" +#include + +#include +#include +#include + +namespace mockturtle::experimental +{ + +/*! \brief Parameters for cost. + */ +struct costfn_windowing_params +{ + /*! \brief Maximum number of PIs of reconvergence-driven cuts. */ + uint32_t max_pis{ 8 }; + + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 150 }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ 2 }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 12u }; + + /*! \brief Whether to normalize the truth tables. + * + * For some enumerative resynthesis engines, if the truth tables + * are normalized, some cases can be eliminated and thus improves + * efficiency. When this option is turned off, be sure to use an + * implementation of resynthesis that does not make this assumption; + * otherwise, quality degradation may be observed. + * + * Normalization is typically only useful for enumerative methods + * and for smaller solutions (i.e. when `max_inserts` < 2). Turning + * on normalization may result in larger runtime overhead when there + * are many divisors or when the truth tables are long. + */ + bool normalize{ false }; +}; + +struct costfn_windowing_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for cut computation. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor collection. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Accumulated runtime for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Accumulated runtime for don't care computation. */ + stopwatch<>::duration time_dont_care{ 0 }; + + /*! \brief Total number of leaves. */ + uint64_t num_leaves{ 0u }; + + /*! \brief Total number of divisors. */ + uint64_t num_divisors{ 0u }; + + /*! \brief Number of constructed windows. */ + uint32_t num_windows{ 0u }; + + /*! \brief Total number of MFFC nodes. */ + uint64_t sum_mffc_size{ 0u }; + + void report() const + { + // clang-format off + fmt::print( "[i] costfn_windowing report\n" ); + fmt::print( " tot. #leaves = {:5d}, tot. #divs = {:5d}, sum |MFFC| = {:5d}\n", num_leaves, num_divisors, sum_mffc_size ); + fmt::print( " avg. #leaves = {:>5.2f}, avg. #divs = {:>5.2f}, avg. |MFFC| = {:>5.2f}\n", float( num_leaves ) / float( num_windows ), float( num_divisors ) / float( num_windows ), float( sum_mffc_size ) / float( num_windows ) ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Cut : {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( " MFFC : {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( " Divs : {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( " Simulation: {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( " Dont cares: {:>5.2f} secs\n", to_seconds( time_dont_care ) ); + // clang-format on + } +}; + +namespace detail +{ +template +struct cost_aware_problem +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + signal root; + std::vector divs; + std::vector div_ids; /* positions of divisor truth tables in `tts` */ + std::vector div_id_to_node; /* maps IDs in `div_ids` to the corresponding node */ + std::vector tts; + TT care; + uint32_t mffc_size; + uint32_t max_cost{ std::numeric_limits::max() }; +}; + +template +class costfn_windowing +{ +public: + using problem_t = cost_aware_problem; + using params_t = costfn_windowing_params; + using stats_t = costfn_windowing_stats; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit costfn_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), cps( { ps.max_pis } ), mffc_mgr( ntk ), + divs_mgr( ntk, divisor_collector_params( { ps.max_divisors, ps.max_divisors, ps.skip_fanout_limit_for_divisors } ) ), + sim( ntk, win.tts, ps.max_pis ) + { + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + } + + void init() + { + } + + std::optional> operator()( node const& n ) + { + stopwatch t( st.time_total ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return std::nullopt; /* skip nodes with too many fanouts */ + } + + /* compute a cut and collect supported nodes */ + std::vector leaves = call_with_stopwatch( st.time_cuts, [&]() { + return reconvergence_driven_cut>( ntk, { n }, cps ).first; + } ); + std::vector supported; + call_with_stopwatch( st.time_divs, [&]() { + divs_mgr.collect_supported_nodes( n, leaves, supported ); + } ); + + /* simulate */ + call_with_stopwatch( st.time_sim, [&]() { + sim.simulate( leaves, supported ); + } ); + + /* mark MFFC nodes and collect divisors */ + ++mffc_marker; + win.mffc_size = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.call_on_mffc_and_count( n, leaves, [&]( node const& n ) { + ntk.set_value( n, mffc_marker ); + } ); + } ); + call_with_stopwatch( st.time_divs, [&]() { + collect_divisors( leaves, supported ); + } ); + + /* normalize */ + call_with_stopwatch( st.time_sim, [&]() { + if ( ps.normalize ) + { + win.root = normalize_truth_tables() ? !ntk.make_signal( n ) : ntk.make_signal( n ); + } + else + { + win.root = ntk.make_signal( n ); + } + } ); + + /* compute don't cares */ + call_with_stopwatch( st.time_dont_care, [&]() { + if ( ps.use_dont_cares ) + { + win.care = ~satisfiability_dont_cares( ntk, leaves, ps.window_size ); + } + else + { + win.care = ~kitty::create( ps.max_pis ); + } + } ); + + /* compute cost */ + win.max_cost = ntk.get_cost( n, win.divs ); + + st.num_windows++; + st.num_leaves += leaves.size(); + st.num_divisors += win.divs.size(); + st.sum_mffc_size += win.mffc_size; + + return win; + } + + template + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + return 1; /* cannot predict the final cost */ + } + + template + bool update_ntk( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + insert( ntk, std::begin( prob.divs ), std::end( prob.divs ), res, [&]( signal const& g ) { + ntk.substitute_node( ntk.get_node( prob.root ), ntk.is_complemented( prob.root ) ? !g : g ); + } ); + return true; /* continue optimization */ + } + + template + bool report( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + fmt::print( "[i] found solution {} for root signal {}{}\n", to_index_list_string( res ), ntk.is_complemented( prob.root ) ? "!" : "", ntk.get_node( prob.root ) ); + return true; + } + +private: + void collect_divisors( std::vector const& leaves, std::vector const& supported ) + { + win.divs.clear(); + win.div_ids.clear(); + + uint32_t i{ 1 }; + for ( auto const& l : leaves ) + { + win.div_ids.emplace_back( i++ ); + win.divs.emplace_back( ntk.make_signal( l ) ); + } + + i = ps.max_pis + 1; + for ( auto const& n : supported ) + { + if ( ntk.value( n ) != mffc_marker ) /* not in MFFC, not root */ + { + win.div_ids.emplace_back( i ); + win.divs.emplace_back( ntk.make_signal( n ) ); + } + ++i; + } + assert( i == win.tts.size() ); + } + + bool normalize_truth_tables() + { + assert( win.divs.size() == win.div_ids.size() ); + for ( auto i = 0u; i < win.divs.size(); ++i ) + { + if ( kitty::get_bit( win.tts.at( win.div_ids.at( i ) ), 0 ) ) + { + win.tts.at( win.div_ids.at( i ) ) = ~win.tts.at( win.div_ids.at( i ) ); + win.divs.at( i ) = !win.divs.at( i ); + } + } + + if ( kitty::get_bit( win.tts.back(), 0 ) ) + { + win.tts.back() = ~win.tts.back(); + return true; + } + else + { + return false; + } + } + +private: + Ntk& ntk; + problem_t win; + params_t const& ps; + stats_t& st; + reconvergence_driven_cut_parameters const cps; + typename mockturtle::detail::node_mffc_inside mffc_mgr; // TODO: namespaces can be removed when we move out of experimental:: + divisor_collector divs_mgr; + window_simulator sim; + uint32_t mffc_marker{ 0u }; + std::shared_ptr::modified_event_type> lazy_update_event; +}; /* costfn_windowing */ + +template +class costfn_resynthesis +{ +public: + using problem_t = cost_aware_problem; + using res_t = typename ResynEngine::index_list_t; + using params_t = typename ResynEngine::params; + using stats_t = typename ResynEngine::stats; + + explicit costfn_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), engine( ntk, ps, st ) + { + static_assert( has_cost_v, "Ntk does not implement the get_cost method" ); + } + + void init() + { + } + + std::optional operator()( problem_t& prob ) + { + return engine( prob.tts.back(), prob.care, prob.divs, std::begin( prob.div_ids ), std::end( prob.div_ids ), prob.tts, prob.max_cost ); + } + +private: + Ntk const& ntk; + typename ResynEngine::stats rst; + ResynEngine engine; +}; /* costfn_resynthesis */ + +} /* namespace detail */ + +using cost_generic_resub_params = boolean_optimization_params; +using cost_generic_resub_stats = boolean_optimization_stats; + +/*! \brief Cost-generic resubstitution algorithm. + * + * This algorithm creates a reconvergence-driven window for each node in the + * network, collects divisors, and builds the resynthesis problem. A search core + * then collects all the resubstitution candidates with the same functionality as + * the target. The candidate with the lowest cost will then replace the MFFC + * of the window. + * + * \param ntk Network + * \param cost_fn Customized cost function + * \param ps Optimization params + * \param pst Optimization statistics + */ +template +void cost_generic_resub( Ntk& ntk, CostFn cost_fn, cost_generic_resub_params const& ps, cost_generic_resub_stats* pst = nullptr ) +{ + fanout_view fntk( ntk ); + cost_view viewed( fntk, cost_fn ); + using Viewed = decltype( viewed ); + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::costfn_windowing; + using engine_t = cost_resyn; + using resyn_t = typename detail::costfn_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + cost_generic_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle::experimental \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_resyn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_resyn.hpp new file mode 100644 index 00000000000..33714989c27 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/experimental/cost_resyn.hpp @@ -0,0 +1,1289 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cost_resyn.hpp + \brief Solver of cost-aware resynthesis problem. + Given a resynthesis problem and the cost function, returns + the solution with (1) correct functionality (2) lower cost. + + This solver is cost-generic. + + \author Hanyu Wang +*/ + +#pragma once + +#include "../../algorithms/cleanup.hpp" +#include "../../utils/index_list/index_list.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle::experimental +{ + +struct cost_resyn_params +{ + /* maximum number of feasible solutions to collect */ + uint32_t max_solutions{ 1000u }; +}; + +struct cost_resyn_stats +{ + /* time for cost view the solution network */ + stopwatch<>::duration time_eval{ 0 }; + + /* time for searching the equivalent network */ + stopwatch<>::duration time_search{ 0 }; + + /* number of solutions */ + uint32_t num_solutions{ 0 }; + + /* number of problems */ + uint32_t num_problems{ 0 }; + + /* number of solution with 0,1,2,3 insertions */ + uint32_t num_resub[4]{ 0 }; + + /* size of the forest of feasible solutions */ + uint32_t size_forest{ 0 }; + + /* number of root (feasible solutions) */ + uint32_t num_roots{ 0 }; + + /* number of total gains */ + uint32_t num_gain{ 0 }; + + /* data */ + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] Evaluation : {:>5.2f} secs\n", to_seconds( time_eval ) ); + fmt::print( "[i] Searching : {:>5.2f} secs\n", to_seconds( time_search ) ); + fmt::print( "[i] # Problem : {}\n", num_problems ); + fmt::print( "[i] Avg. forest size : {:>5.2f}\n", (float)size_forest / num_problems ); + fmt::print( "[i] Avg. num solution: {:>5.2f}\n", (float)num_roots / num_problems ); + fmt::print( "[i] Opt. ratio : {:>5.2f}%\n", (float)num_solutions / num_problems * 100 ); + fmt::print( "[i] 0 - resub : {:>5.2f}\n", (float)num_resub[0] / num_problems ); + fmt::print( "[i] 1 - resub : {:>5.2f}\n", (float)num_resub[1] / num_problems ); + fmt::print( "[i] 2 - resub : {:>5.2f}\n", (float)num_resub[2] / num_problems ); + fmt::print( "[i] 3 - resub : {:>5.2f}\n", (float)num_resub[3] / num_problems ); + fmt::print( "[i] Gain : {:>5.2f}\n", (float)num_gain / num_problems ); + } +}; + +template +class cost_resyn +{ +public: + using params = cost_resyn_params; + using stats = cost_resyn_stats; + using signal = typename Ntk::signal; + using node = typename Ntk::node; + using context_t = typename Ntk::context_t; + using index_list_t = large_xag_index_list; + using truth_table_t = TT; + +private: + struct unate_lit + { + unate_lit( uint32_t l ) + : lit( l ) + { + } + + bool operator==( unate_lit const& other ) const + { + return lit == other.lit; + } + + uint32_t lit; + uint32_t score{ 0 }; + }; + struct fanin_pair + { + fanin_pair( uint32_t l1, uint32_t l2 ) + : lit1( l1 < l2 ? l1 : l2 ), lit2( l1 < l2 ? l2 : l1 ) + { + } + + fanin_pair( uint32_t l1, uint32_t l2, bool is_xor ) + : lit1( l1 > l2 ? l1 : l2 ), lit2( l1 > l2 ? l2 : l1 ) + { + (void)is_xor; + } + + bool operator==( fanin_pair const& other ) const + { + return lit1 == other.lit1 && lit2 == other.lit2; + } + + uint32_t lit1, lit2; + uint32_t score{ 0 }; + }; + + inline TT const& get_div( uint32_t idx ) const + { + return ( *ptts )[divisors[idx]]; + } + + uint32_t eval_result( Ntk& forest, index_list_t const& il ) + { + uint32_t eval = 0u; + // insert il to forest, this might not be applicable to cost related to fanout size! + insert( forest, std::begin( forest_leaves ), std::end( forest_leaves ), il, [&]( signal const& g ) { + forest.incr_trav_id(); + eval = forest.get_cost( forest.get_node( g ), forest_leaves ); + } ); + return eval; // the cost of the whole network + } + + bool update_result( Ntk& forest, index_list_t const& il ) + { + st.num_roots += 1u; + uint32_t curr_cost = eval_result( forest, il ); + if ( curr_cost < best_cost ) + { + best_cost = curr_cost; + index_list = il; + return true; + } + return false; + } + + bool push_solution( index_list_t const& il ) + { + ils.emplace_back( il ); /* push the solution to the solution set */ + return ils.size() < ps.max_solutions; /* continue if capacity allows */ + } + + template + void collect_unate_pairs_detail( uint32_t div1, uint32_t div2 ) + { + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + else if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + } + + /* Sort the unate literals by the number of minterms in the intersection. + - For `pos_unate_lits`, `on_off` = 1, sort by intersection with on-set; + - For `neg_unate_lits`, `on_off` = 0, sort by intersection with off-set + */ + void sort_unate_lits( std::vector& pos_unate_lits, uint32_t on_off ) + { + for ( auto& l : pos_unate_lits ) + { + l.score = kitty::count_ones( ( l.lit & 0x1 ? ~get_div( l.lit >> 1 ) : get_div( l.lit >> 1 ) ) & on_off_sets[on_off] ); + } + std::stable_sort( pos_unate_lits.begin(), pos_unate_lits.end(), [&]( unate_lit const& l1, unate_lit const& l2 ) { + return l1.score > l2.score; // descending order + } ); + } + + void sort_unate_pairs( std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto& p : unate_pairs ) + { + p.score = ( p.lit1 > p.lit2 ) ? kitty::count_ones( ( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) ^ ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) ) & on_off_sets[on_off] ) + : kitty::count_ones( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) & ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) & on_off_sets[on_off] ); + } + std::stable_sort( unate_pairs.begin(), unate_pairs.end(), [&]( fanin_pair const& p1, fanin_pair const& p2 ) { + return p1.score > p2.score; // descending order + } ); + } + + std::optional find_and_detail( std::vector const& pos_unate_lits, uint32_t on_off ) + { + for ( auto i = 0u; i < pos_unate_lits.size(); ++i ) + { + uint32_t const& lit1 = pos_unate_lits[i].lit; + if ( pos_unate_lits[i].score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < pos_unate_lits.size(); ++j ) + { + uint32_t const& lit2 = pos_unate_lits[j].lit; + if ( pos_unate_lits[i].score + pos_unate_lits[j].score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? get_div( lit2 >> 1 ) : ~get_div( lit2 >> 1 ); + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit = il.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + il.add_output( new_lit + on_off ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + template + std::optional find_and_and_helper( std::vector& pos_unate_lits, std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto i = 0u; i < pos_unate_lits.size(); ++i ) + { + uint32_t const& lit1 = pos_unate_lits[i].lit; + for ( auto j = 0u; j < unate_pairs.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs[j]; + if ( pos_unate_lits[i].score + pair2.score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + TT ntt2; + if constexpr ( is_xor ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + uint32_t new_lit1; + if constexpr ( is_xor ) + { + new_lit1 = il.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + new_lit1 = il.add_and( pair2.lit1, pair2.lit2 ); + } + auto const new_lit2 = il.add_and( ( lit1 ^ 0x1 ), new_lit1 ^ 0x1 ); + il.add_output( new_lit2 + on_off ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + template + std::optional find_and_and_and_helper( std::vector& unate_pairs_1, std::vector& unate_pairs_2, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_pairs_1.size(); ++i ) + { + fanin_pair const& pair1 = unate_pairs_1[i]; + if ( pair1.score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < unate_pairs_2.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs_2[j]; + if ( pair1.score + pair2.score < num_bits[on_off] ) + { + break; + } + TT ntt1, ntt2; + if constexpr ( left_xor ) + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) ^ ( pair1.lit2 & 0x1 ? ~get_div( pair1.lit2 >> 1 ) : get_div( pair1.lit2 >> 1 ) ); + } + else + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) | ( pair1.lit2 & 0x1 ? get_div( pair1.lit2 >> 1 ) : ~get_div( pair1.lit2 >> 1 ) ); + } + if constexpr ( right_xor ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + uint32_t fanin_lit1, fanin_lit2; + if constexpr ( left_xor ) + { + fanin_lit1 = il.add_xor( pair1.lit1, pair1.lit2 ); + } + else + { + fanin_lit1 = il.add_and( pair1.lit1, pair1.lit2 ); + } + if constexpr ( right_xor ) + { + fanin_lit2 = il.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + fanin_lit2 = il.add_and( pair2.lit1, pair2.lit2 ); + } + uint32_t const output_lit = il.add_and( fanin_lit1 ^ 0x1, fanin_lit2 ^ 0x1 ); + il.add_output( output_lit + on_off ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + void prepare_clear() + { + pos_unate_lits.clear(); + neg_unate_lits.clear(); + binate_divs.clear(); + pos_unate_pairs.clear(); + neg_unate_pairs.clear(); + pos_unate_xor_pairs.clear(); + neg_unate_xor_pairs.clear(); + mem_xor.clear(); + mem_xor_xor.clear(); + mem_xor_and.clear(); + has_xor_xor = false; + has_xor = false; + has_xor_and = false; + has_unateness = false; + has_and_pairs = false; + has_xor_pairs = false; + has_lit_xor = false; + has_init = false; + + index_list = std::nullopt; + forest_leaves.clear(); + candidates.clear(); + forest_root = std::nullopt; + div_costs.clear(); + isConst = false; + + ils.clear(); + } + + void prepare_task() + { + assert( has_init == false ); + num_bits[0] = kitty::count_ones( on_off_sets[0] ); /* off-set */ + num_bits[1] = kitty::count_ones( on_off_sets[1] ); /* on-set */ + has_init = true; + } + + void prepare_unateness() + { + assert( has_unateness == false && "already have unateness" ); + if ( has_init == false ) + { + prepare_task(); + } + for ( auto v = 1u; v < divisors.size(); ++v ) + { + bool unateness[4] = { false, false, false, false }; + /* check intersection with off-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( v << 1 ); + unateness[0] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( ( v << 1 ) | 0x1 ); + unateness[1] = true; + } + /* check intersection with on-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( v << 1 ); + unateness[2] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( ( v << 1 ) | 0x1 ); + unateness[3] = true; + } + /* useless unate literal */ + if ( ( unateness[0] && unateness[2] ) || ( unateness[1] && unateness[3] ) ) + { + pos_unate_lits.pop_back(); + neg_unate_lits.pop_back(); + } + /* binate divisor */ + else if ( !unateness[0] && !unateness[1] && !unateness[2] && !unateness[3] ) + { + binate_divs.emplace_back( v ); + } + } + sort_unate_lits( pos_unate_lits, 1 ); + sort_unate_lits( neg_unate_lits, 0 ); + has_unateness = true; + } + + void prepare_xor() + { + assert( has_xor == false ); + for ( auto i = 1u; i < divisors.size(); i++ ) + { + mem_xor[on_off_sets[1] ^ get_div( i )] = i; + } + has_xor = true; + } + + void prepare_xor_xor() + { + assert( has_xor_xor == false ); + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + mem_xor_xor[get_div( i ) ^ get_div( j ) ^ on_off_sets[1]] = i * divisors.size() + j; + } + } + has_xor_xor = true; + } + + void prepare_xor_and() + { + assert( has_xor_and == false ); + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + mem_xor_and[tt ^ on_off_sets[1]] = ( ( i << 1 ) + on_off_1 ) * 2 * divisors.size() + ( ( j << 1 ) + on_off_2 ); + } + } + } + } + has_xor_and = true; + } + + void prepare_and_pairs() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + collect_unate_pairs_detail<1, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<1, 0>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 0>( binate_divs[i], binate_divs[j] ); + } + }; + sort_unate_pairs( pos_unate_pairs, 1 ); + sort_unate_pairs( neg_unate_pairs, 0 ); + has_and_pairs = true; + } + + void prepare_xor_pairs() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + auto const tt_xor = get_div( binate_divs[i] ) ^ get_div( binate_divs[j] ); + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_xor_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_xor_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + } + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_xor_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_xor_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + } + } + } + sort_unate_pairs( pos_unate_xor_pairs, 1 ); + sort_unate_pairs( neg_unate_xor_pairs, 0 ); + has_xor_pairs = true; + } + + std::optional find_wire() + { + if ( has_init == false ) + { + prepare_task(); + } + if ( num_bits[0] == 0 ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + isConst = true; + } + if ( num_bits[1] == 0 ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( 0 ); + if ( !push_solution( il ) ) + return std::nullopt; + isConst = true; + } + for ( auto v = 1u; v < divisors.size(); ++v ) + { + if ( get_div( v ) == on_off_sets[1] ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( v << 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( get_div( v ) == on_off_sets[0] ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( ( v << 1 ) + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + return std::nullopt; + } + + std::optional find_and() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + return find_and_detail( neg_unate_lits, 0 ); + } + + std::optional find_or() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + return find_and_detail( pos_unate_lits, 1 ); + } + + std::optional find_xor() + { + if ( has_xor == false ) + { + prepare_xor(); + } + for ( auto i = 1u; i < divisors.size(); ++i ) + { + auto const tt = get_div( i ); + if ( mem_xor.find( tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( il.add_xor( ( i << 1 ), mem_xor[tt] << 1 ) ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor.find( ~tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( il.add_xor( ( i << 1 ) + 1, mem_xor[~tt] << 1 ) ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + return std::nullopt; + } + + std::optional find_or_and() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + return find_and_and_helper( pos_unate_lits, pos_unate_pairs, 1 ); + } + + std::optional find_and_or() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + return find_and_and_helper( neg_unate_lits, neg_unate_pairs, 0 ); + } + + std::optional find_and_and() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < pos_unate_lits.size(); ++i ) + { + if ( pos_unate_lits[i].score * 3 < num_bits[1] ) + { + break; + } + uint32_t const& lit1 = pos_unate_lits[i].lit; + for ( auto j = i + 1u; j < pos_unate_lits.size(); ++j ) + { + if ( pos_unate_lits[i].score + pos_unate_lits[j].score * 2 < num_bits[1] ) + { + break; + } + uint32_t const& lit2 = pos_unate_lits[j].lit; + auto const ntt1 = lit1 & 0x1 ? ~get_div( lit1 >> 1 ) : get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? ~get_div( lit2 >> 1 ) : get_div( lit2 >> 1 ); + TT const tt = ntt1 | ntt2; + for ( auto k = j + 1u; k < pos_unate_lits.size(); ++k ) + { + uint32_t const& lit3 = pos_unate_lits[k].lit; + if ( pos_unate_lits[i].score + pos_unate_lits[j].score + pos_unate_lits[k].score < num_bits[1] ) + { + break; + } + auto const ntt3 = lit3 & 0x1 ? ~get_div( lit3 >> 1 ) : get_div( lit3 >> 1 ); + if ( ( tt | ntt3 ) == on_off_sets[1] ) + { + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit3 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit2 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit2 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit1 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_or_or() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < neg_unate_lits.size(); ++i ) + { + if ( neg_unate_lits[i].score * 3 < num_bits[0] ) + { + break; + } + uint32_t const& lit1 = neg_unate_lits[i].lit; + for ( auto j = i + 1u; j < neg_unate_lits.size(); ++j ) + { + if ( neg_unate_lits[i].score + neg_unate_lits[j].score * 2 < num_bits[0] ) + { + break; + } + uint32_t const& lit2 = neg_unate_lits[j].lit; + auto const ntt1 = lit1 & 0x1 ? ~get_div( lit1 >> 1 ) : get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? ~get_div( lit2 >> 1 ) : get_div( lit2 >> 1 ); + TT const tt = ntt1 | ntt2; + for ( auto k = j + 1u; k < neg_unate_lits.size(); ++k ) + { + uint32_t const& lit3 = neg_unate_lits[k].lit; + if ( neg_unate_lits[i].score + neg_unate_lits[j].score + neg_unate_lits[k].score < num_bits[0] ) + { + break; + } + + auto const ntt3 = lit3 & 0x1 ? ~get_div( lit3 >> 1 ) : get_div( lit3 >> 1 ); + if ( ( tt | ntt3 ) == on_off_sets[0] ) + { + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit3 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit2 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit2 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit1 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_and_xor() + { + if ( has_xor_pairs == false ) + { + prepare_xor_pairs(); + } + auto ret = find_and_and_helper( pos_unate_lits, pos_unate_xor_pairs, 1 ); + if ( !ret ) + ret = find_and_and_helper( neg_unate_lits, neg_unate_xor_pairs, 0 ); + return ret; + } + + std::optional find_xor_xor() + { + if ( has_xor == false ) + { + prepare_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + auto const tt = get_div( i ) ^ get_div( j ); + if ( mem_xor.find( tt ) != mem_xor.end() ) + { + if ( mem_xor[tt] == i ) + continue; + if ( mem_xor[tt] == j ) + continue; + index_list_t il; + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1, mem_xor[tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( mem_xor[tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1, j << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( j << 1 ), ( mem_xor[tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1, i << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + if ( mem_xor.find( ~tt ) != mem_xor.end() ) + { + if ( mem_xor[~tt] == i ) + continue; + if ( mem_xor[~tt] == j ) + continue; + index_list_t il; + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, mem_xor[~tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( mem_xor[~tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, j << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( j << 1 ), ( mem_xor[~tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, i << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + return std::nullopt; + } + + std::optional find_xor_xor_xor() + { + if ( has_xor_xor == false ) + { + prepare_xor_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + auto const tt = get_div( i ) ^ get_div( j ); + if ( mem_xor_xor.find( tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[tt] % divisors.size() ) << 1, ( mem_xor_xor[tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor_xor.find( ~tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[~tt] % divisors.size() ) << 1, ( mem_xor_xor[~tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2 ^ 0x1, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + std::optional find_xor_xor_and() + { + if ( has_xor_xor == false ) + { + prepare_xor_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + if ( mem_xor_xor.find( tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[tt] % divisors.size() ) << 1, ( mem_xor_xor[tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor_xor.find( ~tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[~tt] % divisors.size() ) << 1, ( mem_xor_xor[~tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2 ^ 0x1, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_xor_and() + { + if ( has_xor == false ) + { + prepare_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + if ( mem_xor.find( tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( new_lit1, mem_xor[tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor.find( ~tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, mem_xor[~tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_xor_and_and() + { + if ( has_xor_and == false ) + { + prepare_xor_and(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + if ( mem_xor_and.find( tt ) != mem_xor_and.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_and( mem_xor_and[tt] % ( 2 * divisors.size() ), mem_xor_and[tt] / ( 2 * divisors.size() ) ); + auto new_lit3 = il.add_xor( new_lit1, new_lit2 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor_and.find( ~tt ) != mem_xor_and.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_and( mem_xor_and[~tt] % ( 2 * divisors.size() ), mem_xor_and[~tt] / ( 2 * divisors.size() ) ); + auto new_lit3 = il.add_xor( new_lit1 ^ 0x1, new_lit2 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_and_and_and() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + auto ret = find_and_and_and_helper( pos_unate_pairs, pos_unate_pairs, 1 ); + if ( !ret ) + ret = find_and_and_and_helper( neg_unate_pairs, neg_unate_pairs, 0 ); + return ret; + } + + std::optional find_and_and_xor() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + if ( has_xor_pairs == false ) + { + prepare_xor_pairs(); + } + auto ret = find_and_and_and_helper( pos_unate_xor_pairs, pos_unate_pairs, 1 ); + if ( !ret ) + ret = find_and_and_and_helper( neg_unate_xor_pairs, neg_unate_pairs, 0 ); + return ret; + } + + std::optional find_and_xor_xor() + { + if ( has_xor_pairs == false ) + { + prepare_xor_pairs(); + } + auto ret = find_and_and_and_helper( pos_unate_xor_pairs, pos_unate_xor_pairs, 1 ); + if ( !ret ) + ret = find_and_and_and_helper( neg_unate_xor_pairs, neg_unate_xor_pairs, 0 ); + return ret; + } + + struct core_func_t + { + std::function func; + uint32_t effort; + uint32_t score; + core_func_t( std::function func, uint32_t effort ) : func( func ), effort( effort ) + { + } + void operator()( cost_resyn* pcore ) + { + func( pcore ); + } + }; + + void sorted_core( Ntk& forest ) + { + for ( core_func_t& fn : fns ) + { + if ( ils.size() >= ps.max_solutions ) + break; + uint32_t nbefore = ils.size(); + call_with_stopwatch( st.time_search, [&]() { fn( this ); } ); + st.num_resub[fn.effort] += ils.size() - nbefore; + if ( isConst ) /* try to find more solution of constant will crash */ + break; + } + st.num_roots += ils.size(); + uint32_t ngain = 0u; + + for ( index_list_t const& il : ils ) + { + if ( best_cost > eval_result( forest, il ) ) + ngain = std::max( best_cost - eval_result( forest, il ), ngain ); + call_with_stopwatch( st.time_eval, [&]() { update_result( forest, il ); } ); + } + st.num_gain += ngain; + } + +public: + explicit cost_resyn( Ntk const& ntk, params const& ps, stats& st ) noexcept + : ntk( ntk ), ps( ps ), st( st ) + { + fns.clear(); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_wire(); }, 0 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor(); }, 1 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and(); }, 1 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_or(); }, 1 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_xor(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_and(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_and(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_or(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_or_or(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_or_and(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_xor(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_and_and(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_xor_and(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_xor_xor(); }, 3 ); // bad efficiency / gain trade-off + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_xor_xor(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_and_xor(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_and_and(); }, 3 ); + divisors.reserve( 200u ); + } + + template + std::optional operator()( TT const& target, TT const& care, std::vector const& divs, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_cost = std::numeric_limits::max() ) + { + ptts = &tts; + on_off_sets[0] = ~target & care; + on_off_sets[1] = target & care; + + divisors.resize( 1 ); /* clear previous data and reserve 1 dummy node for constant */ + while ( begin != end ) + { + divisors.emplace_back( *begin ); + ++begin; + } + + best_cost = max_cost; + prepare_clear(); + + // prepare solution forest + Ntk forest; // create an empty network + + for ( signal div : divs ) + { + signal const& s = forest.create_pi(); + node n = forest.get_node( s ); + forest_leaves.emplace_back( s ); + context_t div_cost = ntk.get_context( ntk.get_node( div ) ); + forest.set_context( n, div_cost ); + div_costs.emplace_back( div_cost ); + } + + sorted_core( forest ); + + st.num_problems += 1u; + if ( index_list ) + { + st.num_solutions += 1u; + st.size_forest += forest.num_gates(); + } + + return index_list; + } + +private: + std::array on_off_sets; + std::array num_bits; /* number of bits in on-set and off-set */ + + const std::vector* ptts; + std::vector divisors; + std::vector div_costs; + std::array tts_xors; + std::unordered_map> mem_xor; + std::unordered_map> mem_xor_xor; + std::unordered_map> mem_xor_and; + bool has_xor; + bool has_xor_xor; + bool has_xor_and; + bool has_unateness; + bool has_and_pairs; + bool has_xor_pairs; + bool has_init; + bool has_lit_xor; + /* positive unate: not overlapping with off-set + negative unate: not overlapping with on-set */ + std::vector pos_unate_lits, neg_unate_lits; + std::vector binate_divs; + std::vector pos_unate_pairs, neg_unate_pairs; + std::vector pos_unate_xor_pairs, neg_unate_xor_pairs; + + Ntk const& ntk; + std::vector forest_leaves; + std::vector candidates; // the output signals with correct functionality + std::optional forest_root; + params const& ps; + stats& st; + + std::optional index_list; + std::vector ils; + uint32_t best_cost; + + std::vector fns; + bool isConst; +}; + +} // namespace mockturtle::experimental diff --git a/third-party/mockturtle/include/mockturtle/algorithms/experimental/decompose_multioutput.hpp b/third-party/mockturtle/include/mockturtle/algorithms/experimental/decompose_multioutput.hpp new file mode 100644 index 00000000000..f6385daafeb --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/experimental/decompose_multioutput.hpp @@ -0,0 +1,397 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file decompose_multioutput.hpp + \brief Decomposes the multi-output gates into single output + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/topo_view.hpp" +#include "../cleanup.hpp" + +namespace mockturtle +{ + +struct decompose_multioutput_params +{ + bool set_multioutput_as_dont_touch{ false }; +}; + +namespace detail +{ + +template +void decompose_multioutput_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, std::unordered_map>& old_to_new, decompose_multioutput_params const& ps ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[ntk.make_signal( node )] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[ntk.make_signal( node )] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto child, auto ) { + const auto child_no_complement = child ^ ntk.is_complemented( child ); + const auto f = old_to_new[child_no_complement]; + + assert( dest.get_node( f ) != dest.get_node( dest.get_constant( false ) ) ); + assert( dest.get_node( f ) != dest.get_node( dest.get_constant( true ) ) ); + + children.push_back( f ^ ntk.is_complemented( child ) ); + } ); + + /* clone node */ + if ( ntk.is_multioutput( node ) ) + { + for ( auto i = 0; i < ntk.num_outputs( node ); ++i ) + { + auto f = ntk.make_signal( node, i ); + do + { + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v, "NtkDest cannot create AND gates" ); + if ( ntk.is_and( f ) ) + { + old_to_new[f] = dest.create_and( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_or_v ) + { + static_assert( has_create_or_v, "NtkDest cannot create OR gates" ); + if ( ntk.is_or( f ) ) + { + old_to_new[f] = dest.create_or( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v, "NtkDest cannot create XOR gates" ); + if ( ntk.is_xor( f ) ) + { + old_to_new[f] = dest.create_xor( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v, "NtkDest cannot create MAJ gates" ); + if ( ntk.is_maj( f ) ) + { + old_to_new[f] = dest.create_maj( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_ite_v ) + { + static_assert( has_create_ite_v, "NtkDest cannot create ITE gates" ); + if ( ntk.is_ite( f ) ) + { + old_to_new[f] = dest.create_ite( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v, "NtkDest cannot create XOR3 gates" ); + if ( ntk.is_xor3( f ) ) + { + old_to_new[f] = dest.create_xor3( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_function_v && has_create_node_v ) + { + old_to_new[f] = dest.create_node( children, ntk.node_function_pin( node, i ) ); + break; + } + std::cerr << "[e] something went wrong, could not copy node " << ntk.node_to_index( node ) << "\n"; + } while ( false ); + + /* set dont touch */ + if constexpr ( has_select_dont_touch_v ) + { + if ( ps.set_multioutput_as_dont_touch ) + dest.select_dont_touch( dest.get_node( old_to_new[f] ) ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( f ) ) + { + dest.set_name( old_to_new[f], ntk.get_name( f ) ); + } + if ( ntk.has_name( !f ) ) + { + dest.set_name( !old_to_new[f], ntk.get_name( !f ) ); + } + } + } + } + else + { + auto f = ntk.make_signal( node ); + if constexpr ( std::is_same_v ) + { + old_to_new[f] = dest.clone_node( ntk, node, children ); + } + else + { + do + { + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v, "NtkDest cannot create AND gates" ); + if ( ntk.is_and( node ) ) + { + old_to_new[f] = dest.create_and( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_or_v ) + { + static_assert( has_create_or_v, "NtkDest cannot create OR gates" ); + if ( ntk.is_or( node ) ) + { + old_to_new[f] = dest.create_or( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v, "NtkDest cannot create XOR gates" ); + if ( ntk.is_xor( node ) ) + { + old_to_new[f] = dest.create_xor( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v, "NtkDest cannot create MAJ gates" ); + if ( ntk.is_maj( node ) ) + { + old_to_new[f] = dest.create_maj( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_ite_v ) + { + static_assert( has_create_ite_v, "NtkDest cannot create ITE gates" ); + if ( ntk.is_ite( node ) ) + { + old_to_new[f] = dest.create_ite( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v, "NtkDest cannot create XOR3 gates" ); + if ( ntk.is_xor3( node ) ) + { + old_to_new[f] = dest.create_xor3( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_nary_and_v ) + { + static_assert( has_create_nary_and_v, "NtkDest cannot create n-ary AND gates" ); + if ( ntk.is_nary_and( node ) ) + { + old_to_new[f] = dest.create_nary_and( children ); + break; + } + } + if constexpr ( has_is_nary_or_v ) + { + static_assert( has_create_nary_or_v, "NtkDest cannot create n-ary OR gates" ); + if ( ntk.is_nary_or( node ) ) + { + old_to_new[f] = dest.create_nary_or( children ); + break; + } + } + if constexpr ( has_is_nary_xor_v ) + { + static_assert( has_create_nary_xor_v, "NtkDest cannot create n-ary XOR gates" ); + if ( ntk.is_nary_xor( node ) ) + { + old_to_new[f] = dest.create_nary_xor( children ); + break; + } + } + if constexpr ( has_is_function_v && has_create_node_v ) + { + old_to_new[f] = dest.create_node( children, ntk.node_function( node ) ); + break; + } + std::cerr << "[e] something went wrong, could not copy node " << ntk.node_to_index( node ) << "\n"; + } while ( false ); + } + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( f ) ) + { + dest.set_name( old_to_new[f], ntk.get_name( f ) ); + } + if ( ntk.has_name( !f ) ) + { + dest.set_name( !old_to_new[f], ntk.get_name( !f ) ); + } + } + } + } ); + + /* POs */ + ntk.foreach_po( [&]( auto const& po ) { + const auto po_no_complement = po ^ ntk.is_complemented( po ); + auto const f = old_to_new[po_no_complement]; + dest.create_po( f ^ ntk.is_complemented( po ) ); + } ); + + /* RIs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + dest.create_ri( old_to_new[f ^ ntk.is_complemented( f )] ^ ntk.is_complemented( f ) ); + } ); + } + + /* CO names */ + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + ntk.foreach_co( [&]( auto co, auto index ) { + (void)co; + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } ); + } +} + +} // namespace detail + +/*! \brief Decomposes the multi-output gates into single output. + * + * This method reconstructs a network decomposing the multi-output gates into + * single output gates. Moreover, it omits all dangling nodes. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + * - `has_multioutput` + */ +template +[[nodiscard]] NtkDest decompose_multioutput( NtkSrc const& ntk, decompose_multioutput_params const& ps = {} ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_node_to_index_v, "NtkSrc does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_is_complemented_v, "NtkSrc does not implement the is_complemented method" ); + static_assert( has_is_multioutput_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_node_function_pin_v, "NtkSource does not implement the node_function_pin" ); + static_assert( has_num_outputs_v, "NtkSource does not implement the has_num_outputs" ); + + NtkDest dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis, false ); + + std::unordered_map> old_to_new; + detail::decompose_multioutput_impl( ntk, dest, cis.begin(), cis.end(), old_to_new, ps ); + + return dest; +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/experimental/sim_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/experimental/sim_resub.hpp new file mode 100644 index 00000000000..923c6ee903f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/experimental/sim_resub.hpp @@ -0,0 +1,545 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sim_resub.hpp + \brief Simulation-guided resubstitution + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../io/write_patterns.hpp" +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/index_list/index_list.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../circuit_validator.hpp" +#include "../detail/resub_utils.hpp" +#include "../dont_cares.hpp" +#include "../pattern_generation.hpp" +#include "../resyn_engines/aig_enumerative.hpp" +#include "../resyn_engines/mig_enumerative.hpp" +#include "../resyn_engines/mig_resyn.hpp" +#include "../resyn_engines/xag_resyn.hpp" +#include "../simulation.hpp" +#include + +#include +#include +#include + +namespace mockturtle::experimental +{ + +struct breadth_first_windowing_params +{ + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 50 }; + + /*! \brief Maximum number of TFI nodes to collect. */ + uint32_t max_tfi{ uint32_t( max_divisors * 0.5 ) }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ std::numeric_limits::max() }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; +}; + +struct simulation_guided_resynthesis_params +{ + /*! \brief Whether to use pre-generated patterns stored in a file. + * If not, by default, 1024 random pattern + 1x stuck-at patterns will be generated. + */ + std::optional pattern_filename{}; + + /*! \brief Whether to save the appended patterns (with CEXs) into file. */ + std::optional save_patterns{}; + + /*! \brief Maximum number of clauses of the SAT solver. */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Conflict limit for the SAT solver. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Random seed for the SAT solver (influences the randomness of counter-examples). */ + uint32_t random_seed{ 1 }; + + /*! \brief Maximum number of trials to call the resub functor. */ + uint32_t max_trials{ 100 }; + + /*! \brief Whether to utilize ODC, and how many levels. 0 = no. -1 = Consider TFO until PO. */ + int32_t odc_levels{ 0 }; +}; + +struct breadth_first_windowing_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor collection. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Total number of divisors. */ + uint64_t num_divisors{ 0u }; + + /*! \brief Number of constructed windows. */ + uint32_t num_windows{ 0u }; + + /*! \brief Total number of MFFC nodes. */ + uint64_t sum_mffc_size{ 0u }; + + void report() const + { + // clang-format off + fmt::print( "[i] breadth_first_windowing report\n" ); + fmt::print( " tot. #divs = {:5d}, sum |MFFC| = {:5d}\n", num_divisors, sum_mffc_size ); + fmt::print( " avg. #divs = {:>5.2f}, avg. |MFFC| = {:>5.2f}\n", float( num_divisors ) / float( num_windows ), float( sum_mffc_size ) / float( num_windows ) ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " MFFC: {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( " Divs: {:>5.2f} secs\n", to_seconds( time_divs ) ); + // clang-format on + } +}; + +struct simulation_guided_resynthesis_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for pattern generation. */ + stopwatch<>::duration time_patgen{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_resyn{ 0 }; + + /*! \brief Time for computing ODCs. */ + stopwatch<>::duration time_odc{ 0 }; + + /*! \brief Time for saving patterns. */ + stopwatch<>::duration time_patsave{ 0 }; + + /*! \brief Number of calls to the resynthesis engine. */ + uint32_t num_calls{ 0 }; + + /*! \brief Number of solutions found by the resynthesis engine. */ + uint32_t num_sols{ 0 }; + + /*! \brief Number of patterns used. */ + uint32_t num_pats{ 0 }; + + /*! \brief Number of valid solutions. */ + uint32_t num_valid{ 0 }; + + /*! \brief Number of counter-examples. */ + uint32_t num_cex{ 0 }; + + /*! \brief Number of SAT solver timeout. */ + uint32_t num_timeout{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] simulation_guided_resynthesis report\n" ); + fmt::print( " initial #pats = {} + #CEXs = {} --> {}\n", num_pats, num_cex, num_pats + num_cex ); + fmt::print( " #resyn calls = {}, #solutions = {} ({:.2f}%)\n", num_calls, num_sols, float( num_sols ) / float( num_calls ) * 100 ); + fmt::print( " #valid = {} ({:.2f}%), #CEXs = {} ({:.2f}%), #TOs = {} ({:.2f}%)\n", num_valid, float( num_valid ) / float( num_sols ) * 100, num_cex, float( num_cex ) / float( num_sols ) * 100, num_timeout, float( num_timeout ) / float( num_sols ) * 100 ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Pattern gen.: {:>5.2f} secs [Called in init() -- should be subtracted]\n", to_seconds( time_patgen ) ); + fmt::print( " Simulation : {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( " SAT : {:>5.2f} secs\n", to_seconds( time_sat ) ); + fmt::print( " Resynthesis : {:>5.2f} secs\n", to_seconds( time_resyn ) ); + fmt::print( " ODC comp. : {:>5.2f} secs\n", to_seconds( time_odc ) ); + fmt::print( " Save patterns : {:>5.2f} secs [Called in destructor -- not included in total runtime]\n", to_seconds( time_patsave ) ); + // clang-format on + } +}; + +namespace detail +{ + +template +struct breadth_first_window +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + node root; + std::vector divs; + uint32_t mffc_size; + uint32_t max_size{ std::numeric_limits::max() }; + // uint32_t max_level{std::numeric_limits::max()}; +}; + +template +class breadth_first_windowing +{ +public: + using problem_t = breadth_first_window; + using params_t = breadth_first_windowing_params; + using stats_t = breadth_first_windowing_stats; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit breadth_first_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), mffc_mgr( ntk ), + divs_mgr( ntk, divisor_collector_params( { ps.max_tfi, ps.max_divisors, ps.skip_fanout_limit_for_divisors } ) ) + { + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + } + + void init() + { + } + + std::optional> operator()( node const& n ) + { + stopwatch t( st.time_total ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return std::nullopt; /* skip nodes with too many fanouts */ + } + + /* collect TFI nodes with BFS and supported "wing" nodes */ + win.root = n; + win.divs.clear(); + call_with_stopwatch( st.time_divs, [&]() { + divs_mgr.collect_tfi_and_wings( n, win.divs ); + } ); + + /* compute and mark MFFC nodes */ + ++mffc_marker; + win.mffc_size = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.call_on_mffc_and_count( n, {}, [&]( node const& n ) { + ntk.set_value( n, mffc_marker ); + } ); + } ); + + /* exclude MFFC node in divs */ + uint32_t counter{ 0 }; + for ( int32_t i = 0; i < win.divs.size(); ++i ) + { + if ( ntk.value( win.divs.at( i ) ) == mffc_marker ) + { + ++counter; + win.divs[i] = win.divs.back(); + win.divs.pop_back(); + --i; + } + } + + /* all MFFC nodes should be in TFI (thus collected in divs) */ + assert( counter == win.mffc_size ); + win.max_size = std::min( win.mffc_size - 1, ps.max_inserts ); + + st.num_windows++; + st.num_divisors += win.divs.size(); + st.sum_mffc_size += win.mffc_size; + + return win; + } + + template + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + return prob.mffc_size - res.num_gates(); + } + + template + bool update_ntk( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + insert( ntk, prob.divs.begin(), prob.divs.end(), res, [&]( signal const& g ) { + ntk.substitute_node( prob.root, g ); + } ); + return true; /* continue optimization */ + } + + template + bool report( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + fmt::print( "[i] found solution {} for root node {}\n", to_index_list_string( res ), prob.root ); + return true; + } + +private: +private: + Ntk& ntk; + problem_t win; + params_t const& ps; + stats_t& st; + typename mockturtle::detail::node_mffc_inside mffc_mgr; // TODO: namespaces can be removed when we move out of experimental:: + uint32_t mffc_marker{ 0u }; + divisor_collector divs_mgr; +}; /* breadth_first_windowing */ + +template +class simulation_guided_resynthesis +{ +public: + using problem_t = breadth_first_window; + using res_t = typename ResynEngine::index_list_t; + using params_t = simulation_guided_resynthesis_params; + using stats_t = simulation_guided_resynthesis_stats; + + using node = typename Ntk::node; + using TT = kitty::partial_truth_table; + using validator_t = circuit_validator; + + explicit simulation_guided_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), engine( rst ), + validator( ntk, { ps.max_clauses, ps.odc_levels, ps.conflict_limit, ps.random_seed } ), tts( ntk ) + {} + + ~simulation_guided_resynthesis() + { + if ( ps.save_patterns ) + { + call_with_stopwatch( st.time_patsave, [&]() { + write_patterns( sim, *ps.save_patterns ); + } ); + } + + if ( add_event ) + { + ntk.events().release_add_event( add_event ); + } + } + + void init() + { + add_event = ntk.events().register_add_event( [&]( const auto& n ) { + tts.resize(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } ); + + /* prepare simulation patterns */ + call_with_stopwatch( st.time_patgen, [&]() { + if ( ps.pattern_filename ) + { + sim = partial_simulator( *ps.pattern_filename ); + } + else + { + sim = partial_simulator( ntk.num_pis(), 1024 ); + pattern_generation( ntk, sim ); + } + } ); + st.num_pats = sim.num_bits(); + + /* first simulation: the whole circuit; from 0 bits. */ + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + + std::optional operator()( problem_t& prob ) + { + for ( auto j = 0u; j < ps.max_trials; ++j ) + { + check_tts( prob.root ); + for ( auto const& d : prob.divs ) + { + check_tts( d ); + } + + TT const care = call_with_stopwatch( st.time_odc, [&]() { + return ( ps.odc_levels == 0 ) ? sim.compute_constant( true ) : ~observability_dont_cares( ntk, prob.root, sim, tts, ps.odc_levels ); + } ); + + const auto res = call_with_stopwatch( st.time_resyn, [&]() { + ++st.num_calls; + return engine( tts[prob.root], care, std::begin( prob.divs ), std::end( prob.divs ), tts, prob.max_size ); + } ); + + if ( res ) + { + ++st.num_sols; + auto const& id_list = *res; + auto valid = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( prob.root, prob.divs, id_list ); + } ); + if ( valid ) + { + if ( *valid ) + { + ++st.num_valid; + if constexpr ( UseODC ) + { + /* restart the solver -- clear constructed CNF */ + call_with_stopwatch( st.time_sat, [&]() { + validator.update(); + } ); + } + return id_list; + } + else + { + ++st.num_cex; + call_with_stopwatch( st.time_sim, [&]() { + sim.add_pattern( validator.cex ); + } ); + + /* re-simulate the whole circuit (for the last block) when a block is full */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + continue; + } + } + else /* timeout */ + { + ++st.num_timeout; + return std::nullopt; + } + } + else /* functor can not find any potential resubstitution */ + { + return std::nullopt; + } + } /* limit on number of trials exceeded */ + return std::nullopt; + } + +private: + void check_tts( node const& n ) + { + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + } + +private: + Ntk const& ntk; + params_t const& ps; + stats_t& st; + typename ResynEngine::stats rst; + ResynEngine engine; + partial_simulator sim; + validator_t validator; + incomplete_node_map tts; + + std::shared_ptr::add_event_type> add_event; +}; /* simulation_guided_resynthesis */ + +} /* namespace detail */ + +using sim_resub_params = boolean_optimization_params; +using sim_resub_stats = boolean_optimization_stats; + +template +void simulation_xag_heuristic_resub( Ntk& ntk, sim_resub_params const& ps = {}, sim_resub_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not xag_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using windowing_t = typename detail::breadth_first_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::simulation_guided_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + sim_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void simulation_aig_heuristic_resub( Ntk& ntk, sim_resub_params const& ps = {}, sim_resub_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not aig_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using windowing_t = typename detail::breadth_first_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::simulation_guided_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + sim_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle::experimental */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/experimental/window_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/experimental/window_resub.hpp new file mode 100644 index 00000000000..e693dfc3a32 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/experimental/window_resub.hpp @@ -0,0 +1,834 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file window_resub.hpp + \brief Windowing for small-window-based, enumeration-based (classical) resubstitution + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/index_list/index_list.hpp" +#include "../../utils/null_utils.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../detail/resub_utils.hpp" +#include "../dont_cares.hpp" +#include "../reconv_cut.hpp" +#include "../resyn_engines/aig_enumerative.hpp" +#include "../resyn_engines/mig_enumerative.hpp" +#include "../resyn_engines/mig_resyn.hpp" +#include "../resyn_engines/xag_resyn.hpp" +#include "../simulation.hpp" + +#include "../../utils/include/percy.hpp" + +#include + +#include +#include +#include + +namespace mockturtle::experimental +{ + +struct complete_tt_windowing_params +{ + /*! \brief Maximum number of PIs of reconvergence-driven cuts. */ + uint32_t max_pis{ 8 }; + + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 150 }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ 2 }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 12u }; + + /*! \brief Whether to update node levels lazily. */ + bool update_levels_lazily{ false }; + + /*! \brief Whether to prevent from increasing depth. */ + bool preserve_depth{ false }; + + /*! \brief Whether to normalize the truth tables. + * + * For some enumerative resynthesis engines, if the truth tables + * are normalized, some cases can be eliminated and thus improves + * efficiency. When this option is turned off, be sure to use an + * implementation of resynthesis that does not make this assumption; + * otherwise, quality degradation may be observed. + * + * Normalization is typically only useful for enumerative methods + * and for smaller solutions (i.e. when `max_inserts` < 2). Turning + * on normalization may result in larger runtime overhead when there + * are many divisors or when the truth tables are long. + */ + bool normalize{ false }; +}; + +struct complete_tt_windowing_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for cut computation. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor collection. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Accumulated runtime for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Accumulated runtime for don't care computation. */ + stopwatch<>::duration time_dont_care{ 0 }; + + /*! \brief Total number of leaves. */ + uint64_t num_leaves{ 0u }; + + /*! \brief Total number of divisors. */ + uint64_t num_divisors{ 0u }; + + /*! \brief Number of constructed windows. */ + uint32_t num_windows{ 0u }; + + /*! \brief Total number of MFFC nodes. */ + uint64_t sum_mffc_size{ 0u }; + + void report() const + { + // clang-format off + fmt::print( "[i] complete_tt_windowing report\n" ); + fmt::print( " tot. #leaves = {:5d}, tot. #divs = {:5d}, sum |MFFC| = {:5d}\n", num_leaves, num_divisors, sum_mffc_size ); + fmt::print( " avg. #leaves = {:>5.2f}, avg. #divs = {:>5.2f}, avg. |MFFC| = {:>5.2f}\n", float( num_leaves ) / float( num_windows ), float( num_divisors ) / float( num_windows ), float( sum_mffc_size ) / float( num_windows ) ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Cut : {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( " MFFC : {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( " Divs : {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( " Simulation: {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( " Dont cares: {:>5.2f} secs\n", to_seconds( time_dont_care ) ); + // clang-format on + } +}; + +template +struct resynthesis_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Total number of problems received. */ + uint32_t num_probs{ 0u }; + + /*! \brief Total number of solutions found. */ + uint32_t num_sols{ 0u }; + + /*! \brief Summed sizes of solutions. */ + uint32_t sum_sol_size{ 0u }; + + /*! \brief Summed ratios of solution size over max size. */ + float sum_ratio{ 0.0 }; + uint32_t sum_overhead{ 0u }; + + /*! \brief Summed max_size of all problems. */ + uint32_t sum_max_size{ 0u }; + + /*! \brief Summed max_size of solved problems. */ + uint32_t sum_max_size_solved{ 0u }; + + /*! \brief Statistics object for the resynthesis engine. */ + EngineStats rst; + + void report() const + { + // clang-format off + fmt::print( "[i] resynthesis report\n" ); + fmt::print( " tot. #problems = {:5d}, tot. #solutions = {:5d}, #sols/#probs = {:>5.2f} (%)\n", num_probs, num_sols, float( num_sols ) / float( num_probs ) * 100.0 ); + fmt::print( " avg. |H| = {:>5.2f}, avg. |H|/max = {:>7.4f}, avg. overhead = {:>5.2f}\n", float( sum_sol_size ) / float( num_sols ), sum_ratio / float( num_sols ), sum_overhead / float( num_sols ) ); + fmt::print( " avg. max size (all problems) = {:>5.2f}, avg. max size (solved) = {:>5.2f}\n", float( sum_max_size ) / float( num_probs ), float( sum_max_size_solved ) / float( num_sols ) ); + fmt::print( " Total runtime: {:>5.2f} secs\n", to_seconds( time_total ) ); + // clang-format on + } +}; + +namespace detail +{ + +template +struct small_window +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + signal root; + uint32_t num_leaves; + std::vector divs; + std::vector div_ids; /* positions of divisor truth tables in `tts` */ + std::vector tts; + TT care; + uint32_t mffc_size; + uint32_t max_size{ std::numeric_limits::max() }; + uint32_t max_level{ std::numeric_limits::max() }; +}; + +template +class complete_tt_windowing +{ +public: + using problem_t = small_window; + using params_t = complete_tt_windowing_params; + using stats_t = complete_tt_windowing_stats; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit complete_tt_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), cps( { ps.max_pis } ), mffc_mgr( ntk ), + divs_mgr( ntk, divisor_collector_params( { ps.max_divisors, ps.max_divisors, ps.skip_fanout_limit_for_divisors } ) ), + sim( ntk, win.tts, ps.max_pis ) + { + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + if constexpr ( !has_level_v ) + { + assert( !ps.preserve_depth && "Ntk does not have depth interface" ); + assert( !ps.update_levels_lazily && "Ntk does not have depth interface" ); + } + } + + ~complete_tt_windowing() + { + if constexpr ( has_level_v ) + { + if ( lazy_update_event ) + { + mockturtle::detail::release_lazy_level_update_events( ntk, lazy_update_event ); + } + } + } + + void init() + { + if constexpr ( has_level_v ) + { + if ( ps.update_levels_lazily ) + { + lazy_update_event = mockturtle::detail::register_lazy_level_update_events( ntk ); + } + } + } + + std::optional> operator()( node const& n ) + { + stopwatch t( st.time_total ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return std::nullopt; /* skip nodes with too many fanouts */ + } + + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth ) + { + win.max_level = ntk.level( n ) - 1; + divs_mgr.set_max_level( win.max_level ); + } + } + + /* compute a cut and collect supported nodes */ + std::vector leaves = call_with_stopwatch( st.time_cuts, [&]() { + return reconvergence_driven_cut>( ntk, { n }, cps ).first; + } ); + std::vector supported; + call_with_stopwatch( st.time_divs, [&]() { + divs_mgr.collect_supported_nodes( n, leaves, supported ); + } ); + + /* simulate */ + call_with_stopwatch( st.time_sim, [&]() { + sim.simulate( leaves, supported ); + } ); + + /* mark MFFC nodes and collect divisors */ + ++mffc_marker; + win.mffc_size = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.call_on_mffc_and_count( n, leaves, [&]( node const& n ) { + ntk.set_value( n, mffc_marker ); + } ); + } ); + call_with_stopwatch( st.time_divs, [&]() { + collect_divisors( leaves, supported ); + } ); + + /* normalize */ + call_with_stopwatch( st.time_sim, [&]() { + if ( ps.normalize ) + { + win.root = normalize_truth_tables() ? !ntk.make_signal( n ) : ntk.make_signal( n ); + } + else + { + win.root = ntk.make_signal( n ); + } + } ); + + /* compute don't cares */ + call_with_stopwatch( st.time_dont_care, [&]() { + if ( ps.use_dont_cares ) + { + win.care = ~satisfiability_dont_cares( ntk, leaves, ps.window_size ); + } + else + { + win.care = ~kitty::create( ps.max_pis ); + } + } ); + + win.max_size = std::min( win.mffc_size - 1, ps.max_inserts ); + + st.num_windows++; + st.num_leaves += leaves.size(); + st.num_divisors += win.divs.size(); + st.sum_mffc_size += win.mffc_size; + + return win; + } + + template + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + return prob.mffc_size - res.num_gates(); + } + + template + bool update_ntk( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + insert( ntk, std::begin( prob.divs ), std::end( prob.divs ), res, [&]( signal const& g ) { + ntk.substitute_node( ntk.get_node( prob.root ), ntk.is_complemented( prob.root ) ? !g : g ); + } ); + return true; /* continue optimization */ + } + + template + bool report( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + fmt::print( "[i] found solution {} for root signal {}{}\n", to_index_list_string( res ), ntk.is_complemented( prob.root ) ? "!" : "", ntk.get_node( prob.root ) ); + return true; + } + +private: + void collect_divisors( std::vector const& leaves, std::vector const& supported ) + { + win.divs.clear(); + win.div_ids.clear(); + + uint32_t i{ 1 }; + for ( auto const& l : leaves ) + { + win.div_ids.emplace_back( i++ ); + win.divs.emplace_back( ntk.make_signal( l ) ); + } + win.num_leaves = leaves.size(); + + i = ps.max_pis + 1; + for ( auto const& n : supported ) + { + if ( ntk.value( n ) != mffc_marker ) /* not in MFFC, not root */ + { + win.div_ids.emplace_back( i ); + win.divs.emplace_back( ntk.make_signal( n ) ); + } + ++i; + } + assert( i == win.tts.size() ); + } + + bool normalize_truth_tables() + { + assert( win.divs.size() == win.div_ids.size() ); + for ( auto i = 0u; i < win.divs.size(); ++i ) + { + if ( kitty::get_bit( win.tts.at( win.div_ids.at( i ) ), 0 ) ) + { + win.tts.at( win.div_ids.at( i ) ) = ~win.tts.at( win.div_ids.at( i ) ); + win.divs.at( i ) = !win.divs.at( i ); + } + } + + if ( kitty::get_bit( win.tts.back(), 0 ) ) + { + win.tts.back() = ~win.tts.back(); + return true; + } + else + { + return false; + } + } + +private: + Ntk& ntk; + problem_t win; + params_t const& ps; + stats_t& st; + reconvergence_driven_cut_parameters const cps; + typename mockturtle::detail::node_mffc_inside mffc_mgr; // TODO: namespaces can be removed when we move out of experimental:: + divisor_collector divs_mgr; + window_simulator sim; + uint32_t mffc_marker{ 0u }; + std::shared_ptr::modified_event_type> lazy_update_event; +}; /* complete_tt_windowing */ + +template +class complete_tt_resynthesis +{ +public: + using problem_t = small_window; + using res_t = typename ResynEngine::index_list_t; + using params_t = null_params; + using stats_t = resynthesis_stats; + + explicit complete_tt_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), st( st ), engine( st.rst ) + {} + + void init() + {} + + std::optional operator()( problem_t& prob ) + { + ++st.num_probs; + st.sum_max_size += prob.max_size; + auto const res = call_with_stopwatch( st.time_total, [&](){ + if constexpr ( preserve_depth ) + { + return engine( prob.tts.back(), prob.care, std::begin( prob.div_ids ), std::end( prob.div_ids ), prob.tts, prob.max_size, prob.max_level ); + } + else + { + return engine( prob.tts.back(), prob.care, std::begin( prob.div_ids ), std::end( prob.div_ids ), prob.tts, prob.max_size ); + } + }); + if ( res ) + { + ++st.num_sols; + st.sum_max_size_solved += prob.max_size; + st.sum_sol_size += res->num_gates(); + if ( prob.max_size == 0 ) + st.sum_ratio += 1; + else + st.sum_ratio += float( res->num_gates() ) / float( prob.max_size ); + } + return res; + } + +private: + Ntk const& ntk; + stats_t& st; + ResynEngine engine; +}; /* complete_tt_resynthesis */ + +template> +class exact_resynthesis +{ +public: + using problem_t = small_window; + using res_t = index_list_t; + using params_t = null_params; + using stats_t = resynthesis_stats; + + explicit exact_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), st( st ) + {} + + void init() + { + _allow_xor = false; + } + + std::optional operator()( problem_t& prob ) + { + ++st.num_probs; + st.sum_max_size += prob.max_size; + auto c = call_with_stopwatch( st.time_total, [&](){ + return solve( prob ); + }); + + if ( c && c->get_nr_steps() <= prob.max_size ) + { + ++st.num_sols; + st.sum_max_size_solved += prob.max_size; + st.sum_sol_size += c->get_nr_steps(); + if ( prob.max_size == 0 ) + st.sum_ratio += 1; + else + st.sum_ratio += float( c->get_nr_steps() ) / float( prob.max_size ); + //return translate( prob, *c ); + } + return std::nullopt; + } + +private: + std::optional solve( problem_t& prob ) + { + percy::spec spec; + if ( !_allow_xor ) + { + spec.set_primitive( percy::AIG ); + } + spec.fanin = 2; + spec.verbosity = 0; + spec.add_alonce_clauses = _ps.add_alonce_clauses; + spec.add_colex_clauses = _ps.add_colex_clauses; + spec.add_lex_clauses = _ps.add_lex_clauses; + spec.add_lex_func_clauses = _ps.add_lex_func_clauses; + spec.add_nontriv_clauses = _ps.add_nontriv_clauses; + spec.add_noreapply_clauses = _ps.add_noreapply_clauses; + spec.add_symvar_clauses = _ps.add_symvar_clauses; + spec.conflict_limit = _ps.conflict_limit; + spec.max_nr_steps = prob.max_size; + + TT const& target = prob.tts.back(); + spec[0] = target; + bool with_dont_cares{ false }; + if ( !kitty::is_const0( ~prob.care ) ) + { + spec.set_dont_care( 0, ~prob.care ); + with_dont_cares = true; + } + + /* add divisors */ + for ( auto i = prob.num_leaves; i < prob.divs.size(); ++i ) + { + spec.add_function( prob.tts[prob.div_ids[i]] ); + } + + //if ( !with_dont_cares && _ps.cache ) + //{ + // const auto it = _ps.cache->find( target ); + // if ( it != _ps.cache->end() ) + // { + // return it->second; + // } + //} + //if ( !with_dont_cares && _ps.blacklist_cache ) + //{ + // const auto it = _ps.blacklist_cache->find( target ); + // if ( it != _ps.blacklist_cache->end() && ( it->second == 0 || _ps.conflict_limit <= it->second ) ) + // { + // return std::nullopt; + // } + //} + + percy::chain c; + if ( const auto result = percy::synthesize( spec, c, _ps.solver_type, + _ps.encoder_type, + _ps.synthesis_method ); + result != percy::success ) + { + //if ( !with_dont_cares && _ps.blacklist_cache ) + //{ + // ( *_ps.blacklist_cache )[target] = ( result == percy::timeout ) ? _ps.conflict_limit : 0; + //} + return std::nullopt; + } + + assert( kitty::to_hex( c.simulate()[0u] & prob.care ) == kitty::to_hex( target & prob.care ) ); + + //if ( !with_dont_cares && _ps.cache ) + //{ + // ( *_ps.cache )[target] = c; + //} + return c; + } + + index_list_t translate( problem_t& prob, percy::chain const& c ) + { + index_list_t il( prob.divs.size() ); + /* Doesn't work well yet + std::vector negated( prob.divs.size() + c->get_nr_steps() + 1, false ); + for ( auto i = 0; i < c->get_nr_steps(); ++i ) + { + auto const v1 = c->get_step( i )[0] + 1; // +1 : zero-based to one-based indices + auto const v2 = c->get_step( i )[1] + 1; + auto const l1 = negated[v1] ? ( v1 << 1 ) ^ 0x1 : v1 << 1; + auto const l2 = negated[v2] ? ( v2 << 1 ) ^ 0x1 : v2 << 1; + + switch ( c->get_operator( i )._bits[0] ) + { + default: + std::cerr << "[e] unsupported operation " << kitty::to_hex( c->get_operator( i ) ) << "\n"; + assert( false ); + break; + case 0x8: + il.add_and( l1, l2 ); + break; + case 0x4: + il.add_and( l1 ^ 0x1, l2 ); + break; + case 0x2: + il.add_and( l1, l2 ^ 0x1 ); + break; + case 0xe: + negated[il.add_and( l1 ^ 0x1, l2 ^ 0x1 ) >> 1] = true; + break; + case 0x6: + il.add_xor( l1, l2 ); + break; + } + } + + auto const last_lit = ( prob.divs.size() + c->get_nr_steps() ) << 1; + il.add_output( c->is_output_inverted( 0 ) ? last_lit ^ 0x1 : last_lit ); + */ + return il; + } + +private: + Ntk const& ntk; + bool _allow_xor; + struct + { + using cache_map_t = std::unordered_map>; + using cache_t = std::shared_ptr; + + using blacklist_cache_map_t = std::unordered_map>; + using blacklist_cache_t = std::shared_ptr; + + cache_t cache; + blacklist_cache_t blacklist_cache; + + bool add_alonce_clauses{ true }; + bool add_colex_clauses{ true }; + bool add_lex_clauses{ false }; + bool add_lex_func_clauses{ true }; + bool add_nontriv_clauses{ true }; + bool add_noreapply_clauses{ true }; + bool add_symvar_clauses{ true }; + int conflict_limit{ 1000 }; + + percy::SolverType solver_type = percy::SLV_BSAT2; + percy::EncoderType encoder_type = percy::ENC_SSV; + percy::SynthMethod synthesis_method = percy::SYNTH_STD; + } _ps; + stats_t& st; +}; /* exact_resynthesis */ + +} /* namespace detail */ + +using window_resub_params = boolean_optimization_params; +using window_resub_stats = boolean_optimization_stats>; +using window_resub_stats_xag = boolean_optimization_stats>; +using window_resub_stats_aig_enum = boolean_optimization_stats>; +using window_resub_stats_mig = boolean_optimization_stats>; + +template +void window_xag_heuristic_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_xag* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not xag_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats_xag st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_aig_heuristic_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_xag* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not aig_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats_xag st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_aig_enumerative_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_aig_enum* pst = nullptr ) +{ + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + window_resub_stats_aig_enum st; + + using TT = typename kitty::static_truth_table<8>; + using windowing_t = typename detail::complete_tt_windowing; + if ( ps.wps.normalize ) + { + using engine_t = aig_enumerative_resyn; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + opt_t p( viewed, ps, st ); + p.run(); + } + else + { + using engine_t = aig_enumerative_resyn; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + opt_t p( viewed, ps, st ); + p.run(); + } + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_mig_heuristic_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_mig* pst = nullptr ) +{ + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = mig_resyn_topdown; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats_mig st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_mig_enumerative_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats* pst = nullptr ) +{ + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = mig_enumerative_resyn; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle::experimental */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/explorer.hpp b/third-party/mockturtle/include/mockturtle/algorithms/explorer.hpp new file mode 100644 index 00000000000..1e27c6e0791 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/explorer.hpp @@ -0,0 +1,996 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file explorer.hpp + \brief Implements the design space explorer engine + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "lut_mapper.hpp" +#include "collapse_mapped.hpp" +#include "klut_to_graph.hpp" +#include "cut_rewriting.hpp" +#include "refactoring.hpp" +#include "mig_algebraic_rewriting.hpp" +#include "mapper.hpp" +#include "rewrite.hpp" +#include "node_resynthesis/mig_npn.hpp" +#include "node_resynthesis/sop_factoring.hpp" +#include "resubstitution.hpp" +#include "aig_resub.hpp" +#include "mig_resub.hpp" +#include "sim_resub.hpp" +#include "cleanup.hpp" +#include "balancing.hpp" +#include "balancing/sop_balancing.hpp" +#include "aig_balancing.hpp" +#include "miter.hpp" +#include "equivalence_checking.hpp" +#include "aqfp/buffer_insertion.hpp" +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../views/mapping_view.hpp" +#include "../io/write_verilog.hpp" +#include "../io/write_aiger.hpp" +#include "../io/verilog_reader.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/abc.hpp" + +#include + +#define explorer_debug 0 + +namespace mockturtle +{ + +struct explorer_params +{ + /*! \brief Number of iterations to run with different random seed, restarting from the original + * network (including the first iteration). */ + uint32_t num_restarts{1u}; + + /*! \brief Initial random seed used to generate random seeds randomly. */ + uint32_t random_seed{0u}; + + /*! \brief Maximum number of steps in each iteration. */ + uint32_t max_steps{100000u}; + + /*! \brief Maximum number of steps without improvement in each iteration. */ + uint32_t max_steps_no_impr{1000000u}; + + /*! \brief Number of compressing scripts to run per step. */ + uint32_t compressing_scripts_per_step{3u}; + + /*! \brief Timeout per iteration in seconds. */ + uint32_t timeout{30u}; + + /*! \brief Be verbose. */ + bool verbose{false}; + + /*! \brief Be very verbose. */ + bool very_verbose{false}; +}; + +struct explorer_stats +{ + stopwatch<>::duration time_total{0}; + stopwatch<>::duration time_evaluate{0}; +}; + +template +using script_t = std::function; + +template +using cost_fn_t = std::function; + +template +std::function size_cost_fn = []( Ntk const& ntk ){ return ntk.num_gates(); }; + +template +class explorer +{ +public: + using RandEngine = std::default_random_engine; + + explorer( explorer_params const& ps, explorer_stats& st, cost_fn_t const& cost_fn = size_cost_fn ) + : _ps( ps ), _st( st ), cost( cost_fn ) + { + } + + void add_decompressing_script( script_t const& algo, float weight = 1.0 ) + { + decompressing_scripts.emplace_back( std::make_pair( algo, total_weights_dec ) ); + total_weights_dec += weight; + } + + void add_compressing_script( script_t const& algo, float weight = 1.0 ) + { + compressing_scripts.emplace_back( std::make_pair( algo, total_weights_com ) ); + total_weights_com += weight; + } + + Ntk run( Ntk const& ntk ) + { + stopwatch t( _st.time_total ); + if ( decompressing_scripts.size() == 0 ) + { + std::cerr << "[e] No decompressing script provided.\n"; + return ntk; + } + if ( compressing_scripts.size() == 0 ) + { + std::cerr << "[e] No compressing script provided.\n"; + return ntk; + } + + RandEngine rnd( _ps.random_seed ); + auto init_cost = call_with_stopwatch( _st.time_evaluate, [&](){ return cost( ntk ); } ); + Ntk best = ntk.clone(); + auto best_cost = init_cost; + for ( auto i = 0u; i < _ps.num_restarts; ++i ) + { + Ntk current = ntk.clone(); + auto new_cost = run_one_iteration( current, rnd(), init_cost ); + if ( new_cost < best_cost ) + { + best = current.clone(); + best_cost = new_cost; + } + if ( _ps.verbose ) + fmt::print( "[i] best cost in restart {}: {}, overall best cost: {}\n", i, new_cost, best_cost ); + } + return best; + } + +private: + uint32_t run_one_iteration( Ntk& ntk, uint32_t seed, uint32_t init_cost ) + { + if ( _ps.verbose ) + { + fmt::print( "\n[i] new restart using seed {}, original cost = {}\n", seed, init_cost ); + } + + stopwatch<>::duration elapsed_time{0}; + RandEngine rnd( seed ); + Ntk best = ntk.clone(); + auto best_cost = init_cost; + uint32_t last_update{0u}; + for ( auto i = 0u; i < _ps.max_steps; ++i ) + { + #if explorer_debug + Ntk backup = ntk.clone(); + #endif + + { + stopwatch t( elapsed_time ); + decompress( ntk, rnd, i ); + compress( ntk, rnd, i ); + } + auto new_cost = call_with_stopwatch( _st.time_evaluate, [&](){ return cost( ntk ); } ); + if ( _ps.very_verbose ) + fmt::print( "[i] after step {}, cost = {}\n", i, new_cost ); + + #if explorer_debug + if ( !*equivalence_checking( *miter( ntk, best ) ) ) + { + write_verilog( backup, "debug.v" ); + write_verilog( ntk, "wrong.v" ); + fmt::print( "NEQ at step {}!\n", i ); + break; + } + #endif + + if ( new_cost < best_cost ) + { + best = ntk.clone(); + best_cost = new_cost; + last_update = i; + if ( _ps.verbose ) + { + fmt::print( "[i] updated new best at step {}: {}\n", i, best_cost ); + } + } + if ( i - last_update >= _ps.max_steps_no_impr ) + { + if ( _ps.verbose ) + fmt::print( "[i] break restart at step {} after {} steps without improvement (elapsed time: {} secs)\n", i, _ps.max_steps_no_impr, to_seconds( elapsed_time ) ); + break; + } + if ( to_seconds( elapsed_time ) >= _ps.timeout ) + { + if ( _ps.verbose ) + fmt::print( "[i] break restart at step {} after timeout of {} secs\n", i, to_seconds( elapsed_time ) ); + break; + } + } + std::cout << std::flush; + ntk = best; + return best_cost; + } + + void decompress( Ntk& ntk, RandEngine& rnd, uint32_t i ) + { + std::uniform_real_distribution<> dis( 0.0, total_weights_dec ); + float r = dis( rnd ); + for ( auto it = decompressing_scripts.rbegin(); it != decompressing_scripts.rend(); ++it ) + { + if ( r >= it->second ) + { + it->first( ntk, i, rnd() ); + break; + } + } + } + + void compress( Ntk& ntk, RandEngine& rnd, uint32_t i ) + { + std::uniform_real_distribution<> dis( 0.0, total_weights_com ); + for ( auto j = 0u; j < _ps.compressing_scripts_per_step; ++j ) + { + float r = dis( rnd ); + for ( auto it = compressing_scripts.rbegin(); it != compressing_scripts.rend(); ++it ) + { + if ( r >= it->second ) + { + it->first( ntk, i, rnd() ); + break; + } + } + } + } + +private: + const explorer_params _ps; + explorer_stats& _st; + + std::vector, float>> decompressing_scripts; + float total_weights_dec{0.0}; + std::vector, float>> compressing_scripts; + float total_weights_com{0.0}; + + cost_fn_t cost; +}; + +mig_network explore_mig( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (rand & 0x3); //3 + (i % 4); + mapping_view mapped{ _ntk }; + lut_map( mapped, mps ); + const auto klut = *collapse_mapped_network( mapped ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + mig_npn_resynthesis resyn{ true }; + exact_library exact_lib( resyn ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( _ntk, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x7; + rps.max_pis = (rand >> 3) & 0x1 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = 6u; + _ntk = balancing( _ntk, {balance_fn}, bps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + } ); + + return expl.run( ntk ); +} + +#ifdef ENABLE_ABC +mig_network deepsyn_mig_v1( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with &if using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20; &st", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5)); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (rand & 0x3); //3 + (i % 4); + klut_network klut = lut_map( _ntk, mps ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with resyn2rs using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + //std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + std::string script = "&put; resyn2rs; &get"; + + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x2; + exact_library exact_lib( resyn, eps ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; // high-effort remap + mps.use_dont_cares = rand & 0x2; + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with remapping using random value {}\n", rand ); + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x2; + exact_library exact_lib( resyn, eps ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; // high-effort remap + mps.use_dont_cares = rand & 0x2; + _ntk = map( _ntk, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with rewriting using random value {}\n", rand ); + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x1; + exact_library exact_lib( resyn, eps ); + rewrite_params rps; + rps.use_dont_cares = rand & 0x1; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with resub using random value {}\n", rand ); + resubstitution_params rps; + rps.max_inserts = (rand >> 1) & 0x7; + rps.max_pis = (rand >> 4) & 0x3 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + return expl.run( ntk ); +} + +mig_network deepsyn_mig_v2( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with &if using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20; &st", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5)); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (rand & 0x3); //3 + (i % 4); + klut_network klut = lut_map( _ntk, mps ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with resyn2rs using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + //std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + std::string script = "&put; resyn2rs; &get"; + + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x2; + exact_library exact_lib( resyn, eps ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; // high-effort remap + mps.use_dont_cares = rand & 0x2; + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with Ale flow using random value {}\n", rand ); + //_ntk = cleanup_dangling( _ntk ); + + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = true; + exact_library exact_lib( resyn, eps ); + + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = true; // high-effort remap + + rewrite_params rps; + + resubstitution_params rsps; + rsps.max_inserts = 20; + rsps.max_pis = 8; + + mps.use_dont_cares = rand & 0x8; + _ntk = map( _ntk, exact_lib, mps ); + mps.use_dont_cares = rand & 0xf; + _ntk = map( _ntk, exact_lib, mps ); + mps.use_dont_cares = rand & 0x10; + _ntk = map( _ntk, exact_lib, mps ); + + rps.use_dont_cares = rand & 0x1; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + rps.use_dont_cares = rand & 0x2; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + rps.use_dont_cares = rand & 0x4; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rsps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + return expl.run( ntk ); +} + +mig_network deepsyn_mig_depth( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + cost_fn_t depth_cost = []( Ntk const& _ntk ){ + depth_view d{ _ntk }; + return d.depth(); + }; + + explorer_stats st; + explorer expl( ps, st, depth_cost ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{} -m; &if -K {}; &mfs -e -W 20 {}", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5), + ((rand >> 2) & 0x1) ? "; &fx; &st" : ""); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = false; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + _ntk = cleanup_dangling( _ntk ); + }, 0.3 ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + std::string script = "&put; resyn2rs; &get"; + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = false; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + mig_npn_resynthesis resyn{ true }; + exact_library exact_lib( resyn ); + map_params mps; + mps.skip_delay_round = false; + //mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( _ntk, exact_lib, mps ); + }, 0.5 ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x7; + rps.max_pis = (rand >> 3) & 0x1 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = ( rand & 0x1 ) ? 8 : 10; + _ntk = balancing( _ntk, {balance_fn}, bps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_factoring resyn; + refactoring( _ntk, resyn ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + return expl.run( ntk ); +} + +aig_network deepsyn_aig( aig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = aig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20{}", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5), + ((rand >> 2) & 0x1) ? "; &fx; &st" : ""); + _ntk = call_abc_script( _ntk, script ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + _ntk = call_abc_script( _ntk, script ); + } ); + + return expl.run( ntk ); +} + +mig_network deepsyn_aqfp( mig_network const& ntk, explorer_params const ps = {}, explorer_stats * pst = nullptr ) +{ + using Ntk = mig_network; + + cost_fn_t aqfp_cost = []( Ntk const& _ntk ){ + buffer_insertion_params bps; + bps.assume.balance_cios = true; + bps.assume.splitter_capacity = 4; + bps.assume.ci_phases = {0}; + bps.assume.num_phases = 1; + bps.scheduling = buffer_insertion_params::better_depth; + bps.optimization_effort = buffer_insertion_params::one_pass; + buffer_insertion buf_inst( _ntk, bps ); + auto numbufs = buf_inst.dry_run(); + + //return (_ntk.num_gates() * 6 + numbufs * 2); + return (_ntk.num_gates() * 6 + numbufs * 2) * buf_inst.depth(); + }; + + explorer_stats st; + explorer expl( ps, st, aqfp_cost ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20{}", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5), + ((rand >> 2) & 0x1) ? "; &fx; &st" : ""); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( _ntk, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x3; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + // balancing + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = 6u; + _ntk = balancing( _ntk, {balance_fn}, bps ); + } ); + + // algebraic depth optimization + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + } ); + + auto res = expl.run( ntk ); + if ( pst ) + *pst = st; + return res; +} +#endif + +void compress2rs_aig( aig_network& aig ) +{ + xag_npn_resynthesis resyn; + cut_rewriting_params cps; + cps.cut_enumeration_ps.cut_size = 4; + resubstitution_params rps; + //sop_rebalancing balance_fn; + //balancing_params bps; + //bps.cut_enumeration_ps.cut_size = 6u; + aig_balancing_params abps; + refactoring_params fps; + sop_factoring resyn2; + + /*abps.minimize_levels = true;*/ aig_balance( aig, abps ); // "b -l" + rps.max_pis = 6; rps.max_inserts = 1; /*rps.preserve_depth = true;*/ aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 6 -l" + /*cps.preserve_depth = true;*/ aig = cut_rewriting( aig, resyn, cps ); // "rw -l" + rps.max_pis = 6; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 6 -N 2 -l" + /*fps.preserve_depth = true;*/ refactoring( aig, resyn2, fps ); aig = cleanup_dangling( aig ); // "rf -l" + rps.max_pis = 8; rps.max_inserts = 1; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 8 -l" + aig_balance( aig, abps ); // "b -l" + rps.max_pis = 8; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 8 -N 2 -l" + aig = cut_rewriting( aig, resyn, cps ); // "rw -l" + rps.max_pis = 10; rps.max_inserts = 1; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 10 -l" + cps.allow_zero_gain = true; aig = cut_rewriting( aig, resyn, cps ); // "rwz -l" + rps.max_pis = 10; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 10 -N 2 -l" + aig_balance( aig, abps ); // "b -l" + rps.max_pis = 12; rps.max_inserts = 1; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 12 -l" + fps.allow_zero_gain = true; refactoring( aig, resyn2, fps ); aig = cleanup_dangling( aig ); // "rfz -l" + rps.max_pis = 12; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 12 -N 2 -l" + aig = cut_rewriting( aig, resyn, cps ); // "rwz -l" + aig_balance( aig, abps ); // "b -l" +} + +mig_network explore_aqfp( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + cost_fn_t aqfp_cost = []( Ntk const& _ntk ){ + buffer_insertion_params bps; + bps.assume.balance_cios = true; + bps.assume.splitter_capacity = 4; + bps.assume.ci_phases = {0}; + bps.scheduling = buffer_insertion_params::better_depth; + bps.optimization_effort = buffer_insertion_params::none; + buffer_insertion buf_inst( _ntk, bps ); + + return _ntk.num_gates() * 6 + buf_inst.dry_run() * 2; + }; + + explorer_stats st; + explorer expl( ps, st, aqfp_cost ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (i & 0x3); + mapping_view mapped{ _ntk }; + lut_map( mapped, mps ); + const auto klut = *collapse_mapped_network( mapped ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + }, 0.3 ); + + // high-effort AIG optimization + (50% chance high-effort) mapping + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + compress2rs_aig( aig ); + + mig_npn_resynthesis resyn3{ true }; + exact_library exact_lib( resyn3 ); + map_params mps; + mps.skip_delay_round = false; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( aig, exact_lib, mps ); + } ); + + // high-effort MIG resub x1 + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x7; + rps.max_pis = (rand >> 3) & 0x1 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + // balancing + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = 6u; + _ntk = balancing( _ntk, {balance_fn}, bps ); + }, 0.5 ); + + // algebraic depth optimization + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + return expl.run( ntk ); +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/extract_adders.hpp b/third-party/mockturtle/include/mockturtle/algorithms/extract_adders.hpp new file mode 100644 index 00000000000..072aaf35a00 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/extract_adders.hpp @@ -0,0 +1,971 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file extract_adders.hpp + \brief Maps adders in the network + + \author Alessandro Tempia Calvino +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#include "../networks/block.hpp" +#include "../networks/storage.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/choice_view.hpp" +#include "cut_enumeration.hpp" + +namespace mockturtle +{ + +struct extract_adders_params +{ + extract_adders_params() + { + cut_enumeration_ps.cut_limit = 49; + cut_enumeration_ps.minimize_truth_table = false; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 49. By default, + * truth table minimization is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Map inverted (NAND2-XNOR2, MIN3-XNOR3) */ + bool map_inverted{ false }; + + /*! \brief Filter HAs/FAs using MFFC inclusion */ + bool use_mffc_filter{ true }; + + /*! \brief Be verbose */ + bool verbose{ false }; +}; + +struct extract_adders_stats +{ + /*! \brief Computed cuts. */ + uint32_t cuts_total{ 0 }; + + /*! \brief Gates count. */ + uint32_t and2{ 0 }; + uint32_t maj3{ 0 }; + uint32_t xor2{ 0 }; + uint32_t xor3{ 0 }; + + /*! \brief Hashed classes. */ + uint32_t num_classes{ 0 }; + + /*! \brief Hash size. */ + uint32_t mapped_ha{ 0 }; + uint32_t mapped_fa{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] Cuts = {}\t And2 = {}\t Xor2 = {}\t Maj3 = {}\t Xor3 = {}\n", + cuts_total, and2, xor2, maj3, xor3 ); + std::cout << fmt::format( "[i] Classes = {} \tMapped HA = {}\t Mapped FA:{}\n", num_classes, mapped_ha, mapped_fa ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +struct triple_hash +{ + uint64_t operator()( const std::array& p ) const + { + uint64_t seed = hash_block( p[0] ); + + hash_combine( seed, hash_block( p[1] ) ); + hash_combine( seed, hash_block( p[2] ) ); + + return seed; + } +}; + +struct cut_enumeration_fa_cut +{ + /* stats */ + bool is_xor{ false }; +}; + +template +class extract_adders_impl +{ +public: + using network_cuts_t = fast_network_cuts; + using cut_t = typename network_cuts_t::cut_t; + using leaves_hash_t = phmap::flat_hash_map, std::vector, triple_hash>; + using match_pair_t = std::pair; + using matches_t = std::vector; + using block_map = node_map, Ntk>; + +public: + explicit extract_adders_impl( Ntk& ntk, extract_adders_params const& ps, extract_adders_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps ) ), + cuts_classes(), + half_adders(), + full_adders(), + node_match( ntk.size(), UINT32_MAX ) + { + cuts_classes.reserve( 2000 ); + tmp_visited.reserve( 20 ); + } + + block_network run() + { + stopwatch t( st.time_total ); + + auto [res, old2new] = initialize_map_network(); + create_classes(); + match_adders(); + map(); + topo_sort(); + finalize( res, old2new ); + + return res; + } + +private: + void create_classes() + { + uint32_t counter = 0; + std::array leaves = { 0, 0, 0 }; + + st.cuts_total = cuts.total_cuts(); + + ntk.foreach_gate( [&]( auto const& n ) { + uint32_t cut_index = 0; + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + kitty::static_truth_table<3> tt = cuts.truth_table( *cut ); + + bool to_add = false; + if ( cut->size() == 2 ) + { + /* check for and2 */ + for ( uint32_t func : and2func ) + { + if ( tt._bits == func ) + { + ++st.and2; + to_add = true; + break; + } + } + + /* check for xor2 */ + for ( uint32_t func : xor2func ) + { + if ( tt._bits == func ) + { + ++st.xor2; + ( *cut )->data.is_xor = true; + to_add = true; + break; + } + } + } + else if ( cut->size() == 3 ) + { + /* check for maj3 */ + for ( uint32_t func : maj3func ) + { + if ( tt._bits == func ) + { + ++st.maj3; + to_add = true; + break; + } + } + + /* check xor3 */ + for ( uint32_t func : xor3func ) + { + if ( tt._bits == func ) + { + ++st.xor3; + ( *cut )->data.is_xor = true; + to_add = true; + break; + } + } + } + + if ( !to_add ) + { + ++cut_index; + continue; + } + + uint64_t data = ( static_cast( ntk.node_to_index( n ) ) << 16 ) | cut_index; + leaves[2] = 0; + uint32_t i = 0; + for ( auto l : *cut ) + leaves[i++] = l; + + /* add to hash table */ + auto& v = cuts_classes[leaves]; + v.push_back( data ); + + ++cut_index; + } + } ); + + st.num_classes = cuts_classes.size(); + } + + void match_adder2( std::pair, std::vector> const& it ) + { + for ( uint32_t i = 0; i < it.second.size() - 1; ++i ) + { + uint64_t data_i = it.second[i]; + uint32_t index_i = data_i >> 16; + uint32_t cut_index_i = data_i & UINT16_MAX; + auto const& cut_i = cuts.cuts( index_i )[cut_index_i]; + + /* TODO: find unique matches */ + for ( uint32_t j = i + 1; j < it.second.size(); ++j ) + { + uint64_t data_j = it.second[j]; + uint32_t index_j = data_j >> 16; + uint32_t cut_index_j = data_j & UINT16_MAX; + auto const& cut_j = cuts.cuts( index_j )[cut_index_j]; + + /* not compatible */ + if ( cut_i->data.is_xor == cut_j->data.is_xor ) + continue; + + /* check compatibility */ + if ( !check_adder( index_i, index_j, cut_i ) ) + continue; + + assert( cut_i.size() == 2 ); + assert( cut_j.size() == 2 ); + + half_adders.push_back( { data_i, data_j } ); + } + } + } + + void match_adders() + { + half_adders.reserve( cuts_classes.size() ); + full_adders.reserve( cuts_classes.size() ); + ntk.clear_values(); + + for ( auto& it : cuts_classes ) + { + /* not matched */ + if ( it.second.size() < 2 ) + continue; + + /* half adder */ + if ( it.first[2] == 0 ) + { + match_adder2( it ); + continue; + } + + for ( uint32_t i = 0; i < it.second.size() - 1; ++i ) + { + uint64_t data_i = it.second[i]; + uint32_t index_i = data_i >> 16; + uint32_t cut_index_i = data_i & UINT16_MAX; + auto const& cut_i = cuts.cuts( index_i )[cut_index_i]; + + /* TODO: find unique matches */ + for ( uint32_t j = i + 1; j < it.second.size(); ++j ) + { + uint64_t data_j = it.second[j]; + uint32_t index_j = data_j >> 16; + uint32_t cut_index_j = data_j & UINT16_MAX; + auto const& cut_j = cuts.cuts( index_j )[cut_index_j]; + + /* not compatible */ + if ( cut_i->data.is_xor == cut_j->data.is_xor ) + continue; + + /* check compatibility */ + if ( !check_adder( index_i, index_j, cut_i ) ) + continue; + + assert( cut_i.size() == 3 ); + assert( cut_j.size() == 3 ); + + full_adders.push_back( { data_i, data_j } ); + } + } + } + } + + void map() + { + selected.reserve( full_adders.size() + half_adders.size() ); + + ntk.incr_trav_id(); + + for ( uint32_t i = 0; i < full_adders.size(); ++i ) + { + auto& pair = full_adders[i]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + cut_t const& cut = cuts.cuts( index1 )[cut_index1]; + + /* remove overlapping multi-output gates */ + if ( !gate_mark( index1, index2, cut ) ) + continue; + + selected.push_back( 2 * i ); + node_match[std::max( index1, index2 )] = 2 * i; + node_match[std::min( index1, index2 )] = UINT32_MAX - 1; + + ++st.mapped_fa; + } + + for ( uint32_t i = 0; i < half_adders.size(); ++i ) + { + auto& pair = half_adders[i]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + cut_t const& cut = cuts.cuts( index1 )[cut_index1]; + + if ( !gate_mark( index1, index2, cut ) ) + continue; + + selected.push_back( 2 * i + 1 ); + node_match[std::max( index1, index2 )] = 2 * i + 1; + node_match[std::min( index1, index2 )] = UINT32_MAX - 1; + + ++st.mapped_ha; + } + } + + void topo_sort() + { + topo_order.reserve( ntk.size() ); + + /* add map choices */ + choice_view choice_ntk{ ntk }; + add_choices( choice_ntk ); + + ntk.incr_trav_id(); + ntk.incr_trav_id(); + + /* add constants and CIs */ + const auto c0 = ntk.get_node( ntk.get_constant( false ) ); + ntk.set_visited( c0, ntk.trav_id() ); + + if ( const auto c1 = ntk.get_node( ntk.get_constant( true ) ); ntk.visited( c1 ) != ntk.trav_id() ) + { + ntk.set_visited( c1, ntk.trav_id() ); + } + + ntk.foreach_ci( [&]( auto const& n ) { + if ( ntk.visited( n ) != ntk.trav_id() ) + { + ntk.set_visited( n, ntk.trav_id() ); + } + } ); + + /* sort topologically */ + ntk.foreach_co( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + return; + topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + } ); + } + + void add_choices( choice_view& choice_ntk ) + { + for ( uint32_t index : selected ) + { + auto& pair = ( index & 1 ) ? half_adders[index >> 1] : full_adders[index >> 1]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + + if ( index1 > index2 ) + std::swap( index1, index2 ); + + choice_ntk.add_choice( ntk.index_to_node( index1 ), ntk.index_to_node( index2 ) ); + + assert( choice_ntk.count_choices( ntk.index_to_node( index1 ) ) == 2 ); + } + } + + inline bool check_adder( uint32_t index1, uint32_t index2, cut_t const& cut ) + { + bool valid = true; + + /* check containment of cut1 in cut2 and vice versa */ + if ( index1 > index2 ) + { + std::swap( index1, index2 ); + } + + ntk.foreach_fanin( ntk.index_to_node( index2 ), [&]( auto const& f ) { + auto g = ntk.get_node( f ); + if ( ntk.node_to_index( g ) == index1 && ntk.fanout_size( g ) == 1 ) + { + valid = false; + } + return valid; + } ); + + if ( !valid ) + return false; + + /* check containment when node is reachable from middle nodes with multiple fanouts */ + return check_adder_tfi_valid( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ); + } + + inline bool gate_mark( uint32_t index1, uint32_t index2, cut_t const& cut ) + { + bool contained = false; + + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + contained = mark_visited_rec( ntk.index_to_node( index1 ) ); + contained |= mark_visited_rec( ntk.index_to_node( index2 ) ); + + if ( contained ) + { + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + return false; + } + + /* mark*/ + mark_visited_rec( ntk.index_to_node( index1 ) ); + mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return true; + } + + template + bool mark_visited_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return false; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + if constexpr ( MARK ) + { + ntk.set_visited( n, ntk.trav_id() ); + } + + bool contained = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + contained |= mark_visited_rec( ntk.get_node( f ) ); + + if constexpr ( !MARK ) + { + if ( contained ) + return false; + } + + return true; + } ); + + return contained; + } + + inline bool check_adder_tfi_valid( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + bool valid = true; + if ( ps.use_mffc_filter ) + { + tmp_visited.clear(); + dereference_node_rec( root ); + + if ( ntk.fanout_size( n ) == 0 ) + valid = false; + + for ( auto g : tmp_visited ) + ntk.incr_fanout_size( g ); + } + else + { + ntk.incr_trav_id(); + check_adder_tfi_valid_rec( root, root, n, valid ); + } + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return valid; + } + + bool check_adder_tfi_valid_rec( node const& n, node const& root, node const& target, bool& valid ) + { + /* leaf */ + if ( ntk.value( n ) ) + return false; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return false; + + ntk.set_visited( n, ntk.trav_id() ); + + if ( n == target ) + return true; + + bool found = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + found |= check_adder_tfi_valid_rec( ntk.get_node( f ), root, target, valid ); + return valid; + } ); + + if ( found && n != root && ntk.fanout_size( n ) > 1 ) + valid = false; + + return found; + } + + void dereference_node_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + node g = ntk.get_node( f ); + if ( ntk.decr_fanout_size( g ) == 0 ) + { + dereference_node_rec( g ); + } + tmp_visited.push_back( g ); + } ); + } + + inline bool is_in_tfi( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + mark_visited_rec( root ); + bool contained = ntk.visited( n ) == ntk.trav_id(); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + void topo_sort_rec( choice_view& choice_ntk, node const& n ) + { + /* is permanently marked? */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + /* get the representative (smallest index) */ + node repr = choice_ntk.get_choice_representative( n ); + + /* multioutput gate */ + if ( choice_ntk.count_choices( repr ) > 1 ) + { + /* get the cut */ + uint32_t max_index = 0; + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited or temporarily marked */ + assert( ntk.visited( g ) != ntk.trav_id() ); + assert( ntk.visited( g ) != ntk.trav_id() - 1 ); + + /* mark node temporarily */ + ntk.set_visited( g, ntk.trav_id() - 1 ); + + max_index = std::max( max_index, ntk.node_to_index( g ) ); + return true; + } ); + + uint32_t cindex = node_match[max_index]; + auto& pair = ( cindex & 1 ) ? half_adders[cindex >> 1] : full_adders[cindex >> 1]; + cut_t const& cut = cuts.cuts( pair.first >> 16 )[pair.first & UINT16_MAX]; + + for ( auto l : cut ) + { + topo_sort_rec( choice_ntk, ntk.index_to_node( l ) ); + } + + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited */ + assert( ntk.visited( g ) != ntk.trav_id() ); + + /* mark node n permanently */ + ntk.set_visited( g, ntk.trav_id() ); + + /* visit node */ + topo_order.push_back( g ); + + return true; + } ); + } + else + { + /* ensure that the node is not visited or temporarily marked */ + assert( ntk.visited( n ) != ntk.trav_id() ); + assert( ntk.visited( n ) != ntk.trav_id() - 1 ); + + /* mark node temporarily */ + ntk.set_visited( n, ntk.trav_id() - 1 ); + + /* mark cut leaves */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + } ); + + /* ensure that the node is not visited */ + assert( ntk.visited( n ) != ntk.trav_id() ); + + /* mark node n permanently */ + ntk.set_visited( n, ntk.trav_id() ); + + /* visit node */ + topo_order.push_back( n ); + } + } + + std::pair initialize_map_network() + { + block_network dest; + block_map old2new( ntk ); + + old2new[ntk.get_node( ntk.get_constant( false ) )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + old2new[ntk.get_node( ntk.get_constant( true ) )] = dest.get_constant( true ); + + ntk.foreach_ci( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + void finalize( block_network& res, block_map& old2new ) + { + for ( auto const& n : topo_order ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + continue; + + /* is a multioutput gate root? */ + if ( node_match[ntk.node_to_index( n )] == UINT32_MAX ) + { + finalize_simple_gate( res, old2new, n ); + } + else if ( node_match[ntk.node_to_index( n )] < UINT32_MAX - 1 ) + { + finalize_multi_gate( res, old2new, n ); + } + } + + /* create POs */ + ntk.foreach_co( [&]( auto const& f ) { + res.create_po( ntk.is_complemented( f ) ? !old2new[f] : old2new[f] ); + } ); + } + + inline void finalize_simple_gate( block_network& res, block_map& old2new, node const& n ) + { + kitty::dynamic_truth_table tt = ntk.node_function( n ); + + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + auto s = old2new[f] ^ ntk.is_complemented( f ); + children.push_back( s ); + } ); + + old2new[n] = res.create_node( children, tt ); + } + + inline void finalize_multi_gate( block_network& res, block_map& old2new, node const& n ) + { + uint32_t index = node_match[ntk.node_to_index( n )]; + assert( index < UINT32_MAX - 1 ); + + /* extract the match */ + if ( index & 1 ) + finalize_multi_gate_ha( res, old2new, n, index >> 1 ); + else + finalize_multi_gate_fa( res, old2new, n, index >> 1 ); + } + + inline void finalize_multi_gate_ha( block_network& res, block_map& old2new, node const& n, uint32_t index ) + { + auto& pair = half_adders[index]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + uint32_t cut_index2 = pair.second & UINT16_MAX; + cut_t const& cut1 = cuts.cuts( index1 )[cut_index1]; + cut_t const& cut2 = cuts.cuts( index2 )[cut_index2]; + + kitty::static_truth_table<3> tt1 = cuts.truth_table( cut1 ); + kitty::static_truth_table<3> tt2 = cuts.truth_table( cut2 ); + bool xor_is_1 = false; + + /* find the XOR2 */ + xor_is_1 = cut1->data.is_xor; + + /* find the negation vector of AND2 and XOR2*/ + kitty::static_truth_table<3> tt = xor_is_1 ? tt2 : tt1; + uint32_t neg_and = 0; + for ( uint32_t func : and2func ) + { + if ( tt._bits == func ) + break; + ++neg_and; + } + + tt = xor_is_1 ? tt1 : tt2; + uint32_t neg_xor = 0; + for ( uint32_t func : xor2func ) + { + if ( tt._bits == func ) + break; + ++neg_xor; + } + neg_xor ^= neg_and; + neg_xor = ( neg_xor & 1 ) ^ ( ( neg_xor >> 1 ) & 1 ) ^ ( ( neg_xor >> 2 ) & 1 ); + + /* normalize and create multioutput gate */ + std::array, 2> children; + uint32_t ctr = 0; + for ( auto l : cut1 ) + { + signal f = old2new[ntk.index_to_node( l )]; + bool phase = ( ( neg_and >> ctr ) & 1 ) ? true : false; + children[ctr] = f ^ phase; + ++ctr; + } + + if ( ps.map_inverted ) + { + signal ha = res.create_hai( children[0], children[1] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = ha ^ ( ( neg_and >> 2 ) ? false : true ); + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( ha ) ^ ( neg_xor ? false : true ); + return; + } + + signal ha = res.create_ha( children[0], children[1] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = ha ^ ( ( neg_and >> 2 ) ? true : false ); + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( ha ) ^ ( neg_xor ? true : false ); + } + + inline void finalize_multi_gate_fa( block_network& res, block_map& old2new, node const& n, uint32_t index ) + { + auto& pair = full_adders[index]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + uint32_t cut_index2 = pair.second & UINT16_MAX; + cut_t const& cut1 = cuts.cuts( index1 )[cut_index1]; + cut_t const& cut2 = cuts.cuts( index2 )[cut_index2]; + + kitty::static_truth_table<3> tt1 = cuts.truth_table( cut1 ); + kitty::static_truth_table<3> tt2 = cuts.truth_table( cut2 ); + + bool xor_is_1 = false; + + /* find the XOR3 */ + xor_is_1 = cut1->data.is_xor; + + /* find the phase and permutation of MAJ3 and XOR3*/ + kitty::static_truth_table<3> tt = xor_is_1 ? tt2 : tt1; + uint32_t neg_maj = 0; + for ( uint32_t func : maj3func ) + { + if ( tt._bits == func ) + break; + ++neg_maj; + } + + if ( ps.map_inverted ) + { + neg_maj = ( ~neg_maj ) & 0x7; + } + + tt = xor_is_1 ? tt1 : tt2; + uint32_t neg_xor = 0; + for ( uint32_t func : xor3func ) + { + if ( tt._bits == func ) + break; + ++neg_xor; + } + neg_xor ^= neg_maj; + neg_xor = ( neg_xor & 1 ) ^ ( ( neg_xor >> 1 ) & 1 ) ^ ( ( neg_xor >> 2 ) & 1 ); + + /* normalize and create the multioutput gate */ + std::array, 3> children; + uint32_t ctr = 0; + for ( auto l : cut1 ) + { + signal f = old2new[ntk.index_to_node( l )]; + bool phase = ( ( neg_maj >> ctr ) & 1 ) ? true : false; + children[ctr] = f ^ phase; + ++ctr; + } + + if ( ps.map_inverted ) + { + signal fa = res.create_fai( children[0], children[1], children[2] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = fa; + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( fa ) ^ ( neg_xor ? false : true ); + return; + } + + signal fa = res.create_fa( children[0], children[1], children[2] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = fa; + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( fa ) ^ ( neg_xor ? true : false ); + } + +private: + Ntk& ntk; + extract_adders_params const& ps; + extract_adders_stats& st; + + network_cuts_t cuts; + leaves_hash_t cuts_classes; + matches_t half_adders; + matches_t full_adders; + std::vector selected; + std::vector node_match; + + std::vector> topo_order; + std::vector> tmp_visited; + + const std::array and2func = { 0x88, 0x44, 0x22, 0x11, 0x77, 0xbb, 0xdd, 0xee }; + const std::array maj3func = { 0xe8, 0xd4, 0xb2, 0x71, 0x8e, 0x4d, 0x2b, 0x17 }; + const std::array xor2func = { 0x66, 0x99 }; + const std::array xor3func = { 0x96, 0x69 }; +}; + +} /* namespace detail */ + +/*! \brief Adders extraction. + * + * This function extracts half and full adders from a network. + * It returns a `block_network` with extracted half and full adder + * blocks. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `foreach_gate` + * + * \param ntk Network + * \param ps Parameters + * \param pst Stats + * + */ +template +block_network extract_adders( Ntk& ntk, extract_adders_params const& ps = {}, extract_adders_stats* pst = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + + extract_adders_stats st; + + detail::extract_adders_impl p( ntk, ps, st ); + block_network res = p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + *pst = st; + + return res; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/extract_linear.hpp b/third-party/mockturtle/include/mockturtle/algorithms/extract_linear.hpp new file mode 100644 index 00000000000..72b261194de --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/extract_linear.hpp @@ -0,0 +1,208 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file extract_linear.hpp + \brief Extract linear subcircuit in XAGs + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../networks/xag.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Extract linear circuit from XAG + * + * Creates a new XAG that only contains the XOR gates of the original XAG. For + * each AND gate, the new XAG will contain one additional PI (for the AND + * output) and two additional POs (for the AND inputs) in the same order as the + * AND gates are traversed in topological order. + * + * Besides the new XAG, this function returns a vector of the size of all + * original AND gates with pointers to the signals referring to the AND's fanin + * and fanout (in that order). + */ +inline std::pair>> +extract_linear_circuit( xag_network const& xag ) +{ + xag_network dest; + std::vector> and_tuples; + node_map old_to_new( xag ); + + old_to_new[xag.get_constant( false )] = dest.get_constant( false ); + xag.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = dest.create_pi(); + } ); + + topo_view topo{ xag }; + topo.foreach_node( [&]( auto const& n ) { + if ( xag.is_constant( n ) || xag.is_pi( n ) ) + return; + + if ( xag.is_and( n ) ) + { + std::array signal_tuple; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + signal_tuple[i] = old_to_new[f] ^ xag.is_complemented( f ); + } ); + const auto and_pi = dest.create_pi(); + old_to_new[n] = and_pi; + signal_tuple[2] = and_pi; + and_tuples.push_back( signal_tuple ); + } + else /* if ( xag.is_xor( n ) ) */ + { + std::array children{}; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = old_to_new[f] ^ xag.is_complemented( f ); + } ); + old_to_new[n] = dest.create_xor( children[0], children[1] ); + } + } ); + + xag.foreach_po( [&]( auto const& f ) { + dest.create_po( old_to_new[f] ^ xag.is_complemented( f ) ); + } ); + for ( auto const& [a, b, _] : and_tuples ) + { + (void)_; + dest.create_po( a ); + dest.create_po( b ); + } + + return { dest, and_tuples }; +} + +namespace detail +{ + +struct merge_linear_circuit_impl +{ +public: + merge_linear_circuit_impl( xag_network const& xag, uint32_t num_and_gates ) + : xag( xag ), + num_and_gates( num_and_gates ), + old_to_new( xag ), + and_pi( xag ) + { + } + + xag_network run() + { + old_to_new[xag.get_constant( false )] = dest.get_constant( false ); + + orig_pis = xag.num_pis() - num_and_gates; + orig_pos = xag.num_pos() - 2 * num_and_gates; + + xag.foreach_pi( [&]( auto const& n, auto i ) { + if ( i == orig_pis ) + return false; + + old_to_new[n] = dest.create_pi(); + return true; + } ); + + for ( auto i = 0u; i < num_and_gates; ++i ) + { + create_and( i ); + } + + xag.foreach_po( [&]( auto const& f, auto i ) { + if ( i == orig_pos ) + return false; + + dest.create_po( run_rec( xag.get_node( f ) ) ^ xag.is_complemented( f ) ); + return true; + } ); + + return dest; + } + +private: + xag_network::signal create_and( uint32_t index ) + { + if ( old_to_new.has( xag.pi_at( orig_pis + index ) ) ) + { + return old_to_new[xag.pi_at( orig_pis + index )]; + } + + const auto f1 = xag.po_at( orig_pos + 2u * index ); + const auto f2 = xag.po_at( orig_pos + 2u * index + 1u ); + const auto c1 = run_rec( xag.get_node( f1 ) ) ^ xag.is_complemented( f1 ); + const auto c2 = run_rec( xag.get_node( f2 ) ) ^ xag.is_complemented( f2 ); + return old_to_new[xag.pi_at( orig_pis + index )] = dest.create_and( c1, c2 ); + } + + xag_network::signal run_rec( xag_network::node const& n ) + { + if ( old_to_new.has( n ) ) + { + return old_to_new[n]; + } + + assert( xag.is_xor( n ) ); + std::array children{}; + xag.foreach_fanin( n, [&]( auto const& cf, auto i ) { + children[i] = run_rec( xag.get_node( cf ) ) ^ xag.is_complemented( cf ); + } ); + return old_to_new[n] = dest.create_xor( children[0], children[1] ); + } + +private: + xag_network dest; + xag_network const& xag; + uint32_t num_and_gates; + uint32_t orig_pis, orig_pos; + unordered_node_map old_to_new; + unordered_node_map and_pi; +}; + +} // namespace detail + +/*! \brief Re-insert AND gates in linear circuit + * + * Given an extracted linear circuit from `extract_linear_circuit` and the + * number of original AND gates, this function re-inserts the AND gates, + * assuming that they are represented as PI and PO pairs at the end of the + * original PIs and POs. + */ +inline xag_network merge_linear_circuit( xag_network const& xag, uint32_t num_and_gates ) +{ + return detail::merge_linear_circuit_impl( xag, num_and_gates ).run(); +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/functional_reduction.hpp b/third-party/mockturtle/include/mockturtle/algorithms/functional_reduction.hpp new file mode 100644 index 00000000000..5649d780cdc --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/functional_reduction.hpp @@ -0,0 +1,488 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file functional_reduction.hpp + \brief Functional reduction for any network type + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" + +#include +#include + +#include "../io/write_patterns.hpp" +#include "circuit_validator.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +struct functional_reduction_params +{ + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Maximum number of iterations to run. 0 = repeat until no further improvement can be found. */ + uint32_t max_iterations{ 10 }; + + /*! \brief Whether to use pre-generated patterns stored in a file. + * If not, by default, 256 random patterns will be used. + */ + std::optional pattern_filename{}; + + /*! \brief Whether to save the appended patterns (with CEXs) into file. */ + std::optional save_patterns{}; + + /*! \brief Maximum number of nodes in the transitive fanin cone (and their fanouts) to be compared to. */ + uint32_t max_TFI_nodes{ 1000 }; + + /*! \brief Maximum fanout count of a node in the transitive fanin cone to explore its fanouts. */ + uint32_t skip_fanout_limit{ 100 }; + + /*! \brief Conflict limit for the SAT solver. */ + uint32_t conflict_limit{ 100 }; + + /*! \brief Maximum number of clauses of the SAT solver. (incremental CNF construction) */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Initial number of (random) simulation patterns. */ + uint32_t num_patterns{ 256 }; + + /*! \brief Maximum number of simulation patterns. Discards all patterns and re-seeds with random patterns when exceeded. */ + uint32_t max_patterns{ 1024 }; +}; + +struct functional_reduction_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Number of accepted constant nodes. */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted functionally equivalent nodes. */ + uint32_t num_equ_accepts{ 0 }; + + /*! \brief Number of counter-examples (SAT calls). */ + uint32_t num_cex{ 0 }; + + /*! \brief Number of successful node reductions (UNSAT calls). */ + uint32_t num_reduction{ 0 }; + + /*! \brief Number of SAT solver timeout. */ + uint32_t num_timeout{ 0 }; + + void report() const + { + // clang-format off + std::cout << "[i] Functional Reduction\n"; + std::cout << "[i] ======== Stats ========\n"; + std::cout << fmt::format( "[i] #constant = {:8d}\n", num_const_accepts ); + std::cout << fmt::format( "[i] #FE pairs = {:8d}\n", num_equ_accepts ); + std::cout << fmt::format( "[i] #SAT = {:8d}\n", num_cex ); + std::cout << fmt::format( "[i] #UNSAT = {:8d}\n", num_reduction ); + std::cout << fmt::format( "[i] #TIMEOUT = {:8d}\n", num_timeout ); + std::cout << "[i] ======== Runtime ========\n"; + std::cout << fmt::format( "[i] total : {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] simulation : {:>5.2f} secs\n", to_seconds( time_sim ) ); + std::cout << fmt::format( "[i] SAT solving: {:>5.2f} secs\n", to_seconds( time_sat ) ); + std::cout << "[i] =========================\n\n"; + // clang-format on + } +}; + +namespace detail +{ +template> +class functional_reduction_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using TT = unordered_node_map; + + explicit functional_reduction_impl( Ntk& ntk, functional_reduction_params const& ps, validator_params const& vps, functional_reduction_stats& st ) + : ntk( ntk ), ps( ps ), st( st ), tts( ntk ), + sim( ps.pattern_filename ? partial_simulator( *ps.pattern_filename ) : partial_simulator( ntk.num_pis(), ps.num_patterns, std::rand() ) ), validator( ntk, vps ) + { + static_assert( !validator_t::use_odc_, "`circuit_validator::use_odc` flag should be turned off." ); + } + + ~functional_reduction_impl() + { + if ( ps.save_patterns ) + { + write_patterns( sim, *ps.save_patterns ); + } + } + + void run() + { + stopwatch t( st.time_total ); + + /* first simulation: the whole circuit; from 0 bits. */ + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + + /* remove constant nodes. */ + substitute_constants(); + + /* substitute functional equivalent nodes. */ + auto size_before = ntk.size(); + substitute_equivalent_nodes(); + uint32_t iterations{0}; + while ( ps.max_iterations && iterations++ <= ps.max_iterations && ntk.size() != size_before ) + { + size_before = ntk.size(); + substitute_equivalent_nodes(); + } + } + +private: + void substitute_constants() + { + progress_bar pbar{ ntk.size(), "FR-const |{0}| node = {1:>4} cand = {2:>4}", ps.progress }; + + auto zero = sim.compute_constant( false ); + auto one = sim.compute_constant( true ); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i, candidates ); + + check_tts( n ); + bool const_value; + if ( tts[n] == zero ) + { + const_value = false; + } + else if ( tts[n] == one ) + { + const_value = true; + } + else /* not constant */ + { + return true; /* next */ + } + + /* update progress bar */ + candidates++; + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( n, const_value ); + } ); + if ( !res ) /* timeout */ + { + ++st.num_timeout; + return true; + } + else if ( !( *res ) ) /* SAT, cex found */ + { + found_cex(); + zero = sim.compute_constant( false ); + one = sim.compute_constant( true ); + } + else /* UNSAT, constant verified */ + { + ++st.num_reduction; + ++st.num_const_accepts; + /* update network */ + ntk.substitute_node( n, ntk.get_constant( const_value ) ); + } + return true; + } ); + } + + void substitute_equivalent_nodes() + { + progress_bar pbar{ ntk.size(), "FR-equ |{0}| node = {1:>4} cand = {2:>4}", ps.progress }; + ntk.foreach_gate( [&]( auto const& root, auto i ) { + pbar( i, i, candidates ); + + check_tts( root ); + auto tt = tts[root]; + auto ntt = ~tts[root]; + std::vector tfi; + bool keep_trying = true; + foreach_transitive_fanin( root, [&]( auto const& n ) { + tfi.emplace_back( n ); + if ( tfi.size() > ps.max_TFI_nodes ) + { + return false; + } + + keep_trying = try_node( tt, ntt, root, n ); + return keep_trying; + } ); + + if ( keep_trying ) /* didn't find a substitution in TFI cone, explore fanouts. */ + { + for ( auto j = 0u; j < tfi.size() && tfi.size() <= ps.max_TFI_nodes && keep_trying; ++j ) + { + auto& n = tfi.at( j ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit ) + { + continue; + } + + /* if the fanout has all fanins in the set, add it */ + ntk.foreach_fanout( n, [&]( node const& p ) { + if ( ntk.visited( p ) == ntk.trav_id() ) + { + return true; /* next fanout */ + } + + bool all_fanins_visited = true; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.visited( ntk.get_node( g ) ) != ntk.trav_id() ) + { + all_fanins_visited = false; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + if ( !all_fanins_visited ) + { + return true; /* next fanout */ + } + + bool has_root_as_child = false; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.get_node( g ) == root ) + { + has_root_as_child = true; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + if ( has_root_as_child ) + { + return true; /* next fanout */ + } + + tfi.emplace_back( p ); + ntk.set_visited( p, ntk.trav_id() ); + + check_tts( p ); + keep_trying = try_node( tt, ntt, root, p ); + return keep_trying; + } ); + } + } + + return true; /* next */ + } ); + } + + bool try_node( kitty::partial_truth_table& tt, kitty::partial_truth_table& ntt, node const& root, node const& n ) + { + signal g; + if ( tt == tts[n] ) + { + g = ntk.make_signal( n ); + } + else if ( ntt == tts[n] ) + { + g = !ntk.make_signal( n ); + } + else /* not equivalent */ + { + return true; /* try next transitive fanin node */ + } + + /* update progress bar */ + candidates++; + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( root, g ); + } ); + if ( !res ) /* timeout */ + { + ++st.num_timeout; + return true; /* try next transitive fanin node */ + } + else if ( !( *res ) ) /* SAT, cex found */ + { + found_cex(); + check_tts( root ); + tt = tts[root]; + ntt = ~tts[root]; + return true; /* try next transitive fanin node */ + } + else /* UNSAT, equivalent node verified */ + { + ++st.num_reduction; + ++st.num_equ_accepts; + /* update network */ + ntk.substitute_node( root, g ); + return false; /* break `foreach_transitive_fanin` */ + } + } + + void found_cex() + { + ++st.num_cex; + sim.add_pattern( validator.cex ); + + if ( sim.num_bits() > ps.max_patterns ) + { + reseed_patterns(); + return; + } + + /* re-simulate the whole circuit (for the last block) when a block is full */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + } + + void check_tts( node const& n ) + { + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + } + + void reseed_patterns() + { + sim = partial_simulator( ntk.num_pis(), ps.num_patterns, std::rand() ); + tts.reset(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + + template + void foreach_transitive_fanin( node const& n, Fn&& fn ) + { + ntk.incr_trav_id(); + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + return foreach_transitive_fanin_rec( ntk.get_node( f ), fn ); + } ); + } + + template + bool foreach_transitive_fanin_rec( node const& n, Fn&& fn ) + { + ntk.set_visited( n, ntk.trav_id() ); + if ( !fn( n ) ) + { + return false; + } + bool continue_loop = true; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + { + return true; + } /* skip visited node, continue looping. */ + + continue_loop = foreach_transitive_fanin_rec( ntk.get_node( f ), fn ); + return continue_loop; /* break `foreach_fanin` loop immediately when receiving `false`. */ + } ); + return continue_loop; /* return `false` only if `false` has ever been received from recursive calls. */ + } + +private: + Ntk& ntk; + functional_reduction_params const& ps; + functional_reduction_stats& st; + + TT tts; + partial_simulator sim; + validator_t validator; + + uint32_t candidates{ 0 }; +}; /* functional_reduction_impl */ + +} /* namespace detail */ + +/*! \brief Functional reduction. + * + * Removes constant nodes and substitute functionally equivalent nodes. + */ +template +void functional_reduction( Ntk& ntk, functional_reduction_params const& ps = {}, functional_reduction_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + validator_params vps; + vps.max_clauses = ps.max_clauses; + vps.conflict_limit = ps.conflict_limit; + + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + functional_reduction_stats st; + detail::functional_reduction_impl p( fanout_view, ps, vps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/gates_to_nodes.hpp b/third-party/mockturtle/include/mockturtle/algorithms/gates_to_nodes.hpp new file mode 100644 index 00000000000..6dc6541e5a0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/gates_to_nodes.hpp @@ -0,0 +1,174 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file gates_to_nodes.hpp + \brief Convert gate-based network into node-based network + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Translates a gate-based network into a node-based network. + * + * A node will be created in the node-based network for every gate based on the + * gate function. Possible complemented fanins are merged into the node + * function. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `foreach_pi` + * - `foreach_gate` + * - `foreach_fanin` + * - `get_constant` + * - `get_node` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `node_function` + * + * **Required network functions for return value (type NtkDest):** + * - `create_pi` + * - `create_po` + * - `create_node` + * - `create_not` + * - `get_constant` + * + * \param ntk Network + */ +template +NtkDest gates_to_nodes( NtkSource const& ntk ) +{ + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "NtkSource does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "NtkSource does not implement the foreach_fanin method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_node_function_v, "NtkSource does not implement the node_function method" ); + + NtkDest dest; + node_map, NtkSource> node_to_signal( ntk ); + + ntk.foreach_pi( [&]( auto const& n ) { + node_to_signal[n] = dest.create_pi(); + } ); + + node_to_signal[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_signal[ntk.get_constant( true )] = dest.get_constant( true ); + } + + ntk.foreach_gate( [&]( auto const& n ) { + std::vector> children; + auto func = ntk.node_function( n ); + ntk.foreach_fanin( n, [&]( auto const& c, auto i ) { + if ( ntk.is_complemented( c ) ) + { + kitty::flip_inplace( func, i ); + } + children.push_back( node_to_signal[c] ); + } ); + + node_to_signal[n] = dest.create_node( children, func ); + } ); + + /* outputs */ + ntk.foreach_po( [&]( auto const& s ) { + dest.create_po( ntk.is_complemented( s ) ? dest.create_not( node_to_signal[s] ) : node_to_signal[s] ); + } ); + + return dest; +} + +/*! \brief Creates a new network with a single node per output. + * + * This method can be applied to networks with a small number of primary inputs, + * to collapse all the logic of an output into a single node. The returning + * network must support arbitrary node functions, e.g., `klut_network`. + */ +template +NtkDest single_node_network( NtkSrc const& src ) +{ + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( has_num_pis_v, "NtkSrc does not implement the num_pis method" ); + + NtkDest ntk; + std::vector> pis( src.num_pis() ); + std::generate( pis.begin(), pis.end(), [&]() { return ntk.create_pi(); } ); + + default_simulator sim( src.num_pis() ); + const auto tts = simulate( src, sim ); + + for ( auto tt : tts ) + { + const auto support = kitty::min_base_inplace( tt ); + const auto small_tt = kitty::shrink_to( tt, static_cast( support.size() ) ); + std::vector> children( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + children[i] = pis[support[i]]; + } + ntk.create_po( ntk.create_node( children, small_tt ) ); + } + + return ntk; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/klut_to_graph.hpp b/third-party/mockturtle/include/mockturtle/algorithms/klut_to_graph.hpp new file mode 100644 index 00000000000..4f82385974e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/klut_to_graph.hpp @@ -0,0 +1,131 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file klut_to_graph.hpp + \brief Convert a k-LUT network into AIG, XAG, MIG or XMG. + + \author Andrea Costamagna +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../networks/xag.hpp" +#include "../networks/xmg.hpp" +#include "node_resynthesis.hpp" +#include "node_resynthesis/dsd.hpp" +#include "node_resynthesis/mig_npn.hpp" +#include "node_resynthesis/shannon.hpp" +#include "node_resynthesis/xag_npn.hpp" +#include "node_resynthesis/xmg_npn.hpp" + +namespace mockturtle +{ +namespace detail +{ + +/* declare the npn-resynthesis function to be used depending on the desired network type. */ +template +const auto set_npn_resynthesis_fn() +{ + using aig_npn_type = xag_npn_resynthesis; + using xag_npn_type = xag_npn_resynthesis; + using mig_npn_type = mig_npn_resynthesis; + using xmg_npn_type = xmg_npn_resynthesis; + + if constexpr ( std::is_same_v ) + return aig_npn_type{}; + else if constexpr ( std::is_same_v ) + return xag_npn_type{}; + else if constexpr ( std::is_same_v ) + return mig_npn_type{}; + else if constexpr ( std::is_same_v ) + return xmg_npn_type{}; +} + +} // namespace detail + +/*! \brief Convert a k-LUT network into AIG, XAG, MIG or XMG (out-of-place) + * + * This function is a wrapper function for resynthesizing a k-LUT network (type `NtkSrc`) into a + * new graph (of type `NtkDest`). The new data structure can be of type AIG, XAG, MIG or XMG. + * First the function attempts a Disjoint Support Decomposition (DSD), branching the network into subnetworks. + * As soon as DSD can no longer be done, there are two possibilities depending on the dimensionality of the + * subnetwork to be resynthesized. On the one hand, if the size of the associated support is lower or equal + * than 4, the solution can be recovered by exploiting the mapping of the subnetwork to its NPN-class. + * On the other hand, if the support size is higher than 4, A Shannon decomposition is performed, branching + * the network in further subnetworks with reduced support. + * Finally, once the threshold value of 4 is reached, the NPN mapping completes the graph definition. + * + * \tparam NtkDest Type of the destination network. Its base type should be either `aig_network`, `xag_network`, `mig_network`, or `xmg_network`. + * \tparam NtkSrc Type of the source network. Its base type should be `klut_network`. + * \param ntk_src Input k-lut network + * \return An equivalent AIG, XAG, MIG or XMG network + */ +template +NtkDest convert_klut_to_graph( NtkSrc const& ntk_src ) +{ + using NtkDestBase = typename NtkDest::base_type; + static_assert( std::is_same_v, "NtkSrc is not klut_network" ); + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, + "NtkDest is not an AIG, XAG, MIG, or XMG" ); + + uint32_t threshold{ 4 }; + auto fallback_npn = detail::set_npn_resynthesis_fn(); + shannon_resynthesis fallback_shannon( threshold, &fallback_npn ); + dsd_resynthesis resyn( fallback_shannon ); + return node_resynthesis( ntk_src, resyn ); +} + +/*! \brief Convert a k-LUT network into AIG, XAG, MIG or XMG (in-place) + * + * The algorithmic details are the same as the out-of-place version. + * + * \tparam NtkDest Type of the destination network. Its base type should be either `aig_network`, `xag_network`, `mig_network`, or `xmg_network`. + * \tparam NtkSrc Type of the source network. Its base type should be `klut_network`. + * \param ntk_dest An empty AIG, XAG, MIG or XMG network to be constructed in-place + * \param ntk_src Input k-lut network + */ +template +void convert_klut_to_graph( NtkDest& ntk_dest, NtkSrc const& ntk_src ) +{ + using NtkDestBase = typename NtkDest::base_type; + static_assert( std::is_same_v, "NtkSrc is not klut_network" ); + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, + "NtkDest is not an AIG, XAG, MIG, or XMG" ); + + uint32_t threshold{ 4 }; + auto fallback_npn = detail::set_npn_resynthesis_fn(); + shannon_resynthesis fallback_shannon( threshold, &fallback_npn ); + dsd_resynthesis resyn( fallback_shannon ); + node_resynthesis( ntk_dest, ntk_src, resyn ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/linear_resynthesis.hpp b/third-party/mockturtle/include/mockturtle/algorithms/linear_resynthesis.hpp new file mode 100644 index 00000000000..a0e08dea831 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/linear_resynthesis.hpp @@ -0,0 +1,1008 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file linear_resynthesis.hpp + \brief Resynthesize linear circuit + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../algorithms/cnf.hpp" +#include "../algorithms/simulation.hpp" +#include "../networks/xag.hpp" +#include "../traits.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cnf_view.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +class linear_sum_simulator +{ +public: + std::vector compute_constant( bool ) const { return {}; } + std::vector compute_pi( uint32_t index ) const { return { index }; } + std::vector compute_not( std::vector const& value ) const + { + assert( false && "No NOTs in linear forms allowed" ); + std::abort(); + return value; + } +}; + +class linear_matrix_simulator +{ +public: + linear_matrix_simulator( uint32_t num_inputs ) : num_inputs_( num_inputs ) {} + + std::vector compute_constant( bool ) const { return std::vector( num_inputs_, false ); } + std::vector compute_pi( uint32_t index ) const + { + std::vector row( num_inputs_, false ); + row[index] = true; + return row; + } + std::vector compute_not( std::vector const& value ) const + { + assert( false && "No NOTs in linear forms allowed" ); + std::abort(); + return value; + } + +private: + uint32_t num_inputs_; +}; + +class linear_xag : public xag_network +{ +public: + linear_xag( xag_network const& xag ) : xag_network( xag ) {} + + template + iterates_over_t> + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_pi( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto set1 = *begin++; + auto set2 = *begin++; + + if ( c1.index < c2.index ) + { + assert( false ); + std::abort(); + return {}; + } + else + { + std::vector result; + auto it1 = set1.begin(); + auto it2 = set2.begin(); + + while ( it1 != set1.end() && it2 != set2.end() ) + { + if ( *it1 < *it2 ) + { + result.push_back( *it1++ ); + } + else if ( *it1 > *it2 ) + { + result.push_back( *it2++ ); + } + else + { + ++it1; + ++it2; + } + } + + if ( it1 != set1.end() ) + { + std::copy( it1, set1.end(), std::back_inserter( result ) ); + } + else if ( it2 != set2.end() ) + { + std::copy( it2, set2.end(), std::back_inserter( result ) ); + } + + return result; + } + } + + template + iterates_over_t> + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_pi( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto set1 = *begin++; + auto set2 = *begin++; + + if ( c1.index < c2.index ) + { + assert( false ); + std::abort(); + return {}; + } + else + { + std::vector result( set1.size() ); + std::transform( set1.begin(), set1.end(), set2.begin(), result.begin(), std::not_equal_to{} ); + return result; + } + } +}; + +struct pair_hash +{ + template + std::size_t operator()( std::pair const& p ) const + { + return std::hash()( p.first ) ^ std::hash()( p.second ); + } +}; + +template +struct linear_resynthesis_paar_impl +{ +public: + using index_pair_t = std::pair; + + linear_resynthesis_paar_impl( Ntk const& xag ) : xag( xag ) {} + + Ntk run() + { + xag.foreach_pi( [&]( auto const& ) { + signals.push_back( dest.create_pi() ); + } ); + + extract_linear_equations(); + + while ( !occurrence_to_pairs.empty() ) + { + const auto p = *( occurrence_to_pairs.back().begin() ); + replace_one_pair( p ); + } + + xag.foreach_po( [&]( auto const& f, auto i ) { + if ( linear_equations[i].empty() ) + { + dest.create_po( dest.get_constant( xag.is_complemented( f ) ) ); + } + else + { + assert( linear_equations[i].size() == 1u ); + dest.create_po( signals[linear_equations[i].front()] ^ xag.is_complemented( f ) ); + } + } ); + + return dest; + } + +private: + void extract_linear_equations() + { + occurrence_to_pairs.resize( 1u ); + + linear_xag lxag{ xag }; + linear_equations = simulate>( lxag, linear_sum_simulator{} ); + + for ( auto o = 0u; o < linear_equations.size(); ++o ) + { + const auto& lin_eq = linear_equations[o]; + for ( auto j = 1u; j < lin_eq.size(); ++j ) + { + for ( auto i = 0u; i < j; ++i ) + { + const auto p = std::make_pair( lin_eq[i], lin_eq[j] ); + pairs_to_output[p].push_back( o ); + add_pair( p ); + } + } + } + } + + void add_pair( index_pair_t const& p ) + { + if ( auto it = pair_to_occurrence.find( p ); it != pair_to_occurrence.end() ) + { + // found another time + const auto occ = it->second; + occurrence_to_pairs[occ - 1u].erase( p ); + if ( occurrence_to_pairs.size() <= occ + 1u ) + { + occurrence_to_pairs.resize( occ + 1u ); + } + occurrence_to_pairs[occ].insert( p ); + it->second++; + } + else + { + // first time found + pair_to_occurrence[p] = 1u; + occurrence_to_pairs[0u].insert( p ); + } + } + + void remove_all_pairs( index_pair_t const& p ) + { + auto it = pair_to_occurrence.find( p ); + const auto occ = it->second; + pair_to_occurrence.erase( it ); + occurrence_to_pairs[occ - 1u].erase( p ); + while ( !occurrence_to_pairs.empty() && occurrence_to_pairs.back().empty() ) + { + occurrence_to_pairs.pop_back(); + } + pairs_to_output.erase( p ); + } + + void remove_one_pair( index_pair_t const& p, uint32_t output ) + { + auto it = pair_to_occurrence.find( p ); + const auto occ = it->second; + occurrence_to_pairs[occ - 1u].erase( p ); + if ( occ > 1u ) + { + occurrence_to_pairs[occ - 2u].insert( p ); + } + it->second--; + pairs_to_output[p].erase( std::remove( pairs_to_output[p].begin(), pairs_to_output[p].end(), output ), pairs_to_output[p].end() ); + } + + void replace_one_pair( index_pair_t const& p ) + { + const auto [a, b] = p; + auto c = static_cast( signals.size() ); + signals.push_back( dest.create_xor( signals[a], signals[b] ) ); + + /* update data structures */ + for ( auto o : pairs_to_output[p] ) + { + auto& leq = linear_equations[o]; + leq.erase( std::remove( leq.begin(), leq.end(), a ), leq.end() ); + leq.erase( std::remove( leq.begin(), leq.end(), b ), leq.end() ); + for ( auto i : leq ) + { + remove_one_pair( { std::min( i, a ), std::max( i, a ) }, o ); + remove_one_pair( { std::min( i, b ), std::max( i, b ) }, o ); + add_pair( { i, c } ); + pairs_to_output[{ i, c }].push_back( o ); + } + leq.push_back( c ); + } + remove_all_pairs( p ); + } + + void print_linear_matrix() + { + for ( auto const& le : linear_equations ) + { + auto it = le.begin(); + for ( auto i = 0u; i < signals.size(); ++i ) + { + if ( it != le.end() && *it == i ) + { + std::cout << " 1"; + it++; + } + else + { + std::cout << " 0"; + } + } + assert( it == le.end() ); + std::cout << "\n"; + } + } + +private: + Ntk const& xag; + Ntk dest; + std::vector> signals; + std::vector> linear_equations; + std::vector> occurrence_to_pairs; + std::unordered_map pair_to_occurrence; + std::unordered_map, pair_hash> pairs_to_output; +}; + +} // namespace detail + +/*! \brief Linear circuit resynthesis (Paar's algorithm) + * + * This algorithm works on an XAG that is only composed of XOR gates. It + * extracts a matrix representation of the linear output equations and + * resynthesizes them in a greedy manner by always substituting the most + * frequent pair of variables using the computed function of an XOR gate. + * + * Reference: [C. Paar, IEEE Int'l Symp. on Inf. Theo. (1997), page 250] + */ +template +Ntk linear_resynthesis_paar( Ntk const& xag ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + return detail::linear_resynthesis_paar_impl( xag ).run(); +} + +struct exact_linear_synthesis_params +{ + /*! \brief Upper bound on number of XOR gates. If used, best solution is found decreasing */ + std::optional upper_bound{}; + + /*! \brief Conflict limit for SAT solving (default 0 = no limit). */ + int conflict_limit{ 0 }; + + /*! \brief Solution must be cancellation-free. */ + bool cancellation_free{ false }; + + /*! \brief Ignore inputs in any step to compute this output. + * + * Either the vector is empty, if no inputs should be ignored, or it has as + * many entries as rows in the input matrix. Each entry is a vector of input + * indexes (starting from 0) to be ignored, an entry can be the empty vector, + * if no inputs should be ignored for some output. + */ + std::vector> ignore_inputs; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose (debug messages). */ + bool very_verbose{ false }; +}; + +struct exact_linear_synthesis_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_solving{ 0 }; + + /*! \brief Prints report. */ + void report() const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] solving time = {:>5.2f} secs\n", to_seconds( time_solving ) ); + } +}; + +namespace detail +{ + +template +struct exact_linear_synthesis_problem_network +{ + using problem_network_t = cnf_view; + + exact_linear_synthesis_problem_network( uint32_t num_steps, std::vector> const& linear_matrix, std::vector> const& ignore_inputs, std::vector> const& trivial_pos, exact_linear_synthesis_params const& ps ) + : linear_matrix_( linear_matrix ), + k_( num_steps ), + n_( static_cast( linear_matrix.front().size() ) ), + m_( static_cast( linear_matrix.size() ) ), + bs_( k_ * n_ ), + cs_( ( ( k_ - 1 ) * k_ ) / 2 ), + fs_( k_ * m_ ), + psis_( k_ * n_ ), + phis_( k_ * n_ ), + ignore_inputs_( ignore_inputs ), + trivial_pos_( trivial_pos ), + ps_( ps ) + { + std::generate( bs_.begin(), bs_.end(), [&]() { return pntk_.create_pi(); } ); + std::generate( cs_.begin(), cs_.end(), [&]() { return pntk_.create_pi(); } ); + std::generate( fs_.begin(), fs_.end(), [&]() { return pntk_.create_pi(); } ); + + ensure_row_size2(); + ensure_connectivity(); + ensure_outputs(); + } + + std::optional solve() + { + return pntk_.solve( ps_.conflict_limit ); + } + + template + Ntk extract_solution() + { + Ntk ntk; + + std::vector> nodes( n_ ); + std::generate( nodes.begin(), nodes.end(), [&]() { return ntk.create_pi(); } ); + + for ( auto i = 0u; i < k_; ++i ) + { + std::array, 2> children; + auto it = children.begin(); + for ( auto j = 0u; j < n_ + i; ++j ) + { + if ( pntk_.model_value( b_or_c( i, j ) ) ) + { + *it++ = nodes[j]; + } + } + nodes.push_back( ntk.create_xor( children[0], children[1] ) ); + } + + auto it = trivial_pos_.begin(); + auto poctr = 0u; + for ( auto l = 0u; l < m_; ++l ) + { + while ( it != trivial_pos_.end() && it->first == poctr ) + { + ntk.create_po( it->second == n_ ? ntk.get_constant( false ) : nodes[it->second] ); + poctr++; + ++it; + } + + for ( auto i = 0u; i < k_; ++i ) + { + if ( pntk_.model_value( f( l, i ) ) ) + { + ntk.create_po( nodes[n_ + i] ); + poctr++; + break; + } + } + } + + /* maybe some trivial POs are still left. */ + while ( it != trivial_pos_.end() && it->first == poctr ) + { + ntk.create_po( it->second == n_ ? ntk.get_constant( false ) : nodes[it->second] ); + poctr++; + ++it; + } + + return ntk; + } + + void debug_solution() + { + for ( auto i = 0u; i < k_; ++i ) + { + fmt::print( i == 0 ? "B =" : " " ); + for ( auto j = 0u; j < n_; ++j ) + { + fmt::print( " {}", (int)pntk_.model_value( b( i, j ) ) ); + } + fmt::print( i == 0 ? " C =" : " " ); + for ( auto p = 0u; p < i; ++p ) + { + fmt::print( " {}", (int)pntk_.model_value( c( i, p ) ) ); + } + fmt::print( std::string( 2 * ( k_ - i ), ' ' ) ); + fmt::print( i == 0u ? " F =" : " " ); + for ( auto l = 0u; l < m_; ++l ) + { + fmt::print( " {}", (int)pntk_.model_value( f( l, i ) ) ); + } + fmt::print( "\n" ); + } + } + +private: + void ensure_row_size2() + { + for ( auto i = 0u; i < k_; ++i ) + { + /* at least 2 */ + for ( auto cpl = 0u; cpl <= n_ + i; ++cpl ) + { + std::vector> lits( n_ + i ); + for ( auto j = 0u; j < n_ + i; ++j ) + { + lits[j] = b_or_c( i, j ) ^ ( cpl == j ); + } + pntk_.add_clause( lits ); + } + + /* at most 2 */ + for ( auto j = 2u; j < n_ + i; ++j ) + { + for ( auto jj = 1u; jj < j; ++jj ) + { + for ( auto jjj = 0u; jjj < jj; ++jjj ) + { + pntk_.add_clause( !b_or_c( i, j ), !b_or_c( i, jj ), !b_or_c( i, jjj ) ); + } + } + } + } + } + + void ensure_connectivity() + { + // psi function + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto j = 0u; j < n_; ++j ) + { + std::vector> xors( 1 + i ); + auto it = xors.begin(); + *it++ = b( i, j ); + for ( auto p = 0u; p < i; ++p ) + { + *it++ = pntk_.create_and( c( i, p ), psi( j, p ) ); + } + psi( j, i ) = pntk_.create_nary_xor( xors ); + } + } + + for ( auto l = 0u; l < m_; ++l ) + { + for ( auto i = 0u; i < k_; ++i ) + { + std::vector> ands( n_ ); + for ( auto j = 0u; j < n_; ++j ) + { + ands[j] = pntk_.create_xnor( psi( j, i ), pntk_.get_constant( linear_matrix_[l][j] ) ); + } + pntk_.add_clause( !f( l, i ), pntk_.create_nary_and( ands ) ); + } + } + + // No two steps are the same + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto p = 0u; p < i; ++p ) + { + std::vector> ors( n_ ); + for ( auto j = 0u; j < n_; ++j ) + { + ors[j] = pntk_.create_xor( psi( j, p ), psi( j, i ) ); + } + pntk_.add_clause( ors ); + } + } + + if ( !ignore_inputs_.empty() || ps_.cancellation_free ) + { + // phi function + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto j = 0u; j < n_; ++j ) + { + std::vector> ors( 1 + i ); + auto it = ors.begin(); + *it++ = b( i, j ); + for ( auto p = 0u; p < i; ++p ) + { + *it++ = pntk_.create_and( c( i, p ), phi( j, p ) ); + } + phi( j, i ) = pntk_.create_nary_or( ors ); + } + } + + // cancellation-free + if ( ps_.cancellation_free ) + { + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto j = 0u; j < n_; ++j ) + { + pntk_.add_clause( !psi( j, i ), phi( j, i ) ); + pntk_.add_clause( psi( j, i ), !phi( j, i ) ); + } + } + } + } + + // ignored inputs + if ( !ignore_inputs_.empty() ) + { + for ( auto l = 0u; l < m_; ++l ) + { + for ( auto j : ignore_inputs_[l] ) + { + for ( auto i = 0u; i < k_; ++i ) + { + pntk_.add_clause( !f( l, i ), !phi( j, i ) ); + } + } + } + } + + // at least 2 inputs in each compute form + for ( auto i = 0u; i < k_; ++i ) + { + /* at least 2 */ + for ( auto cpl = 0u; cpl <= n_; ++cpl ) + { + std::vector> lits( n_ ); + for ( auto j = 0u; j < n_; ++j ) + { + lits[j] = psi( j, i ) ^ ( cpl == j ); + } + pntk_.add_clause( lits ); + } + } + } + + void ensure_outputs() + { + // each output covers at least one row + for ( auto l = 0u; l < m_; ++l ) + { + std::vector> lits( k_ ); + for ( auto i = 0u; i < k_; ++i ) + { + lits[i] = f( l, i ); + for ( auto ii = i + 1; ii < k_; ++ii ) + { + pntk_.add_clause( !f( l, i ), !f( l, ii ) ); + } + } + pntk_.add_clause( lits ); + } + + // at most one output (if no duplicates) per row + // for ( auto i = 0u; i < k_; ++i ) + //{ + // for ( auto l = 1u; l < m_; ++l ) + // { + // for ( auto ll = 0u; ll < l; ++ll ) + // { + // pntk_.add_clause( !f( l, i ), !f( ll, i ) ); + // } + // } + //} + } + + // 0 <= i <= k - 1 + // 0 <= j <= n - 1 + const signal& b( uint32_t i, uint32_t j ) const + { + return bs_[i * n_ + j]; + } + + // 0 <= i <= k - 1 + // 0 <= p <= i - 1 + const signal& c( uint32_t i, uint32_t p ) const + { + return cs_[( ( ( i - 1 ) * i ) / 2 ) + p]; + } + + // 0 <= i <= k - 1 + // 0 <= j <= n + i - 1 + const signal& b_or_c( uint32_t i, uint32_t j ) const + { + return j < n_ ? b( i, j ) : c( i, j - n_ ); + } + + // 0 <= l <= m - 1 + // 0 <= i <= k - 1 + const signal& f( uint32_t l, uint32_t i ) const + { + return fs_[l * k_ + i]; + } + + // 0 <= j <= n - 1 + // 0 <= i <= k - 1 + signal& psi( uint32_t j, uint32_t i ) + { + return psis_[i * n_ + j]; + } + + // 0 <= j <= n - 1 + // 0 <= i <= k - 1 + signal& phi( uint32_t j, uint32_t i ) + { + return phis_[i * n_ + j]; + } + +private: + std::vector> const& linear_matrix_; + uint32_t k_; + uint32_t n_; + uint32_t m_; + + std::vector> bs_; + std::vector> cs_; + std::vector> fs_; + std::vector> psis_; + std::vector> phis_; + + problem_network_t pntk_; + std::vector> const& ignore_inputs_; + std::vector> const& trivial_pos_; + exact_linear_synthesis_params const& ps_; +}; + +template +struct exact_linear_synthesis_impl +{ + exact_linear_synthesis_impl( std::vector> const& linear_matrix, exact_linear_synthesis_params const& ps, exact_linear_synthesis_stats& st ) + : ps_( ps ), + st_( st ) + { + if ( ps_.very_verbose ) + { + fmt::print( "[i] input matrix =\n" ); + debug_matrix( linear_matrix ); + } + + /* check whether ignore inputs matches matrix size */ + if ( !ps_.ignore_inputs.empty() && ps_.ignore_inputs.size() != linear_matrix.size() ) + { + fmt::print( "[e] size of ignored inputs vector must match number of rows in linear matrix" ); + std::abort(); + } + + /* check matrix for trivial entries */ + for ( auto j = 0u; j < linear_matrix.size(); ++j ) + { + const auto& row = linear_matrix[j]; + n_ = static_cast( row.size() ); + + auto cnt = 0u; + auto idx = 0u; + for ( auto i = 0u; i < row.size(); ++i ) + { + if ( row[i] ) + { + idx = i; + if ( ++cnt == 2u ) + { + break; + } + } + } + if ( cnt == 0u ) + { + /* constant 0 is encoded as input n */ + trivial_pos_.emplace_back( j, n_ ); + } + else if ( cnt == 1u ) + { + trivial_pos_.emplace_back( j, idx ); + } + else + { + linear_matrix_.push_back( row ); + if ( !ps_.ignore_inputs.empty() ) + { + ignore_inputs_.push_back( ps_.ignore_inputs[j] ); + } + } + } + + m_ = static_cast( linear_matrix_.size() ); + + if ( ps_.very_verbose ) + { + fmt::print( "[i] problem matrix =\n" ); + debug_matrix( linear_matrix_ ); + fmt::print( "\n[i] trivial POs =\n" ); + for ( auto const& [j, i] : trivial_pos_ ) + { + if ( i == n_ ) + { + fmt::print( "f{} = 0\n", j ); + } + else + { + fmt::print( "f{} = x{}\n", j, i ); + } + } + if ( !ignore_inputs_.empty() ) + { + fmt::print( "\n[i] ignored inputs =\n" ); + for ( auto j = 0u; j < ignore_inputs_.size(); ++j ) + { + fmt::print( "for f{} ignore {{{}}}\n", j, fmt::join( ignore_inputs_[j], ", " ) ); + } + } + } + } + + std::optional run() + { + if ( m_ == 0u ) + { + Ntk ntk; + + std::vector> nodes( n_ ); + std::generate( nodes.begin(), nodes.end(), [&]() { return ntk.create_pi(); } ); + + for ( auto po : trivial_pos_ ) + { + ntk.create_po( po.second == n_ ? ntk.get_constant( false ) : nodes[po.second] ); + } + + return ntk; + } + + return ps_.upper_bound ? run_decreasing() : run_increasing(); + } + +private: + std::optional run_increasing() + { + auto k_ = m_; + while ( true ) + { + if ( ps_.verbose ) + { + fmt::print( "[i] try to find a solution with {} steps, solving time so far = {:.2f} secs\n", k_, to_seconds( st_.time_solving ) ); + } + + exact_linear_synthesis_problem_network pntk( k_, linear_matrix_, ignore_inputs_, trivial_pos_, ps_ ); + const auto res = call_with_stopwatch( st_.time_solving, [&]() { return pntk.solve(); } ); + if ( res && *res ) + { + if ( ps_.very_verbose ) + { + pntk.debug_solution(); + } + return pntk.template extract_solution(); + } + ++k_; + } + } + + std::optional run_decreasing() + { + std::optional best{}; + auto k_ = *ps_.upper_bound; + while ( true ) + { + if ( ps_.verbose ) + { + fmt::print( "[i] try to find a solution with {} steps, solving time so far = {:.2f} secs\n", k_, to_seconds( st_.time_solving ) ); + } + exact_linear_synthesis_problem_network pntk( k_, linear_matrix_, ignore_inputs_, trivial_pos_, ps_ ); + const auto res = call_with_stopwatch( st_.time_solving, [&]() { return pntk.solve(); } ); + if ( res && *res ) + { + if ( ps_.very_verbose ) + { + pntk.debug_solution(); + } + best = pntk.template extract_solution(); + --k_; + } + else /* if unsat or timeout */ + { + return best; + } + } + } + +private: + void debug_matrix( std::vector> const& matrix ) const + { + for ( auto const& row : matrix ) + { + for ( auto b : row ) + { + fmt::print( "{}", b ? '1' : '0' ); + } + fmt::print( "\n" ); + } + } + +private: + uint32_t n_{}; + uint32_t m_{ 0u }; + std::vector> linear_matrix_; + std::vector> trivial_pos_; + std::vector> ignore_inputs_; + exact_linear_synthesis_params const& ps_; + exact_linear_synthesis_stats& st_; +}; + +} // namespace detail + +/*! \brief Extracts linear matrix from XOR-based XAG + * + * This algorithm can be used to extract the linear matrix represented by an + * XAG that only contains XOR gates and no inverters at the outputs. The matrix + * can be passed as an argument to `exact_linear_synthesis`. + */ +template +std::vector> get_linear_matrix( Ntk const& ntk ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + detail::linear_matrix_simulator sim( ntk.num_pis() ); + return simulate>( detail::linear_xag{ ntk }, sim ); +} + +/*! \brief Optimum linear circuit synthesis (based on SAT) + * + * This algorithm creates an XAG that is only composed of XOR gates. It is + * given as input a linear matrix, represented as vector of bool-vectors. The + * size of the outer vector corresponds to the number of outputs, the size of + * each inner vector must be the same and corresponds to the number of inputs. + * + * Reference: [C. Fuhs and P. Schneider-Kamp, SAT (2010), page 71-84] + */ +template +std::optional exact_linear_synthesis( std::vector> const& linear_matrix, exact_linear_synthesis_params const& ps = {}, exact_linear_synthesis_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + exact_linear_synthesis_stats st; + const auto xag = detail::exact_linear_synthesis_impl{ linear_matrix, ps, st }.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + return xag; +} + +/*! \brief Optimum linear circuit resynthesis (based on SAT) + * + * This algorithm extracts the linear matrix from an XAG that only contains of + * XOR gates and no inversions and returns a new XAG that has the optimum number + * of XOR gates to represent the same function. + * + * Reference: [C. Fuhs and P. Schneider-Kamp, SAT (2010), page 71-84] + */ +template +std::optional exact_linear_resynthesis( Ntk const& ntk, exact_linear_synthesis_params const& ps = {}, exact_linear_synthesis_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + const auto linear_matrix = get_linear_matrix( ntk ); + return exact_linear_synthesis( linear_matrix, ps, pst ); +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/lut_mapper.hpp b/third-party/mockturtle/include/mockturtle/algorithms/lut_mapper.hpp new file mode 100644 index 00000000000..57659041d33 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/lut_mapper.hpp @@ -0,0 +1,3015 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file lut_mapper.hpp + \brief LUT mapper + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../networks/klut.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/cuts.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/truth_table_cache.hpp" +#include "../views/choice_view.hpp" +#include "../views/mapping_view.hpp" +#include "../views/mffc_view.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "collapse_mapped.hpp" +#include "cut_enumeration.hpp" +#include "exorcism.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for map. + * + * The data structure `map_params` holds configurable parameters + * with default arguments for `map`. + */ +struct lut_map_params +{ + lut_map_params() + { + cut_enumeration_ps.cut_size = 6u; + cut_enumeration_ps.cut_limit = 8u; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 8. The maximum value + * is 16. The maxiumum cut size is 16. By default, + * truth table minimization is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Do area-oriented mapping. */ + bool area_oriented_mapping{ false }; + + /*! \brief Required depth for depth relaxation. */ + uint32_t required_delay{ 0u }; + + /*! \brief Required depth relaxation ratio (%). */ + uint32_t relax_required{ 0u }; + + /*! \brief Recompute cuts at each step. */ + bool recompute_cuts{ true }; + + /*! \brief Number of rounds for area sharing optimization. */ + uint32_t area_share_rounds{ 2u }; + + /*! \brief Number of rounds for area flow optimization. */ + uint32_t area_flow_rounds{ 1u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t ela_rounds{ 2u }; + + /*! \brief Use edge count reduction. */ + bool edge_optimization{ true }; + + /*! \brief Try to expand the cuts. */ + bool cut_expansion{ true }; + + /*! \brief Remove the cuts that are contained in others */ + bool remove_dominated_cuts{ true }; + + /*! \brief Maps by collapsing MFFCs */ + bool collapse_mffcs{ false }; + + /*! \brief Depth optimization by balancing ISOPs */ + bool sop_balancing{ false }; + + /*! \brief Depth optimization by balancing ESOPs */ + bool esop_balancing{ false }; + + /*! \brief Maximum number variables for cost function caching */ + uint32_t cost_cache_vars{ 3u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for mapper. + * + * The data structure `map_stats` provides data collected by running + * `map`. + */ +struct lut_map_stats +{ + /*! \brief Area result. */ + uint32_t area{ 0 }; + /*! \brief Worst delay result. */ + uint32_t delay{ 0 }; + /*! \brief Edge result. */ + uint32_t edges{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Cut enumeration stats. */ + cut_enumeration_stats cut_enumeration_st{}; + + /*! \brief Depth and size stats for each round. */ + std::vector round_stats{}; + + void report() const + { + for ( auto const& stat : round_stats ) + { + std::cout << stat; + } + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +#pragma region cut set +/* cut data */ +struct cut_enumeration_lut_cut +{ + uint32_t delay{ 0 }; + uint32_t lut_area{ 0 }; + uint32_t lut_delay{ 0 }; + float area_flow{ 0 }; + float edge_flow{ 0 }; + bool ignore{ false }; +}; + +enum class lut_cut_sort_type +{ + DELAY, + DELAY2, + AREA, + AREA2, + NONE +}; + +template +class lut_cut_set +{ +public: + /*! \brief Standard constructor. + */ + lut_cut_set() + { + clear(); + } + + /*! \brief Assignment operator. + */ + lut_cut_set& operator=( lut_cut_set const& other ) + { + if ( this != &other ) + { + _pcend = _pend = _pcuts.begin(); + + auto it = other.begin(); + while ( it != other.end() ) + { + **_pend++ = **it++; + ++_pcend; + } + } + + return *this; + } + + /*! \brief Clears a cut set. + */ + void clear() + { + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } + } + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ) + { + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; + } + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const + { + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; + } + + static bool sort_delay( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + return c1->data.edge_flow < c2->data.edge_flow - eps; + } + + static bool sort_delay2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + if ( c1->data.edge_flow < c2->data.edge_flow - eps ) + return true; + if ( c1->data.edge_flow > c2->data.edge_flow + eps ) + return false; + return c1.size() < c2.size(); + } + + static bool sort_area( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); + } + + static bool sort_area2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + if ( c1->data.edge_flow < c2->data.edge_flow - eps ) + return true; + if ( c1->data.edge_flow > c2->data.edge_flow + eps ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + return c1->data.delay < c2->data.delay; + } + + /*! \brief Compare two cuts using sorting functions. + * + * This method compares two cuts using a sorting function. + * + * \param cut1 first cut. + * \param cut2 second cut. + * \param sort sorting function. + */ + static bool compare( CutType const& cut1, CutType const& cut2, lut_cut_sort_type sort = lut_cut_sort_type::NONE ) + { + if ( sort == lut_cut_sort_type::DELAY ) + { + return sort_delay( cut1, cut2 ); + } + else if ( sort == lut_cut_sort_type::DELAY2 ) + { + return sort_delay2( cut1, cut2 ); + } + else if ( sort == lut_cut_sort_type::AREA ) + { + return sort_area( cut1, cut2 ); + } + else if ( sort == lut_cut_sort_type::AREA2 ) + { + return sort_area2( cut1, cut2 ); + } + else + { + return false; + } + } + + /*! \brief Inserts a cut into a set without checking dominance. + * + * This method will insert a cut into a set and maintain an order. This + * method doesn't remove the cuts that are dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param sort Cut prioritization function. + */ + void simple_insert( CutType const& cut, lut_cut_sort_type sort = lut_cut_sort_type::NONE ) + { + /* insert cut in a sorted way */ + typename std::array::iterator ipos = _pcuts.begin(); + + if ( sort == lut_cut_sort_type::DELAY ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_delay( *a, *b ); } ); + } + else if ( sort == lut_cut_sort_type::DELAY2 ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_delay2( *a, *b ); } ); + } + else if ( sort == lut_cut_sort_type::AREA ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area( *a, *b ); } ); + } + else if ( sort == lut_cut_sort_type::AREA2 ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area2( *a, *b ); } ); + } + else /* NONE */ + { + ipos == _pend; + } + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; + } + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. Variable `skip0` tell to skip the dominance check on + * cut zero. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param skip0 Skip dominance check on cut zero. + * \param sort Cut prioritization function. + */ + void insert( CutType const& cut, bool skip0 = false, lut_cut_sort_type sort = lut_cut_sort_type::NONE ) + { + auto begin = _pcuts.begin(); + + if ( skip0 && _pend != _pcuts.begin() ) + ++begin; + + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( begin, _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + simple_insert( cut, sort ); + } + + /*! \brief Replaces a cut of the set. + * + * This method replaces the cut at position `index` in the set by `cut` + * and maintains the cuts order. The function does not check whether + * index is in the valid range. + * + * \param index Index of the cut to replace. + * \param cut Cut to insert. + */ + void replace( uint32_t index, CutType const& cut ) + { + *_pcuts[index] = cut; + } + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ) + { + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; + } + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ) + { + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } + } + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, lut_cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; +}; +#pragma endregion + +#pragma region LUT mapper +struct node_lut +{ + /* required time at node output */ + uint32_t required; + /* number of references in the cover */ + uint32_t map_refs; + /* references estimation */ + float est_refs; +}; + +template +class lut_map_impl +{ +private: + /* special map for output drivers to perform some optimizations */ + enum class driver_type : uint32_t + { + none = 0, + pos = 1, + neg = 2, + mixed = 3 + }; + +public: + static constexpr uint32_t max_cut_num = 32; + static constexpr uint32_t max_cut_size = 16; + static constexpr uint32_t max_cubes = 64; + static constexpr uint32_t max_sop_decomp_size = max_cut_size * ( max_cubes + 1 ); + using cut_t = cut>; + using cut_set_t = lut_cut_set; + using node = typename Ntk::node; + using cut_merge_t = typename std::array; + using TT = kitty::dynamic_truth_table; + using tt_cache = truth_table_cache; + using cost_cache = std::unordered_map>; + using sop_t = std::vector; + using isop_cache = std::vector; + using cubes_queue_t = std::priority_queue, std::greater>; + using lut_info = std::pair>>; + +public: + explicit lut_map_impl( Ntk& ntk, lut_map_params const& ps, lut_map_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + cuts( ntk.size() ) + { + assert( ps.cut_enumeration_ps.cut_limit < max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + + if constexpr ( StoreFunction ) + { + TT zero( 0u ), proj( 1u ); + kitty::create_nth_var( proj, 0u ); + + tmp_visited.reserve( 100 ); + truth_tables.resize( 32768 ); + + truth_tables.insert( zero ); + truth_tables.insert( proj ); + + /* reserve cost cache */ + if constexpr ( !std::is_same::value ) + { + truth_tables_cost.reserve( 1000 ); + } + + /* reserve ISOP cache */ + if ( ps.sop_balancing || ps.esop_balancing ) + { + isops.reserve( 32768 ); + isops.emplace_back(); /* empty ISOP for constant */ + isops.push_back( { kitty::cube{ 1, 1 } } ); /* ISOP for variable */ + } + } + } + + klut_network run() + { + stopwatch t( st.time_total ); + + /* compute and save topological order */ + topo_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + + perform_mapping(); + return create_lut_network(); + } + + void run_inplace() + { + stopwatch t( st.time_total ); + + /* compute and save topological order */ + topo_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + + if ( ps.collapse_mffcs ) + { + compute_mffcs_mapping(); + return; + } + + perform_mapping(); + derive_mapping(); + } + +private: + void perform_mapping() + { + /* define area sorting function */ + lut_cut_sort_type area_sort = ( ps.area_oriented_mapping && !ps.edge_optimization ) ? lut_cut_sort_type::AREA : lut_cut_sort_type::AREA2; + + /* init the data structure */ + init_nodes(); + init_cuts(); + + /* compute mapping for depth or area */ + if ( !ps.area_oriented_mapping ) + { + compute_required_time(); + + if ( ps.recompute_cuts ) + { + compute_mapping( lut_cut_sort_type::DELAY, true, true ); + compute_required_time(); + compute_mapping( lut_cut_sort_type::DELAY2, true, true ); + compute_required_time(); + compute_mapping( area_sort, true, true ); + } + else + { + compute_mapping( lut_cut_sort_type::DELAY2, true, true ); + } + } + else + { + compute_required_time(); + compute_mapping( area_sort, false, true ); + } + + if ( ps.cut_expansion ) + { + compute_required_time(); + expand_cuts(); + } + + /* try backward area iterations */ + uint32_t i = 0; + while ( i < ps.area_share_rounds ) + { + compute_share_mapping( area_sort, i == 0 ); + + if ( ps.cut_expansion ) + { + expand_cuts(); + } + ++i; + } + + /* compute mapping using global area flow */ + i = 0; + while ( i < ps.area_flow_rounds ) + { + compute_required_time(); + compute_mapping( area_sort, false, ps.recompute_cuts ); + + if ( ps.cut_expansion ) + { + compute_required_time(); + expand_cuts(); + } + ++i; + } + + /* compute mapping using exact area/edge */ + i = 0; + while ( i < ps.ela_rounds ) + { + compute_required_time(); + compute_mapping( area_sort, false, ps.recompute_cuts ); + + if ( ps.cut_expansion ) + { + compute_required_time(); + expand_cuts(); + } + ++i; + } + } + + void init_nodes() + { + ntk.foreach_node( [this]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.map_refs = ntk.fanout_size( n ); + node_data.est_refs = static_cast( ntk.fanout_size( n ) ); + } ); + } + + void init_cuts() + { + /* init constant cut */ + add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) ), false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( true ) ) ), true ); + + /* init PIs cuts */ + ntk.foreach_ci( [&]( auto const& n ) { + add_unit_cut( ntk.node_to_index( n ) ); + } ); + } + + template + void compute_mapping( lut_cut_sort_type const sort, bool preprocess, bool recompute_cuts ) + { + cuts_total = 0; + for ( auto const& n : topo_order ) + { + if constexpr ( !ELA ) + { + auto const index = ntk.node_to_index( n ); + if ( !preprocess && iteration != 0 ) + { + node_match[index].est_refs = ( 2.0 * node_match[index].est_refs + 1.0 * node_match[index].map_refs ) / 3.0; + } + else + { + node_match[index].est_refs = static_cast( node_match[index].map_refs ); + } + } + + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + continue; + } + + if ( recompute_cuts ) + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + compute_best_cut2( n, sort, preprocess ); + } + else + { + compute_best_cut( n, sort, preprocess ); + } + } + else + { + /* update cost the function and move the best one first */ + update_cut_data( n, sort ); + } + } + + set_mapping_refs(); + + if constexpr ( DO_AREA ) + { + ++area_iteration; + } + + /* round stats */ + { + std::stringstream stats; + + if ( ( sort == lut_cut_sort_type::AREA || sort == lut_cut_sort_type::AREA2 ) && ELA ) + { + stats << fmt::format( "[i] Area : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + else if ( sort == lut_cut_sort_type::AREA || sort == lut_cut_sort_type::AREA2 ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + else if ( sort == lut_cut_sort_type::DELAY2 ) + { + stats << fmt::format( "[i] Delay2 : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + st.round_stats.push_back( stats.str() ); + } + } + + void compute_share_mapping( lut_cut_sort_type const sort, bool first ) + { + /* reset required times and references except for POs */ + compute_share_mapping_init( first ); + + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + auto const index = ntk.node_to_index( *it ); + + /* skip not used nodes */ + if ( node_match[index].map_refs == 0 ) + continue; + + /* update cost the function and move the best one first */ + update_cut_data_share( *it, sort ); + } + + /* propagate correct arrival times and compute stats */ + propagate_arrival_times(); + + /* round stats */ + { + st.round_stats.push_back( fmt::format( "[i] AreaSh : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ) ); + } + } + + template + void expand_cuts() + { + /* cut expansion is not yet compatible with truth table computation */ + if constexpr ( StoreFunction ) + return; + + /* don't expand if cut recomputed cuts is off */ + if ( !ps.recompute_cuts ) + return; + + for ( auto const& n : topo_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + continue; + } + + expand_cuts_node( n ); + } + + set_mapping_refs(); + + std::string stats = fmt::format( "[i] Reduce : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + st.round_stats.push_back( stats ); + } + + template + void set_mapping_refs() + { + if constexpr ( !ELA ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].map_refs = 0u; + } + } + + /* compute the current worst delay and update the mapping refs */ + delay = 0; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + delay = std::max( delay, cuts[index][0]->data.delay ); + + if constexpr ( !ELA ) + { + ++node_match[index].map_refs; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0; + edges = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) || ntk.is_ci( *it ) ) + { + continue; + } + + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* continue if not referenced in the cover */ + if ( node_match[index].map_refs == 0u ) + continue; + + auto& best_cut = cuts[index][0]; + + if constexpr ( !ELA ) + { + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs++; + } + } + area += best_cut->data.lut_area; + edges += best_cut.size(); + } + + ++iteration; + } + + void compute_required_time() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required = UINT32_MAX >> 1; + } + + /* return in case of area_oriented_mapping */ + if ( iteration == 0 || ps.area_oriented_mapping ) + return; + + uint32_t required = delay; + + /* relax delay constraints */ + if ( ps.required_delay == 0.0f && ps.relax_required > 0.0f ) + { + required *= ( 100.0 + ps.relax_required ) / 100.0; + } + + if ( ps.required_delay != 0 ) + { + /* Global target time constraint */ + if ( ps.required_delay < delay ) + { + if ( !ps.area_oriented_mapping && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {}", ps.required_delay ) << std::endl; + } + else + { + required = ps.required_delay; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + node_match[index].required = required; + } ); + + /* propagate required time to the PIs */ + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_ci( *it ) || ntk.is_constant( *it ) ) + continue; + + const auto index = ntk.node_to_index( *it ); + + if ( node_match[index].map_refs == 0 ) + continue; + + /* in case of decomposition cost */ + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + compute_balancing_cost_required( index ); + continue; + } + } + + for ( auto leaf : cuts[index][0] ) + { + node_match[leaf].required = std::min( node_match[leaf].required, node_match[index].required - cuts[index][0]->data.lut_delay ); + } + } + } + + void propagate_arrival_times() + { + area = 0; + edges = 0; + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + { + continue; + } + + /* propagate arrival time */ + uint32_t node_delay = 0; + auto& best_cut = cuts[index].best(); + + for ( auto leaf : best_cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + node_delay = std::max( node_delay, best_leaf_cut->data.delay ); + } + + best_cut->data.delay = node_delay + best_cut->data.lut_delay; + + /* continue if not referenced in the cover */ + if ( node_match[index].map_refs == 0u ) + continue; + + /* update stats */ + area += best_cut->data.lut_area; + edges += best_cut.size(); + } + + /* update worst delay */ + delay = 0; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + delay = std::max( delay, cuts[index][0]->data.delay ); + } ); + } + + void compute_share_mapping_init( bool first ) + { + /* reset the mapping references and the required time */ + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required = UINT32_MAX >> 1; + if ( !first ) + node_match[i].est_refs = ( 2.0 * node_match[i].est_refs + 1.0 * node_match[i].map_refs ) / 3.0; + else + node_match[i].est_refs = std::max( 1.0, ( 1.0 * node_match[i].est_refs + 2.0 * node_match[i].map_refs ) / 3.0 ); + node_match[i].map_refs = 0; + + /* update flows if in area-oriented mapping */ + if ( ps.area_oriented_mapping ) + compute_cut_data( cuts[i].best(), ntk.index_to_node( i ), false ); + } + + uint32_t required = delay; + if ( ps.required_delay == 0.0f && ps.relax_required > 0.0f ) + { + required *= ( 100.0 + ps.relax_required ) / 100.0; + } + + if ( ps.required_delay != 0 ) + { + /* Global target time constraint */ + if ( ps.required_delay < delay ) + { + if ( !ps.area_oriented_mapping && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {}", ps.required_delay ) << std::endl; + } + else + { + required = ps.required_delay; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + node_match[index].required = required; + node_match[index].map_refs++; + } ); + } + + template + void compute_best_cut2( node const& n, lut_cut_sort_type const sort, bool preprocess ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + cut_t best_cut; + + /* compute cuts */ + const auto fanin = 2; + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_deref( rcuts[0] ); + } + } + + /* recompute the data of the best cut */ + if ( iteration != 0 ) + { + best_cut = rcuts[0]; + compute_cut_data( best_cut, n, true ); + } + + /* clear cuts */ + rcuts.clear(); + + /* insert the previous best cut */ + if ( iteration != 0 && !preprocess ) + { + rcuts.simple_insert( best_cut, sort ); + } + + cut_t new_cut; + std::vector vcuts( fanin ); + + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, ps.cut_enumeration_ps.cut_size ) ) + { + continue; + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( StoreFunction ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + compute_cut_data( new_cut, ntk.index_to_node( index ), true ); + + /* check required time */ + if constexpr ( DO_AREA ) + { + if ( preprocess || new_cut->data.delay <= node_data.required ) + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + else + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* replace the new best cut with previous one */ + if ( preprocess && rcuts[0]->data.delay > node_data.required ) + rcuts.replace( 0, best_cut ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_ref( rcuts[0] ); + } + } + } + + template + void compute_best_cut( node const& n, lut_cut_sort_type const sort, bool preprocess ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + cut_t best_cut; + + /* compute cuts */ + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_deref( rcuts[0] ); + } + } + + /* recompute the data of the best cut */ + if ( iteration != 0 ) + { + best_cut = rcuts[0]; + compute_cut_data( best_cut, n, true ); + } + + /* clear cuts */ + rcuts.clear(); + + /* insert the previous best cut */ + if ( iteration != 0 && !preprocess ) + { + rcuts.simple_insert( best_cut, sort ); + } + + if ( fanin > 1 && fanin <= ps.cut_enumeration_ps.fanin_limit ) + { + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, ps.cut_enumeration_ps.cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, ps.cut_enumeration_ps.cut_size ) ) + { + return true; /* continue */ + } + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( StoreFunction ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + compute_cut_data( new_cut, index, true ); + + /* check required time */ + if constexpr ( DO_AREA ) + { + if ( preprocess || new_cut->data.delay <= node_data.required ) + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + else + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + else if ( fanin == 1 ) + { + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( StoreFunction ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + compute_cut_data( new_cut, index, true ); + + if constexpr ( DO_AREA ) + { + if ( preprocess || new_cut->data.delay <= node_data.required ) + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + else + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + + cuts_total += rcuts.size(); + + /* replace the new best cut with previous one */ + if ( preprocess && rcuts[0]->data.delay > node_data.required ) + rcuts.replace( 0, best_cut ); + + add_unit_cut( index ); + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_ref( rcuts[0] ); + } + } + } + + template + void update_cut_data( node const& n, lut_cut_sort_type const sort ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto& node_cut_set = cuts[index]; + uint32_t best_cut_index = 0; + uint32_t cut_index = 0; + + cut_t const* best_cut = &node_cut_set.best(); + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_deref( *best_cut ); + } + } + + /* recompute the data for all the cuts and pick the best */ + for ( cut_t* cut : node_cut_set ) + { + /* skip trivial cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ++cut_index; + continue; + } + + compute_cut_data( *cut, n, false ); + + /* update best */ + if constexpr ( DO_AREA ) + { + if ( ( *cut )->data.delay <= node_data.required ) + { + if ( node_cut_set.compare( *cut, *best_cut, sort ) ) + { + best_cut = cut; + best_cut_index = cut_index; + } + } + } + else + { + if ( node_cut_set.compare( *cut, *best_cut, sort ) ) + { + best_cut = cut; + best_cut_index = cut_index; + } + } + + ++cut_index; + } + + if constexpr ( DO_AREA || ELA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_ref( *best_cut ); + } + } + + /* update the best cut */ + node_cut_set.update_best( best_cut_index ); + } + + void update_cut_data_share( node const& n, lut_cut_sort_type const sort ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto& node_cut_set = cuts[index]; + uint32_t best_cut_index = 0; + uint32_t cut_index = 0; + + cut_t const* best_cut = &node_cut_set.best(); + + /* recompute the data for all the cuts and pick the best */ + for ( cut_t* cut : node_cut_set ) + { + /* skip trivial cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ++cut_index; + continue; + } + + compute_cut_data_share( *cut ); + + /* update best */ + if ( ( *cut )->data.delay <= node_data.required ) + { + if ( node_cut_set.compare( *cut, *best_cut, sort ) ) + { + best_cut = cut; + best_cut_index = cut_index; + } + } + + ++cut_index; + } + + /* propagate required times backward and reference the leaves */ + for ( auto leaf : *best_cut ) + { + node_match[leaf].required = std::min( node_match[leaf].required, node_data.required - ( *best_cut )->data.lut_delay ); + node_match[leaf].map_refs++; + } + + /* update the best cut */ + node_cut_set.update_best( best_cut_index ); + } + + void expand_cuts_node( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + cut_t best_cut = cuts[index][0]; + + if ( node_data.map_refs == 0 ) + return; + + /* update delay */ + uint32_t delay_update = 0; + for ( auto const leaf : best_cut ) + { + delay_update = std::max( delay_update, cuts[leaf][0]->data.delay + best_cut->data.lut_delay ); + } + best_cut->data.delay = delay_update; + + auto const area_before = cut_deref( best_cut ); + + uint32_t cost_before = 0; + + std::vector leaves; + + /* mark volume */ + ntk.incr_trav_id(); + for ( auto const leaf : best_cut ) + { + ntk.set_visited( ntk.index_to_node( leaf ), ntk.trav_id() ); + leaves.push_back( leaf ); + + /* MFFC leaves */ + if ( node_match[leaf].map_refs == 0 ) + ++cost_before; + } + mark_cut_volume_rec( n ); + + /* improve cut */ + while ( improve_cut( leaves ) ) + ; + + /* measure improvement */ + uint32_t cost_after = 0; + for ( auto const leaf : leaves ) + { + /* MFFC leaves */ + if ( node_match[leaf].map_refs == 0 ) + ++cost_after; + } + + assert( cost_after <= cost_before ); + + /* create the new cut */ + cut_t new_cut; + new_cut.set_leaves( leaves.begin(), leaves.end() ); + new_cut->data = best_cut->data; + + uint32_t delay_after = 0; + for ( auto const leaf : leaves ) + { + delay_after = std::max( delay_after, cuts[leaf][0]->data.delay + new_cut->data.lut_delay ); + } + new_cut->data.delay = delay_after; + + auto const area_after = cut_ref( new_cut ); + + /* new cut is better */ + if ( area_after <= area_before && new_cut->data.delay <= node_data.required ) + { + cuts[index].replace( 0, new_cut ); + } + else + { + /* restore */ + cut_deref( new_cut ); + cut_ref( best_cut ); + } + } + + bool improve_cut( std::vector& leaves ) + { + if ( improve_cut_expand0( leaves ) ) + return true; + + if ( leaves.size() < ps.cut_enumeration_ps.cut_size && improve_cut_expand1( leaves ) ) + return true; + + assert( leaves.size() <= ps.cut_enumeration_ps.cut_size ); + return false; + } + + bool improve_cut_expand0( std::vector& leaves ) + { + for ( auto it = leaves.begin(); it != leaves.end(); ++it ) + { + if ( ntk.is_ci( *it ) ) + continue; + + /* test if expansion would increase the number of leaves */ + int marked = 0; + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + ++marked; + } ); + + if ( marked > 1 ) + continue; + + /* check that the cost does not increase */ + marked = 0; + if ( node_match[*it].map_refs == 0 ) + --marked; + + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + return; + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() && node_match[index].map_refs == 0 ) + ++marked; + } ); + + /* not referenced leaves don't increase from the transformation */ + if ( marked <= 0 ) + { + /* update leaves */ + uint32_t n = *it; + leaves.erase( it ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + { + leaves.push_back( index ); + ntk.set_visited( ntk.get_node( f ), ntk.trav_id() ); + } + } ); + return true; + } + } + + return false; + } + + bool improve_cut_expand1( std::vector& leaves ) + { + for ( auto it = leaves.begin(); it != leaves.end(); ++it ) + { + if ( ntk.is_ci( *it ) ) + continue; + + /* test if expansion would increase the number of leaves by more than 1*/ + int marked = 0; + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + ++marked; + } ); + + if ( marked > 2 ) + continue; + + /* check that the cost reduces */ + marked = 0; + if ( node_match[*it].map_refs == 0 ) + --marked; + + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + return; + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() && node_match[index].map_refs == 0 ) + ++marked; + } ); + + /* not referenced leaves should be reduced by the transformation */ + if ( marked < 0 ) + { + /* update leaves */ + uint32_t n = *it; + leaves.erase( it ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + { + leaves.push_back( index ); + ntk.set_visited( ntk.get_node( f ), ntk.trav_id() ); + } + } ); + return true; + } + } + + return false; + } + + uint32_t cut_ref( cut_t const& cut ) + { + uint32_t count = cut->data.lut_area; + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs++ == 0u ) + { + count += cut_ref( cuts[leaf][0] ); + } + } + + return count; + } + + uint32_t cut_deref( cut_t const& cut ) + { + uint32_t count = cut->data.lut_area; + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( --node_match[leaf].map_refs == 0u ) + { + count += cut_deref( cuts[leaf][0] ); + } + } + + return count; + } + + uint32_t cut_measure_mffc( cut_t const& cut ) + { + tmp_visited.clear(); + + uint32_t count = cut_ref_visit( cut ); + + /* dereference visited */ + for ( auto const& s : tmp_visited ) + { + --node_match[s].map_refs; + } + + return count; + } + + uint32_t cut_ref_visit( cut_t const& cut ) + { + uint32_t count = cut->data.lut_area; + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* add to visited */ + tmp_visited.push_back( leaf ); + + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs++ == 0u ) + { + count += cut_ref_visit( cuts[leaf][0] ); + } + } + + return count; + } + + uint32_t cut_edge_ref( cut_t const& cut ) + { + uint32_t count = cut.size(); + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs++ == 0u ) + { + count += cut_edge_ref( cuts[leaf][0] ); + } + } + return count; + } + + uint32_t cut_edge_deref( cut_t const& cut ) + { + uint32_t count = cut.size(); + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( --node_match[leaf].map_refs == 0u ) + { + count += cut_edge_deref( cuts[leaf][0] ); + } + } + return count; + } + + void mark_cut_volume_rec( node const& n ) + { + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + mark_cut_volume_rec( ntk.get_node( f ) ); + } ); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + void compute_truth_table_support( cut_t const& sub, cut_t const& sup, TT& tt ) + { + std::vector support( sub.size() ); + + size_t j = 0; + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support[j++] = static_cast( std::distance( sup.begin(), itp ) ); + } + + /* swap variables in the truth table */ + for ( int i = j - 1; i >= 0; --i ) + { + assert( i <= support[i] ); + kitty::swap_inplace( tt, i, support[i] ); + } + } + + template + void compute_cut_data( cut_t& cut, node const& n, bool recompute_cut_cost ) + { + uint32_t lut_area = 0; + uint32_t lut_delay = 0; + + if ( recompute_cut_cost ) + { + cut->data.ignore = false; + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + compute_isop( cut ); + } + else + { + if constexpr ( !std::is_same::value ) + { + if ( auto it = truth_tables_cost.find( cut->func_id ); it != truth_tables_cost.end() ) + { + std::tie( lut_area, lut_delay ) = it->second; + } + else + { + auto cost = lut_cost( truth_tables[cut->func_id] ); + if ( truth_tables[cut->func_id].num_vars() <= ps.cost_cache_vars ) + { + /* cache it */ + truth_tables_cost[cut->func_id] = cost; + } + lut_area = cost.first; + lut_delay = cost.second; + } + } + else + { + std::tie( lut_area, lut_delay ) = lut_cost( truth_tables[cut->func_id] ); + } + } + } + else + { + std::tie( lut_area, lut_delay ) = lut_cost( cut.size() ); + } + } + else + { + lut_area = cut->data.lut_area; + lut_delay = cut->data.lut_delay; + + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + /* reset fields to be recomputed */ + cut->data.lut_area = 0; + cut->data.lut_delay = 0; + } + } + } + + if constexpr ( ELA ) + { + uint32_t delay{ 0 }; + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + } + + cut->data.delay = lut_delay + delay; + cut->data.lut_area = lut_area; + cut->data.lut_delay = lut_delay; + if ( ps.edge_optimization ) + { + cut->data.area_flow = static_cast( cut_ref( cut ) ); + cut->data.edge_flow = static_cast( cut_edge_deref( cut ) ); + } + else + { + cut->data.area_flow = static_cast( cut_measure_mffc( cut ) ); + cut->data.edge_flow = 0; + } + } + else + { + uint32_t delay{ 0 }; + + float area_flow = static_cast( lut_area ); + float edge_flow = cut.size(); + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + if ( node_match[leaf].map_refs > 0 && leaf != 0 ) + { + area_flow += best_leaf_cut->data.area_flow / node_match[leaf].est_refs; + edge_flow += best_leaf_cut->data.edge_flow / node_match[leaf].est_refs; + } + else + { + area_flow += best_leaf_cut->data.area_flow; + edge_flow += best_leaf_cut->data.edge_flow; + } + } + + cut->data.delay = lut_delay + delay; + cut->data.lut_area = lut_area; + cut->data.lut_delay = lut_delay; + cut->data.area_flow = area_flow; + cut->data.edge_flow = edge_flow; + } + + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + /* compute delay and area */ + compute_balancing_cost( cut ); + } + } + } + + void compute_cut_data_share( cut_t& cut ) + { + uint32_t delay{ 0 }; + float area_flow = static_cast( cut->data.lut_area ); + float edge_flow = cut.size(); + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + /* flow contribution is added only for not shared leaves */ + if ( node_match[leaf].map_refs == 0 && leaf != 0 ) + { + area_flow += best_leaf_cut->data.area_flow / node_match[leaf].est_refs; + edge_flow += best_leaf_cut->data.edge_flow / node_match[leaf].est_refs; + } + } + + cut->data.delay = cut->data.lut_delay + delay; + cut->data.area_flow = area_flow; + cut->data.edge_flow = edge_flow; + } + + void add_zero_cut( uint32_t index, bool phase ) + { + auto& cut = cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( StoreFunction ) + { + if ( phase ) + cut->func_id = 1; + else + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( StoreFunction ) + { + cut->func_id = 2; + } + } + + inline bool fast_support_minimization( TT& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + tt = kitty::shrink_to( tt, support_size ); + } + + return true; + } + + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + // stopwatch t( st.cut_enumeration_st.time_truth_table ); /* runtime optimized */ + + std::vector tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = kitty::extend_to( truth_tables[( *cut )->func_id], res.size() ); + compute_truth_table_support( *cut, res, tt[i] ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.cut_enumeration_ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + auto tt_res_shrink = shrink_to( tt_res, static_cast( support.size() ) ); + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + return truth_tables.insert( tt_res_shrink ); + } + } + + return truth_tables.insert( tt_res ); + } + + void compute_mffcs_mapping() + { + ntk.clear_mapping(); + + /* map POs */ + ntk.foreach_co( [&]( auto const& f ) { + node const& n = ntk.get_node( f ); + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + return; + + compute_mffc_mapping_node( n ); + } ); + + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + node const& n = *it; + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + if ( ntk.fanout_size( n ) <= 1 ) /* it should be unnecessary */ + continue; + + /* create MFFC cut */ + compute_mffc_mapping_node( n ); + } + + st.area = area; + st.delay = delay; + st.edges = edges; + + { + std::stringstream stats; + stats << fmt::format( "[i] Area MFFC: Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + st.round_stats.push_back( stats.str() ); + } + } + + void compute_mffc_mapping_node( node const& n ) + { + uint32_t lut_area, lut_delay; + + /* create FC cut */ + std::vector inner, leaves; + ntk.incr_trav_id(); + get_fc_nodes_rec( n, inner ); + + /* extract leaves */ + for ( auto const& g : inner ) + { + ntk.foreach_fanin( g, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() && !ntk.is_constant( ntk.get_node( f ) ) ) + { + leaves.push_back( ntk.get_node( f ) ); + ntk.set_visited( ntk.get_node( f ), ntk.trav_id() ); + } + } ); + } + + /* sort leaves in topo order */ + std::stable_sort( leaves.begin(), leaves.end() ); + + ntk.add_to_mapping( n, leaves.begin(), leaves.end() ); + + delay = std::max( delay, static_cast( leaves.size() ) ); + + if constexpr ( StoreFunction ) + { + default_simulator sim( leaves.size() ); + unordered_node_map node_to_value( ntk ); + + /* populate simulation values for constants */ + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + + /* populate simulation values for leaves */ + uint32_t i = 0u; + for ( auto const& g : leaves ) + { + node_to_value[g] = sim.compute_pi( i++ ); + } + + /* simulate recursively */ + simulate_fc_rec( n, node_to_value ); + + ntk.set_cell_function( n, node_to_value[n] ); + + std::tie( lut_area, lut_delay ) = lut_cost( node_to_value[n] ); + } + else + { + std::tie( lut_area, lut_delay ) = lut_cost( leaves.size() ); + } + + area += lut_area; + } + + void get_fc_nodes_rec( node const& n, std::vector& nodes ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + return; + + nodes.push_back( n ); + ntk.set_visited( n, ntk.trav_id() ); + + /* expand cut for single fanout nodes */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto g = ntk.get_node( f ); + if ( ntk.fanout_size( g ) == 1 ) + { + get_fc_nodes_rec( g, nodes ); + } + } ); + } + + void simulate_fc_rec( node const& n, unordered_node_map& node_to_value ) + { + std::vector fanin_values( ntk.fanin_size( n ) ); + + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fc_rec( ntk.get_node( f ), node_to_value ); + } + + fanin_values[i] = node_to_value[ntk.get_node( f )]; + } ); + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + +#pragma region Dump network + klut_network create_lut_network() + { + /* specialized method: does not support buffer/inverter sweeping */ + if ( StoreFunction && ps.cut_enumeration_ps.minimize_truth_table ) + { + return create_lut_network_mapped(); + } + + klut_network res; + node_map, Ntk> node_to_signal( ntk ); + + node_map node_driver_type( ntk, driver_type::none ); + + /* opposites are filled for nodes with mixed driver types, since they have + two nodes in the network. */ + std::unordered_map> opposites; + + /* initial driver types */ + ntk.foreach_co( [&]( auto const& f ) { + switch ( node_driver_type[f] ) + { + case driver_type::none: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::pos; + break; + case driver_type::pos: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::mixed : driver_type::pos; + break; + case driver_type::neg: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::mixed; + break; + case driver_type::mixed: + default: + break; + } + } ); + + /* constants */ + auto add_constant_to_map = [&]( bool value ) { + const auto n = ntk.get_node( ntk.get_constant( value ) ); + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = res.get_constant( value ); + break; + + case driver_type::neg: + node_to_signal[n] = res.get_constant( !value ); + break; + + case driver_type::mixed: + node_to_signal[n] = res.get_constant( value ); + opposites[n] = res.get_constant( !value ); + break; + } + }; + + add_constant_to_map( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + add_constant_to_map( true ); + } + + /* primary inputs */ + ntk.foreach_pi( [&]( auto n ) { + signal res_signal; + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + res_signal = res.create_pi(); + node_to_signal[n] = res_signal; + break; + + case driver_type::neg: + res_signal = res.create_pi(); + node_to_signal[n] = res.create_not( res_signal ); + break; + + case driver_type::mixed: + res_signal = res.create_pi(); + node_to_signal[n] = res_signal; + opposites[n] = res.create_not( node_to_signal[n] ); + break; + } + } ); + + /* TODO: add sequential compatibility */ + edges = 0; + for ( auto const& n : topo_order ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs == 0 ) + continue; + + auto const& best_cut = cuts[index][0]; + + kitty::dynamic_truth_table tt; + std::vector> children; + std::tie( tt, children ) = create_lut( n, node_to_signal, node_driver_type ); + edges += children.size(); + + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = res.create_node( children, tt ); + break; + + case driver_type::neg: + node_to_signal[n] = res.create_node( children, ~tt ); + break; + + case driver_type::mixed: + node_to_signal[n] = res.create_node( children, tt ); + opposites[n] = res.create_node( children, ~tt ); + edges += children.size(); + break; + } + } + + /* outputs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) && node_driver_type[f] == driver_type::mixed ) + res.create_po( opposites[ntk.get_node( f )] ); + else + res.create_po( node_to_signal[f] ); + } ); + + st.area = area; + st.delay = delay; + st.edges = edges; + + return res; + } + + klut_network create_lut_network_mapped() + { + klut_network res; + mapping_view mapping_ntk{ ntk }; + + /* load mapping info */ + for ( auto const& n : topo_order ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs == 0 ) + continue; + + std::vector nodes; + auto const& best_cut = cuts[index][0]; + + for ( auto const& l : best_cut ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + mapping_ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + mapping_ntk.set_cell_function( n, truth_tables[best_cut->func_id] ); + } + } + + /* generate mapped network */ + collapse_mapped_network( res, mapping_ntk ); + + st.area = area; + st.delay = delay; + st.edges = edges; + + return res; + } + + inline lut_info create_lut( node const& n, node_map, Ntk>& node_to_signal, node_map const& node_driver_type ) + { + auto const& best_cut = cuts[ntk.node_to_index( n )][0]; + + std::vector> children; + for ( auto const& l : best_cut ) + { + children.push_back( node_to_signal[ntk.index_to_node( l )] ); + } + + /* recursively compute the function for each choice until success */ + ntk.incr_trav_id(); + unordered_node_map node_to_value( ntk ); + + /* add constants */ + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = kitty::dynamic_truth_table( best_cut.size() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = ~kitty::dynamic_truth_table( best_cut.size() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( true ) ), ntk.trav_id() ); + } + + /* add leaves */ + uint32_t ctr = 0; + for ( uint32_t leaf : best_cut ) + { + kitty::dynamic_truth_table tt_leaf( best_cut.size() ); + kitty::create_nth_var( tt_leaf, ctr++, node_driver_type[ntk.index_to_node( leaf )] == driver_type::neg ); + node_to_value[ntk.index_to_node( leaf )] = tt_leaf; + ntk.set_visited( ntk.index_to_node( leaf ), ntk.trav_id() ); + } + + /* recursively compute the function */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_function_rec( ntk.get_node( f ), node_to_value ); + } ); + + std::vector tts; + ntk.foreach_fanin( n, [&]( auto const& f ) { + tts.push_back( node_to_value[ntk.get_node( f )] ); + } ); + TT tt = ntk.compute( n, tts.begin(), tts.end() ); + + minimize_support( tt, children ); + + return { tt, children }; + } + + void compute_function_rec( node const& n, unordered_node_map& node_to_value ) + { + if ( ntk.visited( n ) == ntk.trav_id() ) + { + assert( node_to_value.has( n ) ); + return; + } + + assert( !ntk.is_ci( n ) ); + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_function_rec( ntk.get_node( f ), node_to_value ); + } ); + + /* compute the function */ + std::vector tts; + ntk.foreach_fanin( n, [&]( auto const& f ) { + tts.push_back( node_to_value[ntk.get_node( f )] ); + } ); + + node_to_value[n] = ntk.compute( n, tts.begin(), tts.end() ); + } + + void derive_mapping() + { + ntk.clear_mapping(); + + for ( auto const& n : topo_order ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs == 0 ) + continue; + + std::vector nodes; + auto const& best_cut = cuts[index][0]; + + for ( auto const& l : best_cut ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + ntk.set_cell_function( n, truth_tables[best_cut->func_id] ); + } + } + + st.area = area; + st.delay = delay; + st.edges = edges; + } + + void minimize_support( TT& tt, std::vector>& children ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* variables not in the support are the most significative */ + if ( ( support & ( support + 1u ) ) == 0u ) + { + if ( support_size != children.size() ) + { + children.erase( children.begin() + support_size, children.end() ); + tt = kitty::shrink_to( tt, support_size ); + } + + return; + } + + /* vacuous variables */ + const auto support_vector = kitty::min_base_inplace( tt ); + assert( support_vector.size() != children.size() ); + + auto tt_shrink = shrink_to( tt, support_size ); + std::vector> children_support( support_size ); + + auto it_support = support_vector.begin(); + auto it_children = children_support.begin(); + while ( it_support != support_vector.end() ) + { + *it_children++ = children[*it_support++]; + } + + children = std::move( children_support ); + } +#pragma endregion + +#pragma region balancing + void compute_isop( cut_t& cut, bool both_phases = true ) + { + uint32_t func_id = cut->func_id >> 1; + + if ( func_id < isops.size() ) + { + auto const& sop = isops[func_id]; + if ( sop.size() > max_cubes ) + { + cut->data.ignore = true; + } + return; + } + + assert( func_id == isops.size() ); + + sop_t sop, sop_n; + if ( ps.sop_balancing ) + { + sop = kitty::isop( truth_tables[func_id << 1u] ); + } + else + { + sop = exorcism( truth_tables[func_id << 1u] ); + } + + if ( both_phases ) + { + sop_t n_sop; + if ( ps.sop_balancing ) + { + n_sop = kitty::isop( ~truth_tables[func_id << 1u] ); + } + else + { + n_sop = exorcism( ~truth_tables[func_id << 1u] ); + } + + if ( n_sop.size() < sop.size() ) + { + sop.swap( n_sop ); + } + else if ( n_sop.size() == sop.size() ) + { + /* compute literal cost */ + uint32_t lit = 0, n_lit = 0; + for ( auto const& c : sop ) + { + lit += c.num_literals(); + } + for ( auto const& c : n_sop ) + { + n_lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + sop.swap( n_sop ); + } + } + } + + /* check size of SOP < max_cubes */ + if ( sop.size() > max_cubes ) + { + cut->data.ignore = true; + } + + isops.push_back( sop ); + return; + } + + void compute_balancing_cost( cut_t& cut ) + { + uint32_t decomposition_size = 0; + uint32_t decomposition_delay = 0; + + auto const& sop = isops[cut->func_id >> 1]; + + if ( cut->data.ignore || sop.size() > max_cubes ) + return; + + /* specific case size = 0 or = 1 */ + if ( cut.size() < 2 ) + return; + + /* collect arrival times for fanin */ + std::array arrival_pin; + unsigned i = 0; + for ( auto l : cut ) + arrival_pin[i++] = cuts[l].best()->data.delay; + + cubes_queue_t terms; + + /* get terms delay */ + assert( sop.size() <= max_cubes ); + for ( kitty::cube const& c : sop ) + { + cubes_queue_t lits; + for ( i = 0; i < cut.size(); ++i ) + { + if ( c.get_mask( i ) ) + { + lits.push( arrival_pin[i] ); + } + } + + if ( lits.size() == 0 ) + continue; + + decomposition_size += lits.size() - 1; + terms.push( compute_balancing_cost_term( lits ) ); + } + + assert( terms.size() > 0 ); + + decomposition_size += terms.size() - 1; + decomposition_delay = compute_balancing_cost_term( terms ); + + cut->data.delay = decomposition_delay; + cut->data.lut_area = decomposition_size; + cut->data.lut_delay = 1; /* not used */ + cut->data.area_flow += decomposition_size; + /* edge flow not used */ + } + + inline uint32_t compute_balancing_cost_term( cubes_queue_t& terms ) + { + while ( terms.size() != 1 ) + { + uint32_t l0 = terms.top(); + terms.pop(); + uint32_t l1 = terms.top(); + terms.pop(); + terms.push( std::max( l0, l1 ) + 1 ); + } + + return terms.top(); + } + + void compute_balancing_cost_required( uint32_t index ) + { + cut_t const& cut = cuts[index][0]; + + if ( cut.size() == 0 ) + return; + + /* propagate unit delay back */ + if ( cut.size() == 1 ) + { + for ( auto l : cut ) + { + node_match[l].required = std::min( node_match[l].required, node_match[index].required ); + } + } + + /* collect arrival times for fanin */ + std::array, max_sop_decomp_size> connections; + std::array arrival_pin; + std::array required; + unsigned size = 0; + for ( auto l : cut ) + { + arrival_pin[size] = cuts[l].best()->data.delay; + connections[size] = std::make_pair( size, size ); + ++size; + } + + auto priority_cmp = []( std::pair const& a, std::pair const& b ) { return a.first > b.first; }; + using cubes_queue2_t = std::priority_queue, std::vector>, decltype( priority_cmp )>; + + cubes_queue2_t terms( priority_cmp ); + + /* get terms delay */ + auto const& sop = isops[cut->func_id >> 1]; + assert( sop.size() <= max_cubes ); + for ( kitty::cube const& c : sop ) + { + cubes_queue2_t lits( priority_cmp ); + for ( auto i = 0; i < cut.size(); ++i ) + { + if ( c.get_mask( i ) ) + { + lits.push( { arrival_pin[i], i } ); + } + } + + if ( lits.size() == 0 ) + continue; + + compute_balancing_cost_required_term( lits, connections, size ); + assert( size <= connections.size() ); + + terms.push( lits.top() ); + } + + assert( terms.size() > 0 ); + compute_balancing_cost_required_term( terms, connections, size ); + + uint32_t required_node = node_match[index].required; + assert( terms.top().first == cut->data.delay ); + assert( required_node >= terms.top().first ); + + /* init required times */ + for ( auto i = 0; i < size - 1; ++i ) + required[i] = UINT32_MAX; + required[size - 1] = required_node; + + compute_balancing_cost_propagate_required( connections, required, size, cut.size() ); + + /* assign required time */ + uint32_t ctr = 0; + for ( auto l : cut ) + { + node_match[l].required = std::min( node_match[l].required, required[ctr++] ); + assert( node_match[l].required >= cuts[l][0]->data.delay ); + } + } + + template + inline void compute_balancing_cost_required_term( Queue& terms, std::array, max_sop_decomp_size>& connections, uint32_t& size ) + { + while ( terms.size() != 1 ) + { + std::pair l0 = terms.top(); + terms.pop(); + std::pair l1 = terms.top(); + terms.pop(); + uint32_t arrival = std::max( l0.first, l1.first ) + 1; + terms.push( { arrival, size } ); + connections[size] = std::make_pair( l0.second, l1.second ); + ++size; + } + } + + inline void compute_balancing_cost_propagate_required( std::array, max_sop_decomp_size> const& connections, std::array& required, uint32_t size, uint32_t leaves ) + { + for ( auto i = size - 1; i >= leaves; --i ) + { + uint32_t time = required[i] - 1; + required[connections[i].first] = std::min( time, required[connections[i].first] ); + required[connections[i].second] = std::min( time, required[connections[i].second] ); + } + } +#pragma endregion + +private: + Ntk& ntk; + lut_map_params const& ps; + lut_map_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + uint32_t area_iteration{ 0 }; /* current area iteration */ + uint32_t delay{ 0 }; /* current delay of the mapping */ + uint32_t area{ 0 }; /* current area of the mapping */ + uint32_t edges{ 0 }; /* current edges of the mapping */ + uint32_t cuts_total{ 0 }; /* current computed cuts */ + const float epsilon{ 0.005f }; /* epsilon */ + LUTCostFn lut_cost{}; + + std::vector topo_order; + std::vector tmp_visited; + std::vector node_match; + + std::vector cuts; /* compressed representation of cuts */ + cut_merge_t lcuts; /* cut merger container */ + tt_cache truth_tables; /* cut truth tables */ + cost_cache truth_tables_cost; /* truth tables cost */ + isop_cache isops; /* cache for isops */ +}; +#pragma endregion + +} /* namespace detail */ + +/*! \brief LUT mapper. + * + * This function implements a LUT mapping algorithm. It is controlled by one + * template argument `ComputeTruth` (defaulted to `false`) which controls + * whether the LUT function is computed or the mapping is structural. In the + * former case, truth tables are computed during cut enumeration, + * which requires more runtime. + * + * This function returns a k-LUT network. + * + * The template `LUTCostFn` sets the cost function to evaluate depth and + * size of a truth table given its support size if `ComputeTruth` is set + * to false, or its function if `ComputeTruth` is set to true. + * + * This implementation offers more options such as delay oriented mapping + * and edges minimization compared to the command `lut_mapping`. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + */ +template +klut_network lut_map( Ntk& ntk, lut_map_params ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + lut_map_params tps = ps; + lut_map_stats st; + klut_network klut; + + /* adjust params for balancing */ + if ( ps.sop_balancing || ps.esop_balancing ) + { + tps.area_oriented_mapping = false; + tps.recompute_cuts = false; + tps.area_share_rounds = 0; + tps.edge_optimization = false; + tps.cut_expansion = false; + } + + detail::lut_map_impl p( ntk, tps, st ); + klut = p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst != nullptr ) + { + *pst = st; + } + + return klut; +} + +/*! \brief LUT mapper inplace. + * + * This function implements a LUT mapping algorithm. It is controlled by one + * template argument `StoreFunction` (defaulted to `false`) which controls + * whether the LUT function is stored in the mapping. In that case + * truth tables are computed during cut enumeration, which requires more + * runtime. + * + * The input network must be wrapped in a `mapping_view`. The computed mapping + * is stored in the view. In this version, some features of the mapper are + * disabled, such as on-the-fly decompositions, due to incompatibility. + * + * The template `LUTCostFn` sets the cost function to evaluate depth and + * size of a truth table given its support size, if `StoreFunction` is set + * to false, or its function, if `StoreFunction` is set to true. + * + * This implementation offers more options such as delay oriented mapping + * and edges minimization compared to the command `lut_mapping`. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + * - `clear_mapping` + * - `add_to_mapping` + * - `set_lut_function` (if `StoreFunction` is true) + * + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was inspired by the LUT + mapping command ``&if`` in ABC. + \endverbatim + */ +template +void lut_map_inplace( Ntk& ntk, lut_map_params const& ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + + lut_map_params tps = ps; + lut_map_stats st; + + /* adjust params for balancing */ + if ( ps.sop_balancing || ps.esop_balancing ) + { + tps.area_oriented_mapping = false; + tps.recompute_cuts = false; + tps.area_share_rounds = 0; + tps.edge_optimization = false; + tps.cut_expansion = false; + } + + detail::lut_map_impl p( ntk, tps, st ); + p.run_inplace(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst != nullptr ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/lut_mapping.hpp b/third-party/mockturtle/include/mockturtle/algorithms/lut_mapping.hpp new file mode 100644 index 00000000000..88748738ea0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/lut_mapping.hpp @@ -0,0 +1,546 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file lut_mapping.hpp + \brief LUT mapping + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include + +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/mf_cut.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for lut_mapping. + * + * The data structure `lut_mapping_params` holds configurable parameters + * with default arguments for `lut_mapping`. + */ +struct lut_mapping_params +{ + lut_mapping_params() + { + cut_enumeration_ps.cut_size = 6; + cut_enumeration_ps.cut_limit = 8; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut size is 6, the default cut limit is 8. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Number of rounds for area flow optimization. + * + * The first round is used for delay optimization. + */ + uint32_t rounds{ 2u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t rounds_ela{ 1u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for lut_mapping. + * + * The data structure `lut_mapping_stats` provides data collected by running + * `lut_mapping`. + */ +struct lut_mapping_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +/* function to update all cuts after cut enumeration */ +template +struct lut_mapping_update_cuts +{ + template + static void apply( NetworkCuts const& cuts, Ntk const& ntk ) + { + (void)cuts; + (void)ntk; + } +}; + +namespace detail +{ + +template +class lut_mapping_impl +{ +public: + using network_cuts_t = network_cuts; + using cut_t = typename network_cuts_t::cut_t; + +public: + lut_mapping_impl( Ntk& ntk, lut_mapping_params const& ps, lut_mapping_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + flow_refs( ntk.size() ), + map_refs( ntk.size(), 0 ), + flows( ntk.size() ), + delays( ntk.size() ), + cuts( cut_enumeration( ntk, ps.cut_enumeration_ps ) ) + { + lut_mapping_update_cuts().apply( cuts, ntk ); + } + + void run() + { + stopwatch t( st.time_total ); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + init_nodes(); + // print_state(); + + set_mapping_refs(); + // print_state(); + + while ( iteration < ps.rounds ) + { + compute_mapping(); + } + + while ( iteration < ps.rounds + ps.rounds_ela ) + { + compute_mapping(); + } + + derive_mapping(); + } + +private: + uint32_t cut_area( cut_t const& cut ) const + { + return static_cast( cut->data.cost ); + } + + void init_nodes() + { + ntk.foreach_node( [this]( auto n, auto ) { + const auto index = ntk.node_to_index( n ); + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + /* all terminals have flow 1.0 */ + flow_refs[index] = 1.0f; + } + else + { + flow_refs[index] = static_cast( ntk.fanout_size( n ) ); + } + + flows[index] = cuts.cuts( index )[0]->data.flow; + delays[index] = cuts.cuts( index )[0]->data.delay; + } ); + } + + template + void compute_mapping() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + compute_best_cut( ntk.node_to_index( n ) ); + } + set_mapping_refs(); + // print_state(); + } + + template + void set_mapping_refs() + { + const auto coef = 1.0f / ( 1.0f + ( iteration + 1 ) * ( iteration + 1 ) ); + + /* compute current delay and update mapping refs */ + delay = 0; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + delay = std::max( delay, delays[index] ); + + if constexpr ( !ELA ) + { + map_refs[index]++; + } + } ); + + /* compute current area and update mapping refs */ + area = 0; + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + /* skip constants and PIs (TODO: stop earlier) */ + if ( ntk.is_constant( *it ) || ntk.is_ci( *it ) ) + continue; + + const auto index = ntk.node_to_index( *it ); + if ( map_refs[index] == 0 ) + continue; + + if constexpr ( !ELA ) + { + for ( auto leaf : cuts.cuts( index )[0] ) + { + map_refs[leaf]++; + } + } + area++; + } + + /* blend flow references */ + for ( auto i = 0u; i < ntk.size(); ++i ) + { + flow_refs[i] = coef * flow_refs[i] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( map_refs[i] ) ); + } + + ++iteration; + } + + std::pair cut_flow( cut_t const& cut ) + { + uint32_t time{ 0u }; + float flow{ 0.0f }; + + for ( auto leaf : cut ) + { + time = std::max( time, delays[leaf] ); + flow += flows[leaf]; + } + + return { flow + cut_area( cut ), time + 1u }; + } + + /* reference cut: + * adds cut to current mapping and recursively adds best cuts of leaf + * nodes, if they are not part of the current mapping. + */ + uint32_t cut_ref( cut_t const& cut ) + { + uint32_t count = cut_area( cut ); + for ( auto leaf : cut ) + { + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) || ntk.is_ci( ntk.index_to_node( leaf ) ) ) + continue; + + if ( map_refs[leaf]++ == 0 ) + { + count += cut_ref( cuts.cuts( leaf )[0] ); + } + } + return count; + } + + /* dereference cut: + * removes cut from current mapping and recursively removes best cuts of + * leaf nodes, if they are part of the current mapping. + * (this is the inverse operation to cut_ref) + */ + uint32_t cut_deref( cut_t const& cut ) + { + uint32_t count = cut_area( cut ); + for ( auto leaf : cut ) + { + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) || ntk.is_ci( ntk.index_to_node( leaf ) ) ) + continue; + + if ( --map_refs[leaf] == 0 ) + { + count += cut_deref( cuts.cuts( leaf ).best() ); + } + } + return count; + } + + /* reference cut (special version): + * this special version of cut_ref does two additional things: + * 1. it stops recursing if it has found `limit` cuts + * 2. it remembers all cuts for which the reference count increases in the + * vector `tmp_area`. + */ + uint32_t cut_ref_limit_save( cut_t const& cut, uint32_t limit ) + { + uint32_t count = cut_area( cut ); + if ( limit == 0 ) + return count; + + for ( auto leaf : cut ) + { + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) || ntk.is_ci( ntk.index_to_node( leaf ) ) ) + continue; + + tmp_area.push_back( leaf ); + if ( map_refs[leaf]++ == 0 ) + { + count += cut_ref_limit_save( cuts.cuts( leaf ).best(), limit - 1 ); + } + } + return count; + } + + /* estimates the cost of adding this cut to the mapping: + * This algorithm references cuts recursively to estimate how many cuts + * would be needed to add to the mapping if `cut` were to be added. It + * temporarily modifies the reference counters but reverts them eventually. + */ + uint32_t cut_area_estimation( cut_t const& cut ) + { + tmp_area.clear(); + const auto count = cut_ref_limit_save( cut, 8 ); + for ( auto const& n : tmp_area ) + { + map_refs[n]--; + } + return count; + } + + template + void compute_best_cut( uint32_t index ) + { + constexpr auto mf_eps{ 0.005f }; + + float flow; + uint32_t time{ 0 }; + int32_t best_cut{ -1 }; + float best_flow{ std::numeric_limits::max() }; + uint32_t best_time{ std::numeric_limits::max() }; + int32_t cut_index{ -1 }; + + if constexpr ( ELA ) + { + if ( map_refs[index] > 0 ) + { + cut_deref( cuts.cuts( index )[0] ); + } + } + + for ( auto* cut : cuts.cuts( index ) ) + { + ++cut_index; + if ( cut->size() == 1 ) + continue; + + if constexpr ( ELA ) + { + flow = static_cast( cut_area_estimation( *cut ) ); + } + else + { + std::tie( flow, time ) = cut_flow( *cut ); + } + + if ( best_cut == -1 || best_flow > flow + mf_eps || ( best_flow > flow - mf_eps && best_time > time ) ) + { + best_cut = cut_index; + best_flow = flow; + best_time = time; + } + } + + if ( best_cut == -1 ) + return; + + if constexpr ( ELA ) + { + if ( map_refs[index] > 0 ) + { + cut_ref( cuts.cuts( index )[best_cut] ); + } + } + else + { + map_refs[index] = 0; + } + if constexpr ( ELA ) + { + best_time = cut_flow( cuts.cuts( index )[best_cut] ).second; + } + delays[index] = best_time; + flows[index] = best_flow / flow_refs[index]; + + if ( best_cut != 0 ) + { + cuts.cuts( index ).update_best( best_cut ); + } + } + + void derive_mapping() + { + ntk.clear_mapping(); + + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( map_refs[index] == 0 ) + continue; + + std::vector> nodes; + for ( auto const& l : cuts.cuts( index ).best() ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + ntk.set_cell_function( n, cuts.truth_table( cuts.cuts( index ).best() ) ); + } + } + } + + void print_state() + { + for ( auto i = 0u; i < ntk.size(); ++i ) + { + std::cout << fmt::format( "*** Obj = {:>3} (node = {:>3}) FlowRefs = {:5.2f} MapRefs = {:>2} Flow = {:5.2f} Delay = {:>3}\n", i, ntk.index_to_node( i ), flow_refs[i], map_refs[i], flows[i], delays[i] ); + // std::cout << cuts.cuts( i ); + } + std::cout << fmt::format( "Level = {} Area = {}\n", delay, area ); + } + +private: + Ntk& ntk; + lut_mapping_params const& ps; + lut_mapping_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + uint32_t delay{ 0 }; /* current delay of the mapping */ + uint32_t area{ 0 }; /* current area of the mapping */ + // bool ela{false}; /* compute exact area */ + + std::vector> top_order; + std::vector flow_refs; + std::vector map_refs; + std::vector flows; + std::vector delays; + network_cuts_t cuts; + + std::vector tmp_area; /* temporary vector to compute exact area */ +}; + +}; /* namespace detail */ + +/*! \brief LUT mapping. + * + * This function implements a LUT mapping algorithm. It is controlled by two + * template arguments `StoreFunction` (defaulted to `true`) and `CutData` + * (defaulted to `cut_enumeration_mf_cut`). The first argument `StoreFunction` + * controls whether the LUT function is stored in the mapping. In that case + * truth tables are computed during cut enumeration, which requires more + * runtime. The second argument is simuilar to the `CutData` argument in + * `cut_enumeration`, which can specialize the cost function to select priority + * cuts and store additional data. For LUT mapping using this function the + * type passed as `CutData` must implement the following three fields: + * + * - `uint32_t delay` + * - `float flow` + * - `float costs` + * + * See `include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp` for one + * example of a CutData type that implements the cost function that is used in + * the LUT mapper `&mf` in ABC. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + * - `clear_mapping` + * - `add_to_mapping` + * - `set_lut_function` (if `StoreFunction` is true) + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired but the LUT + mapping command ``&mf`` in ABC. + \endverbatim + */ +template +void lut_mapping( Ntk& ntk, lut_mapping_params const& ps = {}, lut_mapping_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + static_assert( !StoreFunction || has_set_cell_function_v, "Ntk does not implement the set_cell_function method" ); + + lut_mapping_stats st; + detail::lut_mapping_impl p( ntk, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/mapper.hpp b/third-party/mockturtle/include/mockturtle/algorithms/mapper.hpp new file mode 100644 index 00000000000..441f981a64e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/mapper.hpp @@ -0,0 +1,3483 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mapper.hpp + \brief Mapper + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include + +#include "../networks/aig.hpp" +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../networks/sequential.hpp" +#include "../networks/xag.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/tech_library.hpp" +#include "../views/binding_view.hpp" +#include "../views/color_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/exact_map_cut.hpp" +#include "cut_enumeration/tech_map_cut.hpp" +#include "detail/mffc_utils.hpp" +#include "detail/switching_activity.hpp" +#include "reconv_cut.hpp" +#include "resyn_engines/mig_resyn.hpp" +#include "resyn_engines/xag_resyn.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for map. + * + * The data structure `map_params` holds configurable parameters + * with default arguments for `map`. + */ +struct map_params +{ + map_params() + { + cut_enumeration_ps.cut_limit = 49; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 49. By default, + * truth table minimization is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Required time for delay optimization. */ + double required_time{ 0.0f }; + + /*! \brief Skip delay round for area optimization. */ + bool skip_delay_round{ false }; + + /*! \brief Number of rounds for area flow optimization. */ + uint32_t area_flow_rounds{ 1u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t ela_rounds{ 2u }; + + /*! \brief Number of rounds for exact switching power optimization. */ + uint32_t eswp_rounds{ 0u }; + + /*! \brief Number of patterns for switching activity computation. */ + uint32_t switching_activity_patterns{ 2048u }; + + /*! \brief Exploit logic sharing in exact area optimization of graph mapping. */ + bool enable_logic_sharing{ false }; + + /*! \brief Maximum number of cuts evaluated for logic sharing. */ + uint32_t logic_sharing_cut_limit{ 8u }; + + /*! \brief Use satisfiability don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 12u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for mapper. + * + * The data structure `map_stats` provides data collected by running + * `map`. + */ +struct map_stats +{ + /*! \brief Area result. */ + double area{ 0 }; + /*! \brief Worst delay result. */ + double delay{ 0 }; + /*! \brief Power result. */ + double power{ 0 }; + + /*! \brief Runtime for covering. */ + stopwatch<>::duration time_mapping{ 0 }; + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Cut enumeration stats. */ + cut_enumeration_stats cut_enumeration_st{}; + + /*! \brief Delay and area stats for each round. */ + std::vector round_stats{}; + + /*! \brief Mapping error. */ + bool mapping_error{ false }; + + void report() const + { + for ( auto const& stat : round_stats ) + { + std::cout << stat; + } + std::cout << fmt::format( "[i] Area = {:>5.2f}; Delay = {:>5.2f};", area, delay ); + if ( power != 0 ) + std::cout << fmt::format( " Power = {:>5.2f};\n", power ); + else + std::cout << "\n"; + std::cout << fmt::format( "[i] Mapping runtime = {:>5.2f} secs\n", to_seconds( time_mapping ) ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +struct cut_match_tech +{ + /* list of supergates matching the cut for positive and negative output phases */ + std::array> const*, 2> supergates = { nullptr, nullptr }; + /* input negations, 0: pos, 1: neg */ + std::array negations{ 0, 0 }; +}; + +template +struct node_match_tech +{ + /* best gate match for positive and negative output phases */ + supergate const* best_supergate[2] = { nullptr, nullptr }; + /* fanin pin phases for both output phases */ + uint8_t phase[2]; + /* best cut index for both phases */ + uint32_t best_cut[2]; + /* node is mapped using only one phase */ + bool same_match{ false }; + + /* arrival time at node output */ + double arrival[2]; + /* required time at node output */ + double required[2]; + /* area of the best matches */ + float area[2]; + + /* number of references in the cover 0: pos, 1: neg, 2: pos+neg */ + uint32_t map_refs[3]; + /* references estimation */ + float est_refs[3]; + /* area flow */ + float flows[3]; +}; + +template +class tech_map_impl +{ +public: + using network_cuts_t = fast_network_cuts; + using cut_t = typename network_cuts_t::cut_t; + using match_map = std::unordered_map>>; + using klut_map = std::unordered_map, 2>>; + using map_ntk_t = binding_view; + using seq_map_ntk_t = binding_view>; + +public: + explicit tech_map_impl( Ntk const& ntk, tech_library const& library, map_params const& ps, map_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + matches(), + switch_activity( ps.eswp_rounds ? switching_activity( ntk, ps.switching_activity_patterns ) : std::vector( 0 ) ), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps, &st.cut_enumeration_st ) ) + { + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + } + + explicit tech_map_impl( Ntk const& ntk, tech_library const& library, std::vector const& switch_activity, map_params const& ps, map_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + matches(), + switch_activity( switch_activity ), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps, &st.cut_enumeration_st ) ) + { + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + } + + map_ntk_t run() + { + stopwatch t( st.time_mapping ); + + auto [res, old2new] = initialize_map_network(); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + /* match cuts with gates */ + compute_matches(); + + /* init the data structure */ + init_nodes(); + + /* execute mapping */ + if ( !execute_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + + return res; + } + + seq_map_ntk_t run_seq() + { + stopwatch t( st.time_mapping ); + + auto [res, old2new] = initialize_map_seq_network(); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + /* match cuts with gates */ + compute_matches(); + + /* init the data structure */ + init_nodes(); + + /* execute mapping */ + if ( !execute_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + + return res; + } + +private: + bool execute_mapping() + { + /* compute mapping for delay */ + if ( !ps.skip_delay_round ) + { + if ( !compute_mapping() ) + { + return false; + } + } + + /* compute mapping using global area flow */ + while ( iteration < ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping() ) + { + return false; + } + } + + /* compute mapping using exact area */ + while ( iteration < ps.ela_rounds + ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping_exact() ) + { + return false; + } + } + + /* compute mapping using exact switching activity estimation */ + while ( iteration < ps.eswp_rounds + ps.ela_rounds + ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping_exact() ) + { + return false; + } + } + + return true; + } + + void init_nodes() + { + ntk.foreach_node( [this]( auto const& n, auto ) { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.est_refs[0] = node_data.est_refs[1] = node_data.est_refs[2] = static_cast( ntk.fanout_size( n ) ); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + match_constants( index ); + } + else if ( ntk.is_ci( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.arrival[1] = lib_inv_delay; + } + } ); + } + + void compute_matches() + { + /* match gates */ + ntk.foreach_gate( [&]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + + std::vector> node_matches; + + auto i = 0u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* ignore unit cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ( *cut )->data.ignore = true; + continue; + } + if ( cut->size() > NInputs ) + { + /* Ignore cuts too big to be mapped using the library */ + ( *cut )->data.ignore = true; + continue; + } + const auto tt = cuts.truth_table( *cut ); + const auto fe = kitty::extend_to<6>( tt ); + auto fe_canon = fe; + + uint8_t negations_pos = 0; + uint8_t negations_neg = 0; + + /* match positive polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization( fe ); + fe_canon = std::get<0>( canon ); + negations_pos = std::get<1>( canon ); + } + auto const supergates_pos = library.get_supergates( fe_canon ); + + /* match negative polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization( ~fe ); + fe_canon = std::get<0>( canon ); + negations_neg = std::get<1>( canon ); + } + else + { + fe_canon = ~fe; + } + auto const supergates_neg = library.get_supergates( fe_canon ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut_match_tech match{ { supergates_pos, supergates_neg }, { negations_pos, negations_neg } }; + + node_matches.push_back( match ); + ( *cut )->data.match_index = i++; + } + else + { + /* Ignore not matched cuts */ + ( *cut )->data.ignore = true; + } + } + + matches[index] = node_matches; + } ); + } + + template + bool compute_mapping() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + continue; + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0 ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + bool compute_mapping_exact() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + if ( node_data.same_match && node_data.map_refs[2] != 0 ) + { + if ( node_data.best_supergate[0] != nullptr ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0u ); + else + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1u ); + } + + /* match positive phase */ + match_phase_exact( n, 0u ); + + /* match negative phase */ + match_phase_exact( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0 ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + if constexpr ( SwitchActivity ) + stats << fmt::format( "[i] Switching: Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + else + stats << fmt::format( "[i] Area : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + bool set_mapping_refs() + { + const auto coef = 1.0f / ( 2.0f + ( iteration + 1 ) * ( iteration + 1 ) ); + + if constexpr ( !ELA ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].map_refs[0] = node_match[i].map_refs[1] = node_match[i].map_refs[2] = 0u; + } + } + + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + node_match[index].map_refs[2]++; + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_match[index].map_refs[2] > 0u ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_supergate[0] == nullptr && node_data.best_supergate[1] == nullptr ) + { + std::cerr << "[i] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_ci( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( node_match[index].map_refs[2] == 0u ) + continue; + + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + + if ( node_data.best_supergate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[i] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + area += lib_inv_area; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + } + + /* blend estimated references */ + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[2] = coef * node_match[i].est_refs[2] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[2] ) ); + node_match[i].est_refs[1] = coef * node_match[i].est_refs[1] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[1] ) ); + node_match[i].est_refs[0] = coef * node_match[i].est_refs[0] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[0] ) ); + } + + ++iteration; + return true; + } + + void compute_required_time() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* return in case of `skip_delay_round` */ + if ( iteration == 0 ) + return; + + auto required = delay; + + if ( ps.required_time != 0.0f ) + { + /* Global target time constraint */ + if ( ps.required_time < delay - epsilon ) + { + if ( !ps.skip_delay_round && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl; + } + else + { + required = ps.required_time; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = required; + else + node_match[index].required[0] = required; + } ); + + /* propagate required time to the PIs */ + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + if ( ntk.is_ci( *it ) || ntk.is_constant( *it ) ) + break; + + const auto index = ntk.node_to_index( *it ); + + if ( node_match[index].map_refs[2] == 0 ) + continue; + + auto& node_data = node_match[index]; + + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + unsigned other_phase = use_phase ^ 1; + + assert( node_data.best_supergate[0] != nullptr || node_data.best_supergate[1] != nullptr ); + assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay ); + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto const& supergate = node_data.best_supergate[use_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[use_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + + if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( index )[node_data.best_cut[other_phase]]; + auto const& supergate = node_data.best_supergate[other_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[other_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + } + } + + template + void match_phase( node const& n, uint8_t phase ) + { + double best_arrival = std::numeric_limits::max(); + double best_area_flow = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area_flow = best_supergate->area + cut_leaves_flow( cut, n, phase ); + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_supergate->tdelay[ctr]; + best_arrival = std::max( best_arrival, arrival_pin ); + ++ctr; + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index].supergates; + auto const negation = cut_matches[( *cut )->data.match_index].negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint8_t gate_polarity = gate.polarity ^ negation; + node_data.phase[phase] = gate_polarity; + double area_local = gate.area + cut_leaves_flow( *cut, n, phase ); + double worst_arrival = 0.0f; + + auto ctr = 0u; + for ( auto l : *cut ) + { + double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + if constexpr ( DO_AREA ) + { + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + } + + if ( compare_map( worst_arrival, best_arrival, area_local, best_area_flow, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_area_flow = area_local; + best_size = cut->size(); + best_cut = cut_index; + best_area = gate.area; + best_phase = gate_polarity; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_area_flow; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + } + + template + void match_phase_exact( node const& n, uint8_t phase ) + { + double best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_supergate->tdelay[ctr]; + best_arrival = std::max( best_arrival, arrival_pin ); + ++ctr; + } + + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + else + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index].supergates; + auto const negation = cut_matches[( *cut )->data.match_index].negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint8_t gate_polarity = gate.polarity ^ negation; + node_data.phase[phase] = gate_polarity; + node_data.area[phase] = gate.area; + float area_exact = cut_ref( *cut, n, phase ); + cut_deref( *cut, n, phase ); + double worst_arrival = 0.0f; + + auto ctr = 0u; + for ( auto l : *cut ) + { + double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = gate.area; + best_size = cut->size(); + best_cut = cut_index; + best_phase = gate_polarity; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + template + void match_drop_phase( node const& n, float required_margin_factor ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* compute arrival adding an inverter to the other match phase */ + double worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + double worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + + /* only one phase is matched */ + if ( node_data.best_supergate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + return; + } + else if ( node_data.best_supergate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + return; + } + + /* try to use only one match to cover both phases */ + if constexpr ( !DO_AREA ) + { + /* if arrival improves matching the other phase and inserting an inverter */ + if ( worst_arrival_npos < node_data.arrival[0] + epsilon ) + { + use_one = true; + } + if ( worst_arrival_nneg < node_data.arrival[1] + epsilon ) + { + use_zero = true; + } + } + else + { + /* check if both phases + inverter meet the required time */ + use_zero = worst_arrival_nneg < ( node_data.required[1] + epsilon - required_margin_factor * lib_inv_delay ); + use_one = worst_arrival_npos < ( node_data.required[0] + epsilon - required_margin_factor * lib_inv_delay ); + } + + /* condition on not used phases, evaluate a substitution during exact area recovery */ + if constexpr ( ELA ) + { + if ( iteration != 0 ) + { + if ( node_data.map_refs[0] == 0 || node_data.map_refs[1] == 0 ) + { + /* select the used match */ + auto phase = 0; + auto nphase = 0; + if ( node_data.map_refs[0] == 0 ) + { + phase = 1; + use_one = true; + use_zero = false; + } + else + { + nphase = 1; + use_one = false; + use_zero = true; + } + /* select the not used match instead if it leads to area improvement and doesn't violate the required time */ + if ( node_data.arrival[nphase] + lib_inv_delay < node_data.required[phase] + epsilon ) + { + auto size_phase = cuts.cuts( index )[node_data.best_cut[phase]].size(); + auto size_nphase = cuts.cuts( index )[node_data.best_cut[nphase]].size(); + + if ( compare_map( node_data.arrival[nphase] + lib_inv_delay, node_data.arrival[phase], node_data.flows[nphase] + lib_inv_area, node_data.flows[phase], size_nphase, size_phase ) ) + { + /* invert the choice */ + use_zero = !use_zero; + use_one = !use_one; + } + } + } + } + } + + if ( !use_zero && !use_one ) + { + /* use both phases */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.flows[2] = node_data.flows[0] + node_data.flows[1]; + node_data.same_match = false; + return; + } + + /* use area flow as a tiebreaker */ + if ( use_zero && use_one ) + { + auto size_zero = cuts.cuts( index )[node_data.best_cut[0]].size(); + auto size_one = cuts.cuts( index )[node_data.best_cut[1]].size(); + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + + if ( use_zero ) + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the negative phase cut if in use */ + if ( node_data.map_refs[1] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + /* reference the positive cut if not in use before */ + if ( node_data.map_refs[0] == 0 && node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + } + else + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the positive phase cut if in use */ + if ( node_data.map_refs[0] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + /* reference the negative cut if not in use before */ + if ( node_data.map_refs[1] == 0 && node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + } + } + + inline void set_match_complemented_phase( uint32_t index, uint8_t phase, double worst_arrival_n ) + { + auto& node_data = node_match[index]; + auto phase_n = phase ^ 1; + node_data.same_match = true; + node_data.best_supergate[phase_n] = nullptr; + node_data.best_cut[phase_n] = node_data.best_cut[phase]; + node_data.phase[phase_n] = node_data.phase[phase]; + node_data.arrival[phase_n] = worst_arrival_n; + node_data.area[phase_n] = node_data.area[phase]; + node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[2]; + node_data.flows[phase_n] = node_data.flows[phase]; + node_data.flows[2] = node_data.flows[phase]; + } + + void match_constants( uint32_t index ) + { + auto& node_data = node_match[index]; + + kitty::static_truth_table<6> zero_tt; + auto const supergates_zero = library.get_supergates( zero_tt ); + auto const supergates_one = library.get_supergates( ~zero_tt ); + + /* Not available in the library */ + if ( supergates_zero == nullptr && supergates_one == nullptr ) + { + return; + } + /* if only one is available, the other is obtained using an inverter */ + if ( supergates_zero != nullptr ) + { + node_data.best_supergate[0] = &( ( *supergates_zero )[0] ); + node_data.arrival[0] = node_data.best_supergate[0]->tdelay[0]; + node_data.area[0] = node_data.best_supergate[0]->area; + node_data.phase[0] = 0; + } + if ( supergates_one != nullptr ) + { + node_data.best_supergate[1] = &( ( *supergates_one )[0] ); + node_data.arrival[1] = node_data.best_supergate[1]->tdelay[0]; + node_data.area[1] = node_data.best_supergate[1]->area; + node_data.phase[1] = 0; + } + else + { + node_data.same_match = true; + node_data.arrival[1] = node_data.arrival[0] + lib_inv_delay; + node_data.area[1] = node_data.area[0] + lib_inv_area; + node_data.phase[1] = 1; + } + if ( supergates_zero == nullptr ) + { + node_data.same_match = true; + node_data.arrival[0] = node_data.arrival[1] + lib_inv_delay; + node_data.area[0] = node_data.area[1] + lib_inv_area; + node_data.phase[0] = 1; + } + } + + inline double cut_leaves_flow( cut_t const& cut, node const& n, uint8_t phase ) + { + double flow{ 0.0f }; + auto const& node_data = node_match[ntk.node_to_index( n )]; + + uint8_t ctr = 0u; + for ( auto leaf : cut ) + { + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + flow += node_match[leaf].flows[leaf_phase]; + } + + return flow; + } + + template + float cut_ref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs[2]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + ++node_match[leaf].map_refs[2]; + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_deref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* dereference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( --node_match[leaf].map_refs[1] == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + --node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */ + if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + /* Recursive dereferencing */ + if ( --node_match[leaf].map_refs[2] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + --node_match[leaf].map_refs[2]; + if ( --node_match[leaf].map_refs[leaf_phase] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + void insert_buffers() + { + if ( lib_buf_id != UINT32_MAX ) + { + double area_old = area; + bool buffers = false; + + ntk.foreach_co( [&]( auto const& f ) { + auto const& n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) && ntk.is_ci( n ) && !ntk.is_complemented( f ) ) + { + area += lib_buf_area; + delay = std::max( delay, node_match[ntk.node_to_index( n )].arrival[0] + lib_inv_delay ); + buffers = true; + } + } ); + + /* round stats */ + if ( ps.verbose && buffers ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + area_gain = float( ( area_old - area ) / area_old * 100 ); + + stats << fmt::format( "[i] Buffering: Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + } + } + + std::pair initialize_map_network() + { + map_ntk_t dest( library.get_gates() ); + klut_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + + return { dest, old2new }; + } + + std::pair initialize_map_seq_network() + { + seq_map_ntk_t dest( library.get_gates() ); + klut_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + ntk.foreach_ro( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_ro(); + } ); + + return { dest, old2new }; + } + + template + void finalize_cover( NtkDest& res, klut_map& old2new ) + { + for ( auto const& n : top_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_supergate[0] == nullptr && node_data.best_supergate[1] == nullptr ) + continue; + } + else if ( ntk.is_ci( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[n][0] ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( node_data.map_refs[2] == 0u ) + continue; + + unsigned phase = ( node_data.best_supergate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_binding( res.get_node( old2new[index][phase ^ 1] ), lib_inv_id ); + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + } + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_ci( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_binding( res.get_node( buf ), lib_buf_id ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + if constexpr ( has_foreach_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_ri( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_ci( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for RIs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_ri( buf ); + res.add_binding( res.get_node( buf ), lib_buf_id ); + } + else + { + res.create_ri( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + } + + /* write final results */ + st.area = area; + st.delay = delay; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + template + void create_lut_for_gate( NtkDest& res, klut_map& old2new, uint32_t index, unsigned phase ) + { + auto const& node_data = node_match[index]; + auto& best_cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto const& gate = node_data.best_supergate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_supergate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_binding( res.get_node( f ), gate->root->id ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate_rec( res, *gate, children ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + template + signal create_lut_for_gate_rec( NtkDest& res, composed_gate const& gate, std::vector> const& children ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate_rec( res, *fanin, children ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_binding( res.get_node( f ), gate.root->id ); + return f; + } + + template + inline bool compare_map( double arrival, double best_arrival, double area_flow, double best_area_flow, uint32_t size, uint32_t best_size ) + { + if constexpr ( DO_AREA ) + { + if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + else if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + } + else + { + if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + else if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + } + if ( size < best_size ) + { + return true; + } + return false; + } + + double compute_switching_power() + { + double power = 0.0f; + + for ( auto const& n : top_order ) + { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_supergate[0] == nullptr && node_data.best_supergate[1] == nullptr ) + continue; + } + else if ( ntk.is_ci( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + continue; + } + + /* continue if cut is not in the cover */ + if ( node_match[index].map_refs[2] == 0u ) + continue; + + unsigned phase = ( node_data.best_supergate[0] != nullptr ) ? 0 : 1; + + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + } + + phase = phase ^ 1; + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + } + } + + return power; + } + +private: + Ntk const& ntk; + tech_library const& library; + map_params const& ps; + map_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + double delay{ 0.0f }; /* current delay of the mapping */ + double area{ 0.0f }; /* current area of the mapping */ + const float epsilon{ 0.005f }; /* epsilon */ + + /* lib inverter info */ + float lib_inv_area; + float lib_inv_delay; + uint32_t lib_inv_id; + + /* lib buffer info */ + float lib_buf_area; + float lib_buf_delay; + uint32_t lib_buf_id; + + std::vector> top_order; + std::vector> node_match; + match_map matches; + std::vector switch_activity; + network_cuts_t cuts; +}; + +} /* namespace detail */ + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. It is controlled by a + * template argument `CutData` (defaulted to `cut_enumeration_tech_map_cut`). + * The argument is similar to the `CutData` argument in `cut_enumeration`, which can + * specialize the cost function to select priority cuts and store additional data. + * The default argument gives priority firstly to the cut size, then delay, and lastly + * to area flow. Thus, it is more suited for delay-oriented mapping. + * The type passed as `CutData` must implement the following four fields: + * + * - `uint32_t delay` + * - `float flow` + * - `uint8_t match_index` + * - `bool ignore` + * + * See `include/mockturtle/algorithms/cut_enumeration/cut_enumeration_tech_map_cut.hpp` + * for one example of a CutData type that implements the cost function that is used in + * the technology mapper. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a k-LUT network. Each LUT abstracts a gate of the technology library. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + * The implementation of this algorithm was inspired by the + * mapping command ``map`` in ABC. + */ +template +binding_view map( Ntk const& ntk, tech_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + map_stats st; + detail::tech_map_impl p( ntk, library, ps, st ); + auto res = p.run(); + + st.time_total = st.time_mapping + st.cut_enumeration_st.time_total; + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology mapping for sequential networks. + * + * Version of `map` for technology mapping of sequential networks. + * + * The function returns a sequential k-LUT network. Each LUT abstracts a gate of the technology library. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_ro` + * - `foreach_co` + * - `foreach_ri` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Sequential network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view> seq_map( Ntk const& ntk, tech_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_ri_v, "Ntk does not implement the has_foreach_ri method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the has_foreach_po method" ); + static_assert( has_foreach_ro_v, "Ntk does not implement the has_foreach_ro method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + map_stats st; + detail::tech_map_impl p( ntk, library, ps, st ); + auto res = p.run_seq(); + + st.time_total = st.time_mapping + st.cut_enumeration_st.time_total; + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +namespace detail +{ + +template +struct cut_match_t +{ + /* list of supergates matching the cut for positive and negative output phases */ + std::vector> const* supergates[2] = { nullptr, nullptr }; + /* input permutations, at index i, it contains the permutated position of i */ + std::array permutation{}; + /* permutated input negations */ + uint8_t negation{ 0 }; +}; + +template +struct node_match_t +{ + /* best supergate match for positive and negative output phases */ + exact_supergate const* best_supergate[2] = { nullptr, nullptr }; + /* fanin pin phases for both output phases */ + uint8_t phase[2]; + /* best cut index for both phases */ + uint32_t best_cut[2]; + /* node is mapped using only one phase */ + bool same_match{ false }; + + /* arrival time at node output */ + double arrival[2]; + /* required time at node output */ + double required[2]; + /* area of the best matches */ + float area[2]; + + /* number of references in the cover 0: pos, 1: neg, 2: pos+neg */ + uint32_t map_refs[3]; + /* references estimation */ + float est_refs[3]; + /* area flow */ + float flows[3]; +}; + +template +class exact_map_impl +{ +public: + static constexpr uint32_t max_window_size = 12; + using network_cuts_t = fast_network_cuts; + using cut_t = typename network_cuts_t::cut_t; + +public: + explicit exact_map_impl( Ntk& ntk, exact_library const& library, map_params const& ps, map_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + lib_database( library.get_database() ), + node_match( ntk.size() ), + matches(), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps ) ) + { + std::tie( lib_inv_area, lib_inv_delay ) = library.get_inverter_info(); + } + + NtkDest run() + { + stopwatch t( st.time_mapping ); + + auto [res, old2new] = initialize_dest(); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + /* match cuts with gates */ + if ( ps.use_dont_cares ) + { + compute_matches_dc(); + } + else + { + compute_matches(); + } + + /* init the data structure */ + init_nodes(); + + /* compute mapping delay */ + if ( !ps.skip_delay_round ) + { + if ( !compute_mapping() ) + { + return res; + } + } + + /* compute mapping using global area flow */ + while ( iteration < ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping() ) + { + return res; + } + } + + /* compute mapping using exact area */ + while ( iteration < ps.ela_rounds + ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( ps.enable_logic_sharing && iteration == ps.ela_rounds + ps.area_flow_rounds ) + { + if ( !compute_exact_area_aggressive( res, old2new ) ) + { + return res; + } + } + else + { + if ( !compute_exact_area() ) + { + return res; + } + } + } + + /* generate the output network using the computed mapping */ + finalize_cover( res, old2new ); + + if ( ps.enable_logic_sharing ) + return cleanup_dangling( res ); + else + return res; + } + +private: + void init_nodes() + { + ntk.foreach_node( [this]( auto const& n, auto ) { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.est_refs[0] = node_data.est_refs[1] = node_data.est_refs[2] = static_cast( ntk.fanout_size( n ) ); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + } + else if ( ntk.is_ci( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.arrival[1] = lib_inv_delay; + } + } ); + } + + void compute_matches() + { + /* match gates */ + ntk.foreach_gate( [&]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + + std::vector> node_matches; + + auto i = 0u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* ignore unit cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ( *cut )->data.ignore = true; + continue; + } + + if ( cut->size() > NInputs ) + { + /* Ignore cuts too big to be mapped using the library */ + ( *cut )->data.ignore = true; + continue; + } + + /* match the cut using canonization and get the gates */ + const auto tt = cuts.truth_table( *cut ); + const auto fe = kitty::extend_to( tt ); + const auto config = kitty::exact_npn_canonization( fe ); + auto const supergates_npn = library.get_supergates( std::get<0>( config ) ); + auto const supergates_npn_neg = library.get_supergates( ~std::get<0>( config ) ); + + if ( supergates_npn != nullptr || supergates_npn_neg != nullptr ) + { + auto neg = std::get<1>( config ); + auto perm = std::get<2>( config ); + uint8_t phase = ( neg >> NInputs ) & 1; + cut_match_t match; + + match.supergates[phase] = supergates_npn; + match.supergates[phase ^ 1] = supergates_npn_neg; + + /* store permutations and negations */ + match.negation = 0; + for ( auto j = 0u; j < perm.size() && j < NInputs; ++j ) + { + match.permutation[perm[j]] = j; + match.negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + node_matches.push_back( match ); + ( *cut )->data.match_index = i++; + } + else + { + /* Ignore not matched cuts */ + ( *cut )->data.ignore = true; + } + } + + matches[index] = node_matches; + } ); + } + + void compute_matches_dc() + { + reconvergence_driven_cut_parameters rps; + rps.max_leaves = ps.window_size; + reconvergence_driven_cut_statistics rst; + detail::reconvergence_driven_cut_impl reconv_cuts( ntk, rps, rst ); + + color_view color_ntk{ ntk }; + std::array divisors; + for ( uint32_t i = 0; i < NInputs; ++i ) + { + divisors[i] = i; + } + + /* match gates */ + ntk.foreach_gate( [&]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + std::vector> node_matches; + + std::vector> roots = { n }; + auto const extended_leaves = reconv_cuts.run( roots ).first; + + std::vector> gates{ collect_nodes( color_ntk, extended_leaves, roots ) }; + window_view window_ntk{ color_ntk, extended_leaves, roots, gates }; + + default_simulator> sim; + const auto tts = simulate_nodes>( window_ntk, sim ); + + auto i = 0u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* ignore unit cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ( *cut )->data.ignore = true; + continue; + } + + if ( cut->size() > NInputs ) + { + /* Ignore cuts too big to be mapped using the library */ + ( *cut )->data.ignore = true; + continue; + } + + /* match the cut using canonization and get the gates */ + const auto tt = cuts.truth_table( *cut ); + const auto fe = kitty::shrink_to( tt ); + + auto [tt_npn, neg, perm] = kitty::exact_npn_canonization( fe ); + auto perm_neg = perm; + auto neg_neg = neg; + + /* dont cares computation */ + kitty::static_truth_table care; + + bool containment = true; + bool filter = false; + for ( auto const& l : *cut ) + { + if ( color_ntk.color( ntk.index_to_node( l ) ) != color_ntk.current_color() ) + { + containment = false; + break; + } + } + + if ( containment ) + { + /* compute care set */ + for ( auto i = 0u; i < ( 1u << window_ntk.num_pis() ); ++i ) + { + uint32_t entry{ 0u }; + auto j = 0u; + for ( auto const& l : *cut ) + { + entry |= kitty::get_bit( tts[l], i ) << j; + ++j; + } + kitty::set_bit( care, entry ); + } + } + else + { + /* completely specified */ + care = ~care; + } + + auto const dc_npn = apply_npn_transformation( ~care, neg & ~( 1 << NInputs ), perm ); + const std::vector>* supergates_npn = library.get_supergates( tt_npn, dc_npn, neg, perm ); + const std::vector>* supergates_npn_neg = library.get_supergates( ~tt_npn, dc_npn, neg_neg, perm_neg ); + + if ( supergates_npn != nullptr || supergates_npn_neg != nullptr ) + { + cut_match_t match; + + if ( supergates_npn == nullptr ) + { + perm = perm_neg; + neg = neg_neg; + } + + uint8_t phase = ( neg >> NInputs ) & 1; + + match.supergates[phase] = supergates_npn; + match.supergates[phase ^ 1] = supergates_npn_neg; + + /* store permutations and negations */ + match.negation = 0; + for ( auto j = 0u; j < perm.size() && j < NInputs; ++j ) + { + match.permutation[perm[j]] = j; + match.negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + node_matches.push_back( match ); + ( *cut )->data.match_index = i++; + } + else + { + /* Ignore not matched cuts */ + ( *cut )->data.ignore = true; + } + } + + matches[index] = node_matches; + } ); + } + + template + bool compute_mapping() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0u ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + bool compute_exact_area() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + if ( node_data.same_match && node_data.map_refs[2] != 0 ) + { + if ( node_data.best_supergate[0] != nullptr ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0u ); + else + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1u ); + } + + /* match positive phase */ + match_phase_exact( n, 0u ); + + /* match negative phase */ + match_phase_exact( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0u ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + stats << fmt::format( "[i] Area : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + std::pair, Ntk>> initialize_dest() + { + node_map, Ntk> old2new( ntk ); + NtkDest dest; + + old2new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old2new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + } ); + + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto const& n ) { + old2new[n] = dest.create_ro(); + } ); + } + + return { dest, old2new }; + } + + void finalize_cover( NtkDest& res, node_map, Ntk>& old2new ) + { + if ( !ps.enable_logic_sharing || iteration == ps.area_flow_rounds + 1 ) + { + auto const& db = library.get_database(); + + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + return true; + auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs[2] == 0u ) + return true; + + /* get the implemented phase and map the best cut */ + unsigned phase = ( node_match[index].best_supergate[0] != nullptr ) ? 0 : 1; + auto& best_cut = cuts.cuts( index )[node_match[index].best_cut[phase]]; + + std::vector> children( NInputs, res.get_constant( false ) ); + auto const& match = matches[index][best_cut->data.match_index]; + auto const& supergate = node_match[index].best_supergate[phase]; + auto ctr = 0u; + for ( auto l : best_cut ) + { + children[match.permutation[ctr++]] = old2new[ntk.index_to_node( l )]; + } + for ( auto i = 0u; i < NInputs; ++i ) + { + if ( ( match.negation >> i ) & 1 ) + { + children[i] = !children[i]; + } + } + topo_view topo{ db, supergate->root }; + auto f = cleanup_dangling( topo, res, children.begin(), children.end() ).front(); + + if ( phase == 1 ) + f = !f; + + old2new[n] = f; + return true; + } ); + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + res.create_po( ntk.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f] ); + } ); + + if constexpr ( has_foreach_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + res.create_ri( ntk.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f] ); + } ); + } + + /* write final results */ + st.area = area; + st.delay = delay; + } + + template + bool set_mapping_refs() + { + const auto coef = 1.0f / ( 2.0f + ( iteration + 1 ) * ( iteration + 1 ) ); + + if constexpr ( !ELA ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].map_refs[0] = node_match[i].map_refs[1] = node_match[i].map_refs[2] = 0u; + } + } + + /* compute current delay and update mapping refs */ + delay = 0.0f; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + node_match[index].map_refs[2]++; + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + continue; + } + else if ( ntk.is_ci( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter over the negated fanins */ + area += lib_inv_area; + } + continue; + } + + if ( node_match[index].map_refs[2] == 0u ) + continue; + + auto& node_data = node_match[index]; + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + + if ( node_data.best_supergate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[i] MAP ERROR: library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto const& match = matches[index][best_cut->data.match_index]; + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> match.permutation[ctr++] ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + area += lib_inv_area; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto const& match = matches[index][best_cut->data.match_index]; + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> match.permutation[ctr++] ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + } + + /* blend flow references */ + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[2] = coef * node_match[i].est_refs[2] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[2] ) ); + node_match[i].est_refs[1] = coef * node_match[i].est_refs[1] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[1] ) ); + node_match[i].est_refs[0] = coef * node_match[i].est_refs[0] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[0] ) ); + } + + ++iteration; + return true; + } + + void compute_required_time() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* return in case of `skip_delay_round` */ + if ( iteration == 0 ) + return; + + auto required = delay; + + if ( ps.required_time != 0.0f ) + { + /* Global target time constraint */ + if ( ps.required_time < delay - epsilon ) + { + if ( !ps.skip_delay_round && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl; + } + else + { + required = ps.required_time; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = required; + else + node_match[index].required[0] = required; + } ); + + /* propagate required time to the PIs */ + auto i = ntk.size(); + while ( i-- > 0u ) + { + const auto n = ntk.index_to_node( i ); + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + break; + + if ( node_match[i].map_refs[2] == 0 ) + continue; + + auto& node_data = node_match[i]; + + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + unsigned other_phase = use_phase ^ 1; + + assert( node_data.best_supergate[0] != nullptr || node_data.best_supergate[1] != nullptr ); + assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over output inverter if present */ + if ( node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay ); + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( i )[node_data.best_cut[use_phase]]; + auto const& match = matches[i][best_cut->data.match_index]; + auto const& supergate = node_data.best_supergate[use_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[use_phase] >> match.permutation[ctr] ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[match.permutation[ctr]] ); + ctr++; + } + } + + if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( i )[node_data.best_cut[other_phase]]; + auto const& match = matches[i][best_cut->data.match_index]; + auto const& supergate = node_data.best_supergate[other_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[other_phase] >> match.permutation[ctr] ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[match.permutation[ctr]] ); + ctr++; + } + } + } + } + + template + void match_phase( node const& n, uint8_t phase ) + { + float best_arrival = std::numeric_limits::max(); + float best_area_flow = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + exact_supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto& supergates = cut_matches[( cut )->data.match_index]; + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area_flow = best_supergate->area + cut_leaves_flow( cut, n, phase ); + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( best_phase >> pin ) & 1] + best_supergate->tdelay[pin]; + best_arrival = std::max( best_arrival, arrival_pin ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index]; + + if ( supergates.supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : *cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates.supergates[phase] ) + { + uint8_t complement = supergates.negation ^ gate.polarity; + node_data.phase[phase] = complement; + float area_local = gate.area + cut_leaves_flow( *cut, n, phase ); + float worst_arrival = 0.0f; + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( complement >> pin ) & 1] + gate.tdelay[pin]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + } + + if constexpr ( DO_AREA ) + { + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + } + + if ( compare_map( worst_arrival, best_arrival, area_local, best_area_flow, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_area_flow = area_local; + best_size = cut->size(); + best_cut = cut_index; + best_area = gate.area; + best_phase = complement; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_area_flow; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + } + + void match_phase_exact( node const& n, uint8_t phase ) + { + float best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + exact_supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto const& supergates = cut_matches[( cut )->data.match_index]; + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( best_phase >> pin ) & 1] + best_supergate->tdelay[pin]; + best_arrival = std::max( best_arrival, arrival_pin ); + } + + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + else + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index]; + + if ( supergates.supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : *cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + for ( auto const& gate : *supergates.supergates[phase] ) + { + uint8_t complement = supergates.negation ^ gate.polarity; + node_data.phase[phase] = complement; + node_data.area[phase] = gate.area; + auto area_exact = cut_ref( *cut, n, phase ); + cut_deref( *cut, n, phase ); + float worst_arrival = 0.0f; + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( complement >> pin ) & 1] + gate.tdelay[pin]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + } + + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = gate.area; + best_size = cut->size(); + best_cut = cut_index; + best_phase = complement; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + bool compute_exact_area_aggressive( NtkDest& res, node_map, Ntk>& old2new ) + { + depth_view res_d{ res }; + + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + if ( node_data.same_match && node_data.map_refs[2] != 0 ) + { + if ( node_data.best_supergate[0] != nullptr ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0u ); + else + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1u ); + } + + /* match positive phase */ + auto sig0 = match_phase_exact_aggressive( res_d, old2new, n, 0u ); + + /* match negative phase */ + auto sig1 = match_phase_exact_aggressive( res_d, old2new, n, 1u ); + + /* try to drop one phase */ + float worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + float worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + if ( node_data.best_supergate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + recursive_ref( res, res.get_node( sig1 ) ); + } + old2new[n] = sig1; + continue; + } + else if ( node_data.best_supergate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + recursive_ref( res, res.get_node( sig0 ) ); + } + old2new[n] = sig0; + continue; + } + use_zero = worst_arrival_nneg < node_data.required[1] + epsilon; + use_one = worst_arrival_npos < node_data.required[0] + epsilon; + + if ( use_zero && use_one ) + { + auto size_zero = cuts.cuts( index )[node_data.best_cut[0]].size(); + auto size_one = cuts.cuts( index )[node_data.best_cut[1]].size(); + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + + if ( use_zero ) + { + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + recursive_ref( res, res.get_node( sig0 ) ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + old2new[n] = sig0; + } + else + { + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + recursive_ref( res, res.get_node( sig1 ) ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + old2new[n] = sig1; + } + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + stats << fmt::format( "[i] Area RW : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + signal match_phase_exact_aggressive( depth_view& res, node_map, Ntk>& old2new, node const& n, uint8_t phase ) + { + signal best_signal = res.get_constant( false ); + + float best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + exact_supergate const* best_supergate = node_data.best_supergate[phase]; + + /* create best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto const& supergates = cut_matches[( cut )->data.match_index]; + + /* permutate the children to the NPN-represenentative configuration */ + std::vector> children( NInputs, res.get_constant( false ) ); + auto ctr = 0u; + + for ( auto l : cut ) + { + children[supergates.permutation[ctr++]] = old2new[ntk.index_to_node( l )]; + } + + best_phase = supergates.negation; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + for ( auto i = 0u; i < NInputs; ++i ) + { + if ( ( best_phase >> i ) & 1 ) + { + children[i] = !children[i]; + } + } + topo_view topo{ lib_database, best_supergate->root }; + auto f = cleanup_dangling( topo, res, children.begin(), children.end() ).front(); + + if ( phase == 1 ) + f = !f; + + best_signal = f; + + best_arrival = res.level( res.get_node( f ) ); + + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_area = recursive_ref( res, res.get_node( f ) ); + recursive_deref( res, res.get_node( f ) ); + best_exact_area = cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + else + { + best_area = recursive_ref( res, res.get_node( f ) ); + recursive_deref( res, res.get_node( f ) ); + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + /* foreach cut */ + unsigned int rewrite_count = 1u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts, not matched cuts, or rewriting limit reached */ + if ( ( *cut )->data.ignore || ( rewrite_count > ps.logic_sharing_cut_limit && cut_index != best_cut ) ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index]; + + if ( supergates.supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + ++rewrite_count; + + std::vector> children( NInputs, res.get_constant( false ) ); + + auto ctr = 0u; + for ( auto l : *cut ) + { + children[supergates.permutation[ctr++]] = old2new[ntk.index_to_node( l )]; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates.supergates[phase] ) + { + uint8_t complement = supergates.negation; + node_data.phase[phase] = complement; + + /* rewrite each structure and measure the logic sharing */ + std::vector> children_loc( NInputs ); + + for ( auto ctr = 0u; ctr < NInputs; ++ctr ) + { + children_loc[ctr] = children[ctr] ^ ( ( ( complement >> ctr ) & 1 ) == 1 ); + } + topo_view topo{ lib_database, gate.root }; + auto f = cleanup_dangling( topo, res, children_loc.begin(), children_loc.end() ).front(); + + if ( phase == 1 ) + f = !f; + + float worst_arrival = res.level( res.get_node( f ) ); + + float area_hashed = recursive_ref( res, res.get_node( f ) ); + node_data.area[phase] = area_hashed; + recursive_deref( res, res.get_node( f ) ); + auto area_exact = cut_ref( *cut, n, phase ); + cut_deref( *cut, n, phase ); + + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = area_hashed; + best_size = cut->size(); + best_cut = cut_index; + best_phase = complement; + best_supergate = &gate; + best_signal = f; + } + } + + ++cut_index; + } + old2new[n] = best_signal; + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + recursive_ref( res, res.get_node( best_signal ) ); + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + } + return best_signal; + } + + template + void match_drop_phase( node const& n, unsigned area_margin_factor ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* compute arrival adding an inverter to the other match phase */ + float worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + float worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + + /* only one phase is matched */ + if ( node_data.best_supergate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + return; + } + else if ( node_data.best_supergate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + return; + } + + /* try to use only one match to cover both phases */ + if constexpr ( !DO_AREA ) + { + /* if arrival is less matching the other phase and inserting an inverter */ + if ( worst_arrival_npos < node_data.arrival[0] + epsilon ) + { + use_one = true; + } + if ( worst_arrival_nneg < node_data.arrival[1] + epsilon ) + { + use_zero = true; + } + if ( !use_zero && !use_one ) + { + /* use both phases to improve delay */ + node_data.flows[2] = ( node_data.flows[0] + node_data.flows[1] ) / node_data.est_refs[2]; + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + return; + } + } + else + { + /* check if both phases + inverter meet the required time */ + use_zero = worst_arrival_nneg < node_data.required[1] + epsilon - area_margin_factor * lib_inv_delay; + use_one = worst_arrival_npos < node_data.required[0] + epsilon - area_margin_factor * lib_inv_delay; + } + + /* use area flow as a tiebreaker. Unfortunately cannot keep + * the both phases since `node_map` does not support that */ + if ( use_zero && use_one ) + { + auto size_zero = cuts.cuts( index )[node_data.best_cut[0]].size(); + auto size_one = cuts.cuts( index )[node_data.best_cut[1]].size(); + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + + if ( use_zero ) + { + if constexpr ( ELA ) + { + if ( !node_data.same_match ) + { + if ( node_data.map_refs[1] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + if ( node_data.map_refs[0] == 0 ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + } + else + { + if constexpr ( ELA ) + { + if ( !node_data.same_match ) + { + if ( node_data.map_refs[0] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + if ( node_data.map_refs[1] == 0 && node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + } + } + + inline void set_match_complemented_phase( uint32_t index, uint8_t phase, float worst_arrival_n ) + { + auto& node_data = node_match[index]; + auto phase_n = phase ^ 1; + node_data.same_match = true; + node_data.best_supergate[phase_n] = nullptr; + node_data.best_cut[phase_n] = node_data.best_cut[phase]; + node_data.phase[phase_n] = node_data.phase[phase] ^ ( 1 << NInputs ); + node_data.arrival[phase_n] = worst_arrival_n; + node_data.area[phase_n] = node_data.area[phase]; + node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[2]; + node_data.flows[phase_n] = node_data.flows[phase]; + node_data.flows[2] = node_data.flows[phase]; + } + + inline float cut_leaves_flow( cut_t const& cut, node const& n, uint8_t phase ) + { + float flow{ 0.0f }; + auto const& node_data = node_match[ntk.node_to_index( n )]; + auto const& match = matches[ntk.node_to_index( n )][cut->data.match_index]; + + uint8_t ctr = 0u; + for ( auto leaf : cut ) + { + uint8_t leaf_phase = ( node_data.phase[phase] >> match.permutation[ctr++] ) & 1; + flow += node_match[leaf].flows[leaf_phase]; + } + + return flow; + } + + float cut_ref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + auto const& match = matches[ntk.node_to_index( n )][cut->data.match_index]; + float count = node_data.area[phase]; + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> match.permutation[ctr] ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + ++ctr; + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + count += lib_inv_area; + } + else + { + ++node_match[leaf].map_refs[0]; + } + ++ctr; + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + count += lib_inv_area; + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs[2]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + ++node_match[leaf].map_refs[2]; + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + ++ctr; + } + return count; + } + + float cut_deref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + auto const& match = matches[ntk.node_to_index( n )][cut->data.match_index]; + float count = node_data.area[phase]; + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> match.permutation[ctr] ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + ++ctr; + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* dereference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( --node_match[leaf].map_refs[1] == 0u ) + count += lib_inv_area; + } + else + { + --node_match[leaf].map_refs[0]; + } + ++ctr; + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */ + if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + count += lib_inv_area; + /* Recursive dereferencing */ + if ( --node_match[leaf].map_refs[2] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + --node_match[leaf].map_refs[2]; + if ( --node_match[leaf].map_refs[leaf_phase] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + ++ctr; + } + return count; + } + + template + inline bool compare_map( float arrival, float best_arrival, float area_flow, float best_area_flow, uint32_t size, uint32_t best_size ) + { + if constexpr ( DO_AREA ) + { + if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + else if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + } + else + { + if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + else if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + } + if ( size < best_size ) + { + return true; + } + return false; + } + +private: + Ntk& ntk; + exact_library const& library; + map_params const& ps; + map_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + double delay{ 0.0f }; /* current delay of the mapping */ + double area{ 0.0f }; /* current area of the mapping */ + const float epsilon{ 0.005f }; /* epsilon */ + + /* lib inverter info */ + float lib_inv_area; + float lib_inv_delay; + + NtkDest const& lib_database; + + std::vector> top_order; + std::vector> node_match; + std::unordered_map>> matches; + network_cuts_t cuts; +}; + +} /* namespace detail */ + +/*! \brief Exact mapping. + * + * This function implements a mapping algorithm using a database of structures. + * It is controlled by a template argument `CutData` (defaulted to + * `cut_enumeration_exact_map_cut`). The argument is similar to the `CutData` argument + * in `cut_enumeration`, which can specialize the cost function to select priority + * cuts and store additional data. The default argument gives priority firstly to + * area flow, then delay, and lastly to the cut size. + * The type passed as `CutData` must implement the following four fields: + * + * - `uint32_t delay` + * - `float flow` + * - `uint8_t match_index` + * - `bool ignore` + * + * See `include/mockturtle/algorithms/cut_enumeration/cut_enumeration_exact_map_cut.hpp` + * for one example of a CutData type that implements the cost function that is used in + * the technology mapper. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a mapped network representation generated using the exact + * synthesis entries in the `exact_library`. This function supports also sequential networks. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Exact library + * \param ps Mapping params + * \param pst Mapping statistics + */ +template +NtkDest map( Ntk& ntk, exact_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_foreach_ri_v == has_create_ri_v, "Ntk and NtkDest networks are not both sequential" ); + static_assert( has_foreach_ro_v == has_create_ro_v, "Ntk and NtkDest networks are not both sequential" ); + + map_stats st; + detail::exact_map_impl p( ntk, library, ps, st ); + auto res = p.run(); + + st.time_total = st.time_mapping + st.cut_enumeration_st.time_total; + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return res; +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/mig_algebraic_rewriting.hpp b/third-party/mockturtle/include/mockturtle/algorithms/mig_algebraic_rewriting.hpp new file mode 100644 index 00000000000..9aa2fce7a92 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/mig_algebraic_rewriting.hpp @@ -0,0 +1,376 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_algebraic_rewriting.hpp + \brief MIG algebraric rewriting + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for mig_algebraic_depth_rewriting. + * + * The data structure `mig_algebraic_depth_rewriting_params` holds configurable + * parameters with default arguments for `mig_algebraic_depth_rewriting`. + */ +struct mig_algebraic_depth_rewriting_params +{ + /*! \brief Rewriting strategy. */ + enum strategy_t + { + /*! \brief DFS rewriting strategy. + * + * Applies depth rewriting once to all output cones whose drivers have + * maximum levels + */ + dfs, + /*! \brief Aggressive rewriting strategy. + * + * Applies depth reduction multiple times until the number of nodes, which + * cannot be rewritten, matches the number of nodes, in the current + * network; or the new network size is larger than the initial size w.r.t. + * to an `overhead`. + */ + aggressive, + /*! \brief Selective rewriting strategy. + * + * Like `aggressive`, but only applies rewriting to nodes on critical paths + * and without `overhead`. + */ + selective + } strategy = dfs; + + /*! \brief Overhead factor in aggressive rewriting strategy. + * + * When comparing to the initial size in aggressive depth rewriting, also the + * number of dangling nodes are taken into account. + */ + float overhead{ 2.0f }; + + /*! \brief Allow area increase while optimizing depth. */ + bool allow_area_increase{ true }; +}; + +/*! \brief Statistics for mig_algebraic_depth_rewriting. */ +struct mig_algebraic_depth_rewriting_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; +}; + +namespace detail +{ + +template +class mig_algebraic_depth_rewriting_impl +{ +public: + mig_algebraic_depth_rewriting_impl( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps, mig_algebraic_depth_rewriting_stats& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + switch ( ps.strategy ) + { + case mig_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case mig_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case mig_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [this]( auto n ) { + reduce_depth( n ); + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + + if ( reduce_depth( n ) ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + if ( !reduce_depth( n ) ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + bool reduce_depth( node const& n ) + { + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_maj( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + ocs2[0] = !ocs2[0]; + ocs2[1] = !ocs2[1]; + ocs2[2] = !ocs2[2]; + } + + if ( auto cand = associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + auto opt = ntk.create_maj( z, assoc ? u : x, ntk.create_maj( x, y, u ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* distributivity */ + if ( ps.allow_area_increase ) + { + auto opt = ntk.create_maj( ocs2[2], + ntk.create_maj( ocs[0], ocs[1], ocs2[0] ), + ntk.create_maj( ocs[0], ocs[1], ocs2[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + } + return true; + } + + using candidate_t = std::tuple, signal, signal, signal, bool>; + std::optional associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + + return std::nullopt; + } + + std::array, 3> ordered_children( node const& n ) const + { + std::array, 3> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } ); + std::stable_sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) { + return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) ); + } ); + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + mig_algebraic_depth_rewriting_params const& ps; + mig_algebraic_depth_rewriting_stats& st; +}; + +} // namespace detail + +/*! \brief Majority algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with majority gates for depth + * optimization using the associativity and distributivity rule in + * majority-of-3 logic. It can be applied to networks other than MIGs, but + * only considers pairs of nodes which both implement the majority-of-3 + * function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_maj` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_maj` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired by an + implementation from Luca Amarù. + \endverbatim + */ +template +void mig_algebraic_depth_rewriting( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps = {}, mig_algebraic_depth_rewriting_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + mig_algebraic_depth_rewriting_stats st; + detail::mig_algebraic_depth_rewriting_impl p( ntk, ps, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/mig_inv_optimization.hpp b/third-party/mockturtle/include/mockturtle/algorithms/mig_inv_optimization.hpp new file mode 100644 index 00000000000..7e0473c6d50 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/mig_inv_optimization.hpp @@ -0,0 +1,394 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_inv_optimization.hpp + \brief MIG inverter optimization + + \author Bugra Eryilmaz + \author Marcel Walter +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../networks/storage.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Statistics for mig_inv_optimization. */ +struct mig_inv_optimization_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Number of one level inverted nodes. */ + int32_t num_inverted{ 0 }; + + /*! \brief Number of two level inverted nodes. */ + int32_t num_two_level_inverted{ 0 }; + + /*! \brief Total gain in terms of number of inverters. */ + int32_t total_gain{ 0 }; +}; + +namespace detail +{ + +template +class mig_inv_optimization_impl +{ +public: + mig_inv_optimization_impl( Ntk& ntk, mig_inv_optimization_stats& st ) + : ntk( ntk ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + minimize(); + } + +private: + /*! \brief implements the inverter minimization algorithm */ + void minimize() + { + bool changed = true; + while ( changed ) + { + changed = false; + ntk.foreach_gate( [this, &changed]( auto const& f ) { + int32_t gain = calculate_gain( f ); + if ( gain > 0 ) + { + st.num_inverted++; + st.total_gain += gain; + changed = true; + invert_node( f ); + } + else if ( two_level_gain( f ) > 0 ) + { + st.num_two_level_inverted++; + st.total_gain += gain; + changed = true; + std::vector> nodes_to_invert{}; + ntk.foreach_fanout( f, [this, &f, &nodes_to_invert]( auto const& parent ) { + // convert each fanout if inverting it makes sense + int32_t sub_gain = 0; + sub_gain += calculate_gain( parent ); + if ( is_complemented_parent( parent, f ) ) + { + // if the connection between f and parent is complemented, we counted the same gain twice which will not be inverted at all + sub_gain -= 2; + } + else + { + // if the connection between f and parent is not complemented, we counted the same negative gain twice which will not be inverted at all + sub_gain += 2; + } + if ( sub_gain > 0 ) + { + st.total_gain += sub_gain; + nodes_to_invert.push_back( parent ); + } + } ); + invert_node( f ); + for ( auto const& n : nodes_to_invert ) + { + invert_node( n ); + } + } + } ); + } + } + + /*! \brief calculates the decrease in the number of inverters if this node is inverted and all fanouts that is beneficial also inverted */ + int32_t two_level_gain( node n ) + { + int32_t gain = 0; + gain += calculate_gain( n ); + + ntk.foreach_fanout( n, [this, &gain, &n]( auto const& f ) { + int32_t sub_gain = 0; + sub_gain += calculate_gain( f ); + if ( is_complemented_parent( f, n ) ) + { + // if the connection between f and parent is complemented, we counted the same gain twice which will not be inverted at all + sub_gain -= 2; + } + else + { + // if the connection between f and parent is not complemented, we counted the same negative gain twice which will not be inverted at all + sub_gain += 2; + } + + // convert each fanout if inverting it makes sense + if ( sub_gain > 0 ) + { + gain += sub_gain; + } + } ); + + return gain; + } + + /*! \brief calculates the decrease in the number of inverters if this node is inverted */ + int32_t calculate_gain( node n ) + { + if ( ntk.is_dead( n ) ) + { + std::cerr << "node" << n << " is dead\n"; + return 0; + } + int32_t gain = 0; + + // count the inverted and non-inverted fanins + ntk.foreach_fanin( n, [this, &gain]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + return; + } + update_gain_is_complemented( f, gain ); + } ); + + // count the inverted and non-inverted fanouts + ntk.foreach_fanout( n, [this, &n, &gain]( auto const& parent ) { + if ( is_complemented_parent( parent, n ) ) + { + gain++; + } + else + { + gain--; + } + } ); + + // count the inverted and non-inverted POs + ntk.foreach_po( [this, &n, &gain]( auto const& f ) { + if ( ntk.get_node( f ) == n ) + { + update_gain_is_complemented( f, gain ); + } + } ); + return gain; + } + + /*! \brief increases the gain if signal f is complemented, decreases otherwise. */ + void update_gain_is_complemented( signal f, int32_t& gain ) + { + if ( ntk.is_complemented( f ) ) + { + gain++; + } + else + { + gain--; + } + } + + /*! \brief checks if parent is parent of child and returns if the connection is complemented. */ + bool is_complemented_parent( node parent, node child ) + { + bool ret = false; + bool changed = false; + ntk.foreach_fanin( parent, [this, &child, &ret, &changed]( auto const& f ) { + if ( ntk.get_node( f ) == child ) + { + changed = true; + ret = ntk.is_complemented( f ); + } + } ); + if ( !changed ) + { + std::cerr << "parent " << parent << " is not parent of child " << child << "\n"; + } + return ret; + } + + /*! \brief inverts the inputs and changes all occurances of the node with the !inverted_node. */ + void invert_node( node n ) + { + signal a, b, c; + ntk.foreach_fanin( n, [&]( auto const& f, auto idx ) { + if ( idx == 0 ) + { + a = f; + } + else if ( idx == 1 ) + { + b = f; + } + else if ( idx == 2 ) + { + c = f; + } + } ); + signal new_node = !create_maj_directly( !a, !b, !c ); + ntk.substitute_node( n, new_node ); + ntk.replace_in_outputs( n, new_node ); + } + + /*! \brief original create_maj function was inverting the node + if more than 2 of the inputs were inverted which is + not suitable for the algorithm, so I removed that part. */ + signal create_maj_directly( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + { + std::swap( b, c ); + } + if ( a.index > b.index ) + { + std::swap( a, b ); + } + } + else + { + if ( b.index > c.index ) + { + std::swap( b, c ); + } + if ( a.index > b.index ) + { + std::swap( a, b ); + } + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + std::shared_ptr>>::element_type::node_type nd; + nd.children[0] = a; + nd.children[1] = b; + nd.children[2] = c; + + /* structural hashing */ + if ( auto const it = ntk._storage->hash.find( nd ); it != ntk._storage->hash.end() ) + { + return { it->second, 0 }; + } + + auto const index = ntk._storage->nodes.size(); + + if ( index >= .9 * ntk._storage->nodes.capacity() ) + { + ntk._storage->nodes.reserve( static_cast( 3.1415f * index ) ); + ntk._storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + ntk._storage->nodes.push_back( nd ); + + ntk._storage->hash[nd] = index; + + /* increase ref-count to children */ + ntk._storage->nodes[a.index].data[0].h1++; + ntk._storage->nodes[b.index].data[0].h1++; + ntk._storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : ntk._events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + +private: + fanout_view ntk; + mig_inv_optimization_stats& st; +}; + +} // namespace detail + +/*! \brief MIG inverter optimization. + * + * This algorithm tries to reduce the number + * of inverters in a MIG network without + * increasing the node number. It checks each + * node for 1 level and 2 level optimization + * opportunuties and inverts the node if it + * decreases the number of inverted connections. + * It does not count constant values as inverted + * even if they are complemented in the graph. + * + * **Required network functions:** + * 'foreach_fanin' + * 'foreach_fanout' + * 'substitute_node' + * 'replace_in_outputs' + * 'is_complemented' + * 'get_node' + * 'is_dead' + * 'is_constant' + * 'foreach_gate' + */ +template +void mig_inv_optimization( Ntk& ntk, mig_inv_optimization_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Ntk is not an MIG network" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_replace_in_outputs_v, "Ntk does not implement the replace_in_outputs method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + mig_inv_optimization_stats st; + detail::mig_inv_optimization_impl p( ntk, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/mig_inv_propagation.hpp b/third-party/mockturtle/include/mockturtle/algorithms/mig_inv_propagation.hpp new file mode 100644 index 00000000000..ce9163bc444 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/mig_inv_propagation.hpp @@ -0,0 +1,374 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_inv_propagation.hpp + \brief MIG inverter optimization + + \author Bugra Eryilmaz + \author Marcel Walter +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../networks/storage.hpp" +#include "../utils/stopwatch.hpp" +#include "./cleanup.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Statistics for mig_inv_propagation. */ +struct mig_inv_propagation_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Increase in the node count. */ + uint32_t node_increase{ 0 }; + + /*! \brief Total gain in terms of number of inverters. */ + uint32_t total_gain{ 0 }; +}; + +namespace detail +{ + +template +class mig_inv_propagation_impl +{ +public: + mig_inv_propagation_impl( Ntk& ntk, mig_inv_propagation_stats& st ) + : ntk( ntk ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + auto const initial_size = number_of_nodes( ntk ); + auto const initial_inverters = number_of_inverters( ntk ); + + propagate(); + + st.node_increase = number_of_nodes( ntk ) - initial_size; + st.total_gain = initial_inverters - number_of_inverters( ntk ); + } + +private: + /*! \brief implements the inverter propagation algorithm */ + void propagate() + { + // starting from primary outputs, propagate the inversions + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + // if it is complemented, invert the node + auto const old_node = ntk.get_node( f ); + auto const new_node = invert_node( old_node ); + + // replace the po with the inverted node + ntk.replace_in_outputs( old_node, new_node ); + + // check if the old node should stay alive + if ( ntk.fanout_size( old_node ) == 0 ) + { + ntk.take_out_node( old_node ); + } + + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( new_node ) ); + } + else + { + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( f ) ); + } + } ); + } + + void propagate_helper( const node n ) + { + std::vector> complement_list{}; + complement_list.reserve( Ntk::max_fanin_size ); + + // for each fanin, check if it is complemented + ntk.foreach_fanin( n, [this, &complement_list]( auto const& f ) { + // skip if it is a constant, PI + if ( ntk.is_constant( ntk.get_node( f ) ) || ntk.is_pi( ntk.get_node( f ) ) ) + { + return; + } + // there should not be a dead child + if ( ntk.is_dead( ntk.get_node( f ) ) ) + { + std::cerr << "node " << ntk.get_node( f ) << " is dead\n"; + } + + // lazy substitute node since child order is fixed and + // changing one child will mix the order and create a bug + // in the foreach_fanin loop + if ( ntk.is_complemented( f ) ) + { + complement_list.push_back( ntk.get_node( f ) ); + } + else + { + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( f ) ); + } + } ); + + // lazy invert the complemented fanins + for ( auto const& f : complement_list ) + { + // for each complemented fanin, invert the node + auto const new_node = invert_node( f ); + // replace the fanin with the inverted node + if ( auto const simplification = ntk.replace_in_node( n, f, new_node ) ) + { + ntk.substitute_node( simplification->first, simplification->second ); + } + + // check if the old node should stay alive + if ( ntk.fanout_size( f ) == 0 ) + { + ntk.take_out_node( f ); + } + + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( new_node ) ); + } + } + + /*! \brief gets maj(a,b,c) returns !maj(!a,!b,!c). */ + signal invert_node( const node n ) + { + signal a, b, c; + + ntk.foreach_fanin( n, [&a, &b, &c]( auto const& f, auto idx ) { + if ( idx == 0 ) + { + a = f; + } + else if ( idx == 1 ) + { + b = f; + } + else if ( idx == 2 ) + { + c = f; + } + } ); + + return !create_maj_directly( !a, !b, !c ); + } + + /** + * \brief The original create_maj function was inverting the node if more than + * 2 of the inputs were inverted which is not suitable for the algorithm, + * so I removed that part. + */ + signal create_maj_directly( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + { + std::swap( b, c ); + } + if ( a.index > b.index ) + { + std::swap( a, b ); + } + } + else + { + if ( b.index > c.index ) + { + std::swap( b, c ); + } + if ( a.index > b.index ) + { + std::swap( a, b ); + } + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + std::shared_ptr>>::element_type::node_type nd; + nd.children[0] = a; + nd.children[1] = b; + nd.children[2] = c; + + /* structural hashing */ + auto const it = ntk._storage->hash.find( nd ); + if ( it != ntk._storage->hash.end() ) + { + return { it->second, 0 }; + } + + auto const index = ntk._storage->nodes.size(); + + if ( index >= .9 * ntk._storage->nodes.capacity() ) + { + ntk._storage->nodes.reserve( static_cast( 3.1415f * index ) ); + ntk._storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + ntk._storage->nodes.push_back( nd ); + + ntk._storage->hash[nd] = index; + + /* increase ref-count to children */ + ntk._storage->nodes[a.index].data[0].h1++; + ntk._storage->nodes[b.index].data[0].h1++; + ntk._storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : ntk._events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + uint32_t number_of_inverters( Ntk const& ntk ) const + { + uint32_t num_inverters{ 0 }; + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_dead( ntk.get_node( f ) ) ) + { + return; + } + if ( ntk.is_constant( ntk.get_node( f ) ) || ntk.is_pi( ntk.get_node( f ) ) ) + { + return; + } + if ( ntk.is_complemented( f ) ) + { + ++num_inverters; + } + } ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + ++num_inverters; + } + } ); + + return num_inverters; + } + /** + * \brief Determines the number of nodes in the given network that are actually alive. + */ + uint64_t number_of_nodes( Ntk const& ntk ) const + { + uint64_t nodes{}; + + ntk.foreach_node( [&nodes]( auto const ) { + ++nodes; + } ); + + return nodes; + } + +private: + Ntk& ntk; + mig_inv_propagation_stats& st; +}; + +} // namespace detail + +/*! \brief MIG inverter propagation. + * + * This algorithm tries to push all + * the inverters to the inputs. + * However, it can increase the number + * of nodes while doing so. + * + * **Required network functions:** + * get_node + * substitute_node + * take_out_node + * foreach_fanin + * replace_in_node + * fanout_size + * is_complemented + * is_dead + * is_constant + * is_pi + * foreach_po + */ +template +void mig_inv_propagation( Ntk& ntk, mig_inv_propagation_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Ntk is not an MIG network" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_take_out_node_v, "Ntk does not implement the take_out_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_replace_in_node_v, "Ntk does not implement the replace_in_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + + mig_inv_propagation_stats st; + detail::mig_inv_propagation_impl p( ntk, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/mig_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/mig_resub.hpp new file mode 100644 index 00000000000..b4a1355d1ac --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/mig_resub.hpp @@ -0,0 +1,875 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_resub.hpp + \brief Majority-specific resustitution rules + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../utils/index_list/index_list.hpp" +#include "../utils/truth_table_utils.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/mig_resyn.hpp" + +#include + +namespace mockturtle +{ + +struct mig_enumerative_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for relevance resub */ + stopwatch<>::duration time_resubR{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted relevance resubsitutions */ + uint32_t num_divR_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: mig_enumerative_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] R-resub {:6d} ({:>5.2f} secs)\n", + num_divR_accepts, to_seconds( time_resubR ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} = {:6d} MAJ ({:>5.2f} secs)\n", + num_div1_accepts, num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + std::cout << fmt::format( "[i] 2-resub {:6d} = {:6d} 2MAJ ({:>5.2f} secs)\n", + num_div2_accepts, num_div2_accepts, to_seconds( time_resub2 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_divR_accepts + num_div1_accepts + num_div2_accepts ) ); + } +}; /* mig_enumerative_resub_stats */ + +template +struct mig_enumerative_resub_functor +{ +public: + using node = mig_network::node; + using signal = mig_network::signal; + using stats = mig_enumerative_resub_stats; + + struct unate_divisors + { + std::vector u0; + std::vector u1; + std::vector next_candidates; + + void clear() + { + u0.clear(); + u1.clear(); + next_candidates.clear(); + } + }; + + struct binate_divisors + { + std::vector b0; + std::vector b1; + std::vector b2; + + void clear() + { + b0.clear(); + b1.clear(); + b2.clear(); + } + }; + +public: + explicit mig_enumerative_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider relevance optimization */ + g = call_with_stopwatch( st.time_resubR, [&]() { + return resub_divR( root, required ); + } ); + if ( g ) + { + ++st.num_divR_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, required ); + } ); + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + if ( max_inserts == 1 || num_mffc == 2 ) + return std::nullopt; + + /* collect level two divisors */ + call_with_stopwatch( st.time_collect_binate_divisors, [&]() { + collect_binate_divisors( root, required ); + } ); + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, required ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = num_mffc - 2; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + continue; /* next */ + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + std::optional resub_divR( node const& root, uint32_t required ) + { + (void)required; + + std::vector fs; + ntk.foreach_fanin( root, [&]( const auto& f ) { + fs.emplace_back( f ); + } ); + + for ( auto i = 0u; i < divs.size(); ++i ) + { + auto const& d0 = divs.at( i ); + auto const& s = ntk.make_signal( d0 ); + auto const& tt = sim.get_tt( s ); + + if ( d0 == root ) + break; + + auto const tt0 = sim.get_tt( fs[0] ); + auto const tt1 = sim.get_tt( fs[1] ); + auto const tt2 = sim.get_tt( fs[2] ); + + if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ); + } + else if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( ~tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( ~tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( ~tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ); + } + } + + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t required ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + auto const& one = sim.get_tt( ntk.get_constant( true ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d0 = divs.at( i ); + if ( ntk.level( d0 ) > required - 1 ) + continue; + auto const& tt_s0 = sim.get_tt( ntk.make_signal( d0 ) ); + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const d1 = divs.at( j ); + if ( ntk.level( d1 ) > required - 1 ) + continue; + auto const& tt_s1 = sim.get_tt( ntk.make_signal( d1 ) ); + + /* Boolean filtering rule for MAJ-3 */ + if ( kitty::ternary_majority( tt_s0, tt_s1, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt ) == tt ) + { + udivs.u0.emplace_back( !ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( kitty::ternary_majority( tt_s0, ~tt_s1, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( !ntk.make_signal( d1 ) ); + continue; + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d1 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d1 ) ); + } + + if constexpr ( use_constant ) /* allowing "not real" MAJ gates (one fanin is constant) */ + { + if ( kitty::ternary_majority( tt_s0, one, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.get_constant( true ) ); + continue; + } + + if ( kitty::ternary_majority( ~tt_s0, one, tt ) == tt ) + { + udivs.u0.emplace_back( !ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.get_constant( true ) ); + continue; + } + + if ( kitty::ternary_majority( tt_s0, ~one, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.get_constant( false ) ); + continue; + } + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d0 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d0 ) ); + } + + if constexpr ( use_constant ) + { + udivs.next_candidates.emplace_back( ntk.get_constant( true ) ); + } + } + + std::optional resub_div1( node const& root, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + for ( auto i = 0u; i < udivs.u0.size(); ++i ) + { + auto const s0 = udivs.u0.at( i ); + auto const s1 = udivs.u1.at( i ); + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + for ( auto j = i + 1; j < udivs.u0.size(); ++j ) + { + auto s2 = udivs.u0.at( j ); + auto tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + return sim.get_phase( root ) ? !ntk.create_maj( a, b, c ) : ntk.create_maj( a, b, c ); + } + + s2 = udivs.u1.at( j ); + tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + return sim.get_phase( root ) ? !ntk.create_maj( a, b, c ) : ntk.create_maj( a, b, c ); + } + } + } + + return std::nullopt; + } + + void collect_binate_divisors( node const& root, uint32_t required ) + { + bdivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < udivs.next_candidates.size(); ++i ) + { + auto const& s0 = udivs.next_candidates.at( i ); + if ( ntk.level( ntk.get_node( s0 ) ) > required - 2 ) + continue; + + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = i + 1; j < udivs.next_candidates.size(); ++j ) + { + auto const& s1 = udivs.next_candidates.at( j ); + if ( ntk.level( ntk.get_node( s1 ) ) > required - 2 ) + continue; + + auto const& tt_s1 = sim.get_tt( s1 ); + + for ( auto k = j + 1; k < udivs.next_candidates.size(); ++k ) + { + auto const& s2 = udivs.next_candidates.at( k ); + if ( ntk.level( ntk.get_node( s2 ) ) > required - 2 ) + continue; + + auto const& tt_s2 = sim.get_tt( s2 ); + + /* Note: the implication relation is actually not necessary for majority; this is an over-filtering */ + if ( kitty::implies( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( tt_s0, ~tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( tt_s0, tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, ~tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( tt_s0, ~tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, ~tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + } + } + } + } + + std::optional resub_div2( node const& root, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + for ( auto i = 0u; i < udivs.u0.size(); ++i ) + { + auto const& s0 = udivs.u0.at( i ); + auto const& s1 = udivs.u1.at( i ); + + for ( auto j = 0u; j < bdivs.b0.size(); ++j ) + { + auto const& s2 = bdivs.b0.at( j ); + auto const& s3 = bdivs.b1.at( j ); + auto const& s4 = bdivs.b2.at( j ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto const d = sim.get_phase( ntk.get_node( s3 ) ) ? !s3 : s3; + auto const e = sim.get_phase( ntk.get_node( s4 ) ) ? !s4 : s4; + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + auto const& tt_s3 = sim.get_tt( s3 ); + auto const& tt_s4 = sim.get_tt( s4 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, kitty::ternary_majority( tt_s2, tt_s3, tt_s4 ) ) == tt ) + { + return sim.get_phase( root ) ? !ntk.create_maj( a, b, ntk.create_maj( c, d, e ) ) : ntk.create_maj( a, b, ntk.create_maj( c, d, e ) ); + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; + binate_divisors bdivs; +}; /* mig_enumerative_resub_functor */ + +struct mig_resyn_resub_stats +{ + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_compute_function{ 0 }; + + /*! \brief Number of found solutions. */ + uint32_t num_success{ 0 }; + + /*! \brief Number of times that no solution can be found. */ + uint32_t num_fail{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] #solution = {:6d}\n", num_success ); + fmt::print( "[i] #invoke = {:6d}\n", num_success + num_fail ); + fmt::print( "[i] engine time: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + } +}; /* mig_resyn_resub_stats */ + +/*! \brief Interfacing resubstitution functor with MIG resynthesis engines for `window_based_resub_engine`. + */ +template> +struct mig_resyn_functor +{ +public: + using node = mig_network::node; + using signal = mig_network::signal; + using stats = mig_resyn_resub_stats; + using TT = typename ResynEngine::truth_table_t; + + static_assert( std::is_same_v, "truth table type of the simulator does not match" ); + +public: + explicit mig_resyn_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), tts( ntk ), divs( divs ), st( st ) + { + assert( divs.size() == num_divs ); + (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TTcare care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)required; + TT target = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + TT care_transformed = target.construct(); + care_transformed = care; + + typename ResynEngine::stats st_eng; + ResynEngine engine( st_eng ); + for ( auto const& d : divs ) + { + div_signals.emplace_back( sim.get_phase( d ) ? !ntk.make_signal( d ) : ntk.make_signal( d ) ); + tts[d] = sim.get_tt( div_signals.back() ); + } + + auto const res = call_with_stopwatch( st.time_compute_function, [&]() { + return engine( target, care_transformed, divs.begin(), divs.end(), tts, std::min( potential_gain - 1, max_inserts ) ); + } ); + if ( res ) + { + ++st.num_success; + signal ret; + real_gain = potential_gain - ( *res ).num_gates(); + insert( ntk, div_signals.begin(), div_signals.end(), *res, [&]( signal const& s ) { ret = s; } ); + return ret; + } + else + { + ++st.num_fail; + return std::nullopt; + } + } + +private: + Ntk& ntk; + Simulator const& sim; + unordered_node_map tts; + std::vector const& divs; + std::vector div_signals; + stats& st; +}; /* mig_resyn_functor */ + +/*! \brief MIG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from mig_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void mig_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not mig_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + if ( ps.max_pis == 8 ) + { + using truthtable_t = kitty::static_truth_table<8u>; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_enumerative_resub_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_enumerative_resub_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +/*! \brief MIG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from mig_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void mig_resubstitution2( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not mig_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_resyn_functor, truthtable_dc_t>; + + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/miter.hpp b/third-party/mockturtle/include/mockturtle/algorithms/miter.hpp new file mode 100644 index 00000000000..1eb82631b7e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/miter.hpp @@ -0,0 +1,113 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file miter.hpp + \brief Generate miter from two networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" +#include "cleanup.hpp" + +namespace mockturtle +{ + +/*! \brief Creates a combinational miter from two networks. + * + * This method combines two networks that have the same number of primary + * inputs and the same number of primary outputs into a miter. The miter + * has the same number of inputs and one primary output. This output is the + * OR of XORs of all primary output pairs. In other words, the miter outputs + * 1 for all input assignments in which the two input networks differ. + * + * All networks may have different types. The method returns an optional, which + * is `nullopt`, whenever the two input networks don't match in their number of + * primary inputs and primary outputs. + */ +template +std::optional miter( NtkSource1 const& ntk1, NtkSource2 const& ntk2 ) +{ + static_assert( is_network_type_v, "NtkSource1 is not a network type" ); + static_assert( is_network_type_v, "NtkSource2 is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_num_pis_v, "NtkSource1 does not implement the num_pis method" ); + static_assert( has_num_pos_v, "NtkSource1 does not implement the num_pos method" ); + static_assert( has_num_pis_v, "NtkSource2 does not implement the num_pis method" ); + static_assert( has_num_pos_v, "NtkSource2 does not implement the num_pos method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_xor_v, "NtkDest does not implement the create_xor method" ); + static_assert( has_create_nary_or_v, "NtkDest does not implement the create_nary_or method" ); + + /* both networks must have same number of inputs and outputs */ + if ( ( ntk1.num_pis() != ntk2.num_pis() ) || ( ntk1.num_pos() != ntk2.num_pos() ) ) + { + return std::nullopt; + } + + /* create primary inputs */ + NtkDest dest; + std::vector> pis; + for ( auto i = 0u; i < ntk1.num_pis(); ++i ) + { + pis.push_back( dest.create_pi() ); + } + + /* copy networks */ + const auto pos1 = cleanup_dangling( ntk1, dest, pis.begin(), pis.end() ); + const auto pos2 = cleanup_dangling( ntk2, dest, pis.begin(), pis.end() ); + + if constexpr ( has_EXODC_interface_v ) + { + ntk1.build_oe_miter( dest, pos1, pos2 ); + return dest; + } + if constexpr ( has_EXODC_interface_v ) + { + ntk2.build_oe_miter( dest, pos1, pos2 ); + return dest; + } + + /* create XOR of output pairs */ + std::vector> xor_outputs; + std::transform( pos1.begin(), pos1.end(), pos2.begin(), std::back_inserter( xor_outputs ), + [&]( auto const& o1, auto const& o2 ) { return dest.create_xor( o1, o2 ); } ); + + /* create big OR of XOR gates */ + dest.create_po( dest.create_nary_or( xor_outputs ) ); + + return dest; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/network_fuzz_tester.hpp b/third-party/mockturtle/include/mockturtle/algorithms/network_fuzz_tester.hpp new file mode 100644 index 00000000000..48ae5c9895c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/network_fuzz_tester.hpp @@ -0,0 +1,260 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file network_fuzz_tester.hpp + \brief Network fuzz tester + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#include "../io/aiger_reader.hpp" +#include "../io/verilog_reader.hpp" +#include "../io/write_aiger.hpp" +#include "../io/write_verilog.hpp" +#include "../utils/stopwatch.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for testcase_minimizer. */ +struct fuzz_tester_params +{ + /*! \brief File format to be generated. */ + enum + { + verilog, + aiger + } file_format = verilog; + + /*! \brief Name of the generated testcase file. */ + std::string filename{ "fuzz_test.v" }; + + /*! \brief Filename written out by the command (to do CEC with the input testcase). */ + std::optional outputfile{ std::nullopt }; + + /*! \brief Max number of networks to test: nullopt means infinity. */ + std::optional num_iterations{ std::nullopt }; + + /*! \brief Timeout in seconds: nullopt means infinity. */ + std::optional timeout{ std::nullopt }; +}; /* fuzz_tester_params */ + +/*! \brief Network fuzz tester + * + * Runs an algorithm on many small random logic networks. Fuzz + * testing is often useful to detect potential segmentation faults in + * new implementations. The generated benchmarks are saved first in a + * file. If a segmentation fault or unexpected behavior occurs, the + * file can be used to reproduce and debug the problem. + * + * The entry function `run` generates different networks with the same + * number of PIs and gates. The function `run_incremental`, on the other + * hand, generates networks of increasing sizes. These functions return + * true if it was terminated by an unexpected behavior, or return false + * if it terminates normally after the specified number of iterations + * without observing any defect. + * + * The script of algorithm(s) to be tested can be provided as (1) a + * lambda function taking a network as input and returning a Boolean, + * which is true if the algorithm behaves as expected; or (2) a lambda + * function making a command string to be called, taking a filename string + * as input (not supported on Windows platform). If the command exits + * normally (with return value 0), CEC will be performed on the output + * file; otherwise (segfault, assertion fail, or return value is not 0), + * the fuzzer is terminated. + * + \verbatim embed:rst + + Usage + + .. code-block:: c++ + + #include + #include + #include + #include + #include + + auto opt = [&]( aig_network aig ) -> bool { + resubstitution_params ps; + resubstitution_stats st; + aig_resubstitution( aig, ps, &st ); + aig = cleanup_dangling( aig ); + return true; + }; + + fuzz_tester_params ps; + ps.num_iterations = 100; + auto gen = default_random_aig_generator(); + network_fuzz_tester fuzzer( gen, ps ); + fuzzer.run( opt ); + \endverbatim +*/ +template +class network_fuzz_tester +{ +public: + explicit network_fuzz_tester( NetworkGenerator& gen, fuzz_tester_params const ps = {} ) + : gen( gen ), ps( ps ) + {} + +#ifndef _MSC_VER + uint64_t run( std::function&& make_command ) + { + return run( make_callback( make_command ) ); + } +#endif + + uint64_t run( std::function&& fn ) + { + uint64_t counter{ 0 }; + stopwatch<>::duration time{ 0 }; + while ( ( !ps.num_iterations || counter < ps.num_iterations ) && + ( !ps.timeout || to_seconds( time ) < ps.timeout ) ) + { + stopwatch t( time ); + auto ntk = gen.generate(); + fmt::print( "[i] create network #{}: I/O = {}/{} gates = {} nodes = {}, write into `{}`\n", + ++counter, ntk.num_pis(), ntk.num_pos(), ntk.num_gates(), ntk.size(), ps.filename ); + + switch ( ps.file_format ) + { + case fuzz_tester_params::verilog: + write_verilog( ntk, ps.filename ); + break; + case fuzz_tester_params::aiger: + write_aiger( ntk, ps.filename ); + break; + default: + fmt::print( "[w] unsupported format\n" ); + return 0; + } + + /* run optimization algorithm */ + if ( !fn( ntk ) ) + { + return counter; + } + + if ( ps.outputfile ) + { + if ( !abc_cec() ) + return counter; + } + } + return 0; + } + +private: +#ifndef _MSC_VER + inline std::function make_callback( std::function& make_command ) + { + std::function fn = [&]( Ntk ntk ) -> bool { + (void)ntk; + int status = std::system( make_command( ps.filename ).c_str() ); + if ( status < 0 ) + { + std::cout << "[e] Unexpected error when calling command: " << strerror( errno ) << '\n'; + return false; + } + else + { + if ( WIFEXITED( status ) ) + { + if ( WEXITSTATUS( status ) == 0 ) // normal + { + if ( ps.outputfile ) + return abc_cec(); + return true; + } + else if ( WEXITSTATUS( status ) == 1 || WEXITSTATUS( status ) == 134 ) // buggy or assertion fail + { + return false; + } + else + { + std::cout << "[e] Unexpected return value: " << WEXITSTATUS( status ) << '\n'; + return false; + } + } + else // segfault + { + return false; + } + } + }; + return fn; + } +#endif + + inline bool abc_cec() + { + std::string command = fmt::format( "abc -q \"cec -n {} {}\"", ps.filename, *ps.outputfile ); + + std::array buffer; + std::string result; +#ifdef _MSC_VER + std::unique_ptr pipe( _popen( command.c_str(), "r" ), _pclose ); +#else + std::unique_ptr pipe( popen( command.c_str(), "r" ), pclose ); +#endif + if ( !pipe ) + { + throw std::runtime_error( "popen() failed" ); + } + while ( fgets( buffer.data(), buffer.size(), pipe.get() ) != nullptr ) + { + result += buffer.data(); + } + + /* search for one line which says "Networks are equivalent" and ignore all other debug output from ABC */ + std::stringstream ss( result ); + std::string line; + while ( std::getline( ss, line, '\n' ) ) + { + if ( line.size() >= 23u && line.substr( 0u, 23u ) == "Networks are equivalent" ) + { + return true; + } + } + + fmt::print( "[e] Files are not equivalent\n" ); + return false; + } + +private: + NetworkGenerator& gen; + fuzz_tester_params const ps; +}; /* network_fuzz_tester */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis.hpp new file mode 100644 index 00000000000..743d1926dd0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis.hpp @@ -0,0 +1,357 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file node_resynthesis.hpp + \brief Node resynthesis + + \author Heinz Riener + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for node_resynthesis. + * + * The data structure `node_resynthesis_params` holds configurable parameters + * with default arguments for `node_resynthesis`. + */ +struct node_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for node_resynthesis. + * + * The data structure `node_resynthesis_stats` provides data collected by + * running `node_resynthesis`. + */ +struct node_resynthesis_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class node_resynthesis_impl +{ +public: + node_resynthesis_impl( NtkDest& ntk_dest, NtkSource const& ntk, ResynthesisFn&& resynthesis_fn, node_resynthesis_params const& ps, node_resynthesis_stats& st ) + : ntk_dest( ntk_dest ), + ntk( ntk ), + resynthesis_fn( resynthesis_fn ), + ps( ps ), + st( st ) + { + } + + NtkDest run() + { + stopwatch t( st.time_total ); + + node_map, NtkSource> node2new( ntk ); + + /* map constants */ + node2new[ntk.get_node( ntk.get_constant( false ) )] = ntk_dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + node2new[ntk.get_node( ntk.get_constant( true ) )] = ntk_dest.get_constant( true ); + } + + /* map primary inputs */ + ntk.foreach_pi( [&]( auto n ) { + node2new[n] = ntk_dest.create_pi(); + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk.get_name( ntk.make_signal( n ) ) ); + } + } ); + + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + ntk.foreach_ro( [&]( auto n, auto i ) { + node2new[n] = ntk_dest.create_ro(); + ntk_dest.set_register( i, ntk.register_at( i ) ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk.get_name( ntk.make_signal( n ) ) ); + } + } ); + } + + /* map nodes */ + topo_view ntk_topo{ ntk }; + ntk_topo.foreach_node( [&]( auto n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + return; + + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( ntk.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f] ); + } ); + + bool performed_resyn = false; + resynthesis_fn( ntk_dest, ntk.node_function( n ), children.begin(), children.end(), [&]( auto const& f ) { + node2new[n] = f; + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + ntk_dest.set_name( f, ntk.get_name( ntk.make_signal( n ) ) ); + } + + performed_resyn = true; + return false; + } ); + + if ( !performed_resyn ) + { + fmt::print( "[e] could not perform resynthesis for node {} in node_resynthesis\n", ntk.node_to_index( n ) ); + std::abort(); + } + } ); + + /* map primary outputs */ + ntk.foreach_po( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_po( o ); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk.has_output_name( index ) ) + { + ntk_dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } + } ); + + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_ri( o ); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk.has_output_name( index ) ) + { + ntk_dest.set_output_name( index + ntk.num_pos(), ntk.get_output_name( index + ntk.num_pos() ) ); + } + } + } ); + } + + return ntk_dest; + } + +private: + NtkDest& ntk_dest; + NtkSource const& ntk; + ResynthesisFn&& resynthesis_fn; + node_resynthesis_params const& ps; + node_resynthesis_stats& st; +}; + +} /* namespace detail */ + +/*! \brief Node resynthesis algorithm. + * + * This algorithm takes as input a network (of type `NtkSource`) and creates a + * new network (of type `NtkDest`), by translating each node of the input + * network into a subnetwork for the output network. To find a new subnetwork, + * the algorithm uses a resynthesis function that takes as input the input + * node's truth table. This algorithm can for example be used to translate + * k-LUT networks into AIGs or MIGs. + * + * The resynthesis function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `get_node` + * - `get_constant` + * - `foreach_pi` + * - `foreach_node` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `foreach_fanin` + * - `node_function` + * - `foreach_po` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_not` + * - `create_po` + * + * \param ntk Input network of type `NtkSource` + * \param resynthesis_fn Resynthesis function + * \return An equivalent network of type `NtkDest` + */ +template +NtkDest node_resynthesis( NtkSource const& ntk, ResynthesisFn&& resynthesis_fn, node_resynthesis_params const& ps = {}, node_resynthesis_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_foreach_fanin_v, "NtkSource does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "NtkSource does not implement the node_function method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + + node_resynthesis_stats st; + NtkDest ntk_dest; + detail::node_resynthesis_impl p( ntk_dest, ntk, resynthesis_fn, ps, st ); + const auto ret = p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return ret; +} + +/*! \brief Node resynthesis algorithm. + * + * This algorithm takes as input a network (of type `NtkSource`) and creates a + * new network (of type `NtkDest`), by translating each node of the input + * network into a subnetwork for the output network. To find a new subnetwork, + * the algorithm uses a resynthesis function that takes as input the input + * node's truth table. This algorithm can for example be used to translate + * k-LUT networks into AIGs or MIGs. + * + * The resynthesis function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `get_node` + * - `get_constant` + * - `foreach_pi` + * - `foreach_node` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `foreach_fanin` + * - `node_function` + * - `foreach_po` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_not` + * - `create_po` + * + * \param ntk_dest Output network of type `NtkDest` + * \param ntk Input network of type `NtkSource` + * \param resynthesis_fn Resynthesis function + */ +template +void node_resynthesis( NtkDest& ntk_dest, NtkSource const& ntk, ResynthesisFn&& resynthesis_fn, node_resynthesis_params const& ps = {}, node_resynthesis_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_foreach_fanin_v, "NtkSource does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "NtkSource does not implement the node_function method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + + node_resynthesis_stats st; + detail::node_resynthesis_impl p( ntk_dest, ntk, resynthesis_fn, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/akers.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/akers.hpp new file mode 100644 index 00000000000..8f20f1b5dc2 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/akers.hpp @@ -0,0 +1,76 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file akers.hpp + \brief Resynthesis with Akers synthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../../algorithms/akers_synthesis.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on Akers synthesis. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + akers_resynthesis resyn; + const auto mig = node_resynthesis( klut, resyn ); + \endverbatim + */ +template +class akers_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + fn( akers_synthesis( ntk, function, ~function.construct(), begin, end ) ); + } +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/bidecomposition.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/bidecomposition.hpp new file mode 100644 index 00000000000..270c4124c97 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/bidecomposition.hpp @@ -0,0 +1,82 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bidecomposition.hpp + \brief Resynthesis with bi_decomposition + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../../algorithms/bi_decomposition.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on bi-decomposition + * + * This resynthesis function can be passed to ``refactoring``. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const xag_network xag = ...; + bidecomposition_resynthesis resyn; + const auto xag = refactoring( xag, resyn ); + \endverbatim + */ + +template +class bidecomposition_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dc, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + fn( bi_decomposition( ntk, function, ~dc, { begin, end } ) ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + operator()( ntk, function, function.construct(), begin, end, fn ); + } +}; +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/cached.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/cached.hpp new file mode 100644 index 00000000000..738dc53690c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/cached.hpp @@ -0,0 +1,317 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cached.hpp + \brief Generic resynthesis with a cache + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#if !__clang__ || __clang_major__ > 10 + +#include +#if __GNUC__ == 7 +#include +#else +#include +#endif +#include +#include +#include +#include +#include + +#include "../../algorithms/cleanup.hpp" +#include "../../traits.hpp" +#include "../../utils/json_utils.hpp" +#include "../../utils/network_cache.hpp" +#include "traits.hpp" + +namespace mockturtle +{ + +struct no_blacklist_cache_info +{ + bool retry( no_blacklist_cache_info const& old_info ) const + { + (void)old_info; + return false; + } +}; + +inline void to_json( nlohmann::json& j, no_blacklist_cache_info const& info ) +{ + (void)info; + j = nullptr; +} + +inline void from_json( nlohmann::json const& j, no_blacklist_cache_info& info ) +{ + (void)j; + (void)info; +} + +template +class cached_resynthesis +{ +public: + explicit cached_resynthesis( ResynthesisFn const& resyn_fn, uint32_t max_pis, std::string const& cache_filename = {}, BlacklistCacheInfo const& blacklist_cache_info = {} ) + : _resyn_fn( resyn_fn ), + _cache( max_pis ), + _cache_filename( cache_filename ), + _blacklist_cache_info( blacklist_cache_info ), + _initial_size( max_pis ) + { + if ( !_cache_filename.empty() ) + { + load(); + } + } + + ~cached_resynthesis() + { + if ( !_cache_filename.empty() ) + { + save(); + } + } + +private: + using cache_key_t = std::pair>; + + struct cache_hash + { + std::size_t operator()( const cache_key_t& p ) const + { + auto seed = _h( p.first ); + std::for_each( p.second.begin(), p.second.end(), [&]( auto const& tt ) { kitty::hash_combine( seed, _h( tt ) ); } ); + return seed; + } + + private: + kitty::hash _h; + }; + + using blacklist_cache_key_t = std::pair; + + struct blacklist_cache_hash + { + std::size_t operator()( const blacklist_cache_key_t& p ) const + { + return _h( p.first ); + } + + private: + kitty::hash _h; + }; + + struct blacklist_cache_equal + { + bool operator()( const blacklist_cache_key_t& lhs, const blacklist_cache_key_t& rhs ) const + { + return lhs.first == rhs.first; + } + }; + + bool is_blacklisted( kitty::dynamic_truth_table const& tt ) const + { + auto it = _blacklist_cache.find( { tt, _blacklist_cache_info } ); + + /* function cannot be found in black list cache */ + if ( it == _blacklist_cache.end() ) + { + return false; + } + /* newer black list info, erase old entry from cache */ + else if ( _blacklist_cache_info.retry( it->second ) ) + { + _blacklist_cache.erase( it ); + return false; + } + /* function is black listed */ + else + { + return true; + } + } + +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( auto const key = std::make_pair( function, _existing_functions ); + _cache.has( key ) ) + { + ++_cache_hits; + std::vector> signals( _cache.pis().size(), ntk.get_constant( false ) ); + std::copy( begin, end, signals.begin() ); + std::copy( _existing_signals.begin(), _existing_signals.end(), signals.begin() + _initial_size ); + fn( cleanup_dangling( _cache.get_view( key ), ntk, signals.begin(), signals.end() ).front() ); + } + else if ( is_blacklisted( function ) ) + { + ++_cache_hits; + return; /* do nothing */ + } + else + { + bool found_one = false; + auto on_signal = [&]( signal const& f ) -> bool { + if ( !found_one ) + { + ++_cache_misses; + _cache.insert_signal( key, f ); + found_one = true; + + std::vector> signals( _cache.pis().size(), ntk.get_constant( false ) ); + std::copy( begin, end, signals.begin() ); + std::copy( _existing_signals.begin(), _existing_signals.end(), signals.begin() + _initial_size ); + fn( cleanup_dangling( _cache.get_view( key ), ntk, signals.begin(), signals.end() ).front() ); + } + return false; + }; + + _resyn_fn( _cache.network(), function, _cache.pis().begin(), _cache.pis().begin() + function.num_vars(), on_signal ); + + if ( !found_one ) + { + _blacklist_cache.insert( { function, _blacklist_cache_info } ); + } + } + } + + void set_bounds( std::optional const& lower_bound, std::optional const& upper_bound ) + { + if constexpr ( has_set_bounds_v ) + { + _resyn_fn.set_bounds( lower_bound, upper_bound ); + } + } + + void clear_functions() + { + if constexpr ( has_clear_functions_v ) + { + _existing_signals.clear(); + _existing_functions.clear(); + _resyn_fn.clear_functions(); + } + } + + void add_function( signal const& s, kitty::dynamic_truth_table const& tt ) + { + if constexpr ( has_add_function_v ) + { + // index of cache PI to forward + const auto pi_index = _initial_size + _existing_signals.size(); + + _existing_signals.push_back( s ); + _existing_functions.push_back( tt ); + _cache.ensure_pis( _initial_size + _existing_functions.size() ); + + _resyn_fn.add_function( _cache.pis()[pi_index], tt ); + } + else + { + // TODO assert or warn? + } + } + + void report() const + { + fmt::print( "[i] cache hits = {}\n", _cache_hits ); + fmt::print( "[i] cache misses = {}\n", _cache_misses ); + fmt::print( "[i] size of cache = {}\n", _cache.size() ); + fmt::print( "[i] size of blacklist cache = {}\n", _blacklist_cache.size() ); + } + +private: + void load() + { + std::ifstream is( _cache_filename.c_str(), std::ifstream::in ); + if ( !is.good() ) + return; + nlohmann::json data; + is >> data; + + _cache.insert_json( data["cache"] ); + data["blacklist_cache"].get_to( _blacklist_cache ); + data["initial_size"].get_to( _initial_size ); + } + + void save() + { +#if __GNUC__ == 7 + namespace fs = std::experimental::filesystem::v1; +#else + namespace fs = std::filesystem; +#endif + + nlohmann::json data{ + { "cache", _cache.to_json() }, + { "blacklist_cache", _blacklist_cache }, + { "initial_size", _initial_size } }; + + // make a backup of existing cache file, if it exists + std::string _backup_filename = fmt::format( "{}.bak", _cache_filename ); + if ( fs::exists( _cache_filename ) ) + { + fs::copy( _cache_filename, _backup_filename ); + } + + std::ofstream os( _cache_filename.c_str(), std::ofstream::out ); + os << data.dump() << "\n"; + os.close(); + + if ( fs::exists( _backup_filename ) ) + { + fs::remove( _backup_filename ); + } + } + +private: + ResynthesisFn _resyn_fn; + mutable network_cache _cache; + mutable std::unordered_set _blacklist_cache; + std::string _cache_filename; + BlacklistCacheInfo _blacklist_cache_info; + uint32_t _initial_size{}; + + std::vector _existing_functions; + std::vector> _existing_signals; + + /* statistics */ + mutable uint32_t _cache_hits{}; + mutable uint32_t _cache_misses{}; +}; +} /* namespace mockturtle */ + +#endif diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/composed.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/composed.hpp new file mode 100644 index 00000000000..7219c3c148c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/composed.hpp @@ -0,0 +1,83 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file composed.hpp + \brief Traits for additional node_resynthesis methods + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#if !__clang__ || __clang_major__ > 10 + +#include +#include + +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "cached.hpp" +#include "exact.hpp" + +namespace mockturtle +{ + +struct exact_blacklist_cache_info +{ + bool retry( exact_blacklist_cache_info const& old_info ) const + { + return conflict_limit > old_info.conflict_limit; + } + + int conflict_limit; +}; + +inline void to_json( nlohmann::json& j, exact_blacklist_cache_info const& info ) +{ + j = info.conflict_limit; +} + +inline void from_json( nlohmann::json const& j, exact_blacklist_cache_info& info ) +{ + j.get_to( info.conflict_limit ); +} + +template +auto cached_exact_xag_resynthesis( std::string const& cache_filename, uint32_t input_limit = 12u, int conflict_limit = 10e5 ) +{ + exact_resynthesis_params exact_ps; + exact_ps.conflict_limit = conflict_limit; + exact_aig_resynthesis exact_resyn( std::is_same_v, exact_ps ); + exact_blacklist_cache_info info; + info.conflict_limit = conflict_limit; + return cached_resynthesis( exact_resyn, input_limit, cache_filename, info ); +} + +} // namespace mockturtle + +#endif \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/davio.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/davio.hpp new file mode 100644 index 00000000000..064f5ad2504 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/davio.hpp @@ -0,0 +1,150 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file davio.hpp + \brief Use Davio decomposition for resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../../traits.hpp" +#include "../decomposition.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on Davio decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on Shanon decomposition. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + positive_davio_resynthesis resyn; + auto xag = node_resynthesis( klut, resyn ); + \endverbatim + * + */ +template> +class positive_davio_resynthesis +{ +public: + positive_davio_resynthesis( std::optional const& threshold = {}, ResynFn* resyn = nullptr ) + : threshold_( threshold ), + resyn_( resyn ) {} + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( threshold_ ) + { + std::vector vars( function.num_vars() - std::min( *threshold_, function.num_vars() ) ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = positive_davio_decomposition( ntk, function, vars, std::vector>( begin, end ), *resyn_ ); + fn( f ); + } + else + { + std::vector vars( function.num_vars() ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = positive_davio_decomposition( ntk, function, vars, std::vector>( begin, end ) ); + fn( f ); + } + } + +private: + std::optional threshold_; + ResynFn* resyn_; +}; + +/*! \brief Resynthesis function based on Davio decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on Shanon decomposition. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + negative_davio_resynthesis resyn; + auto xag = node_resynthesis( klut, resyn ); + \endverbatim + * + */ +template> +class negative_davio_resynthesis +{ +public: + negative_davio_resynthesis( std::optional const& threshold = {}, ResynFn* resyn = nullptr ) + : threshold_( threshold ), + resyn_( resyn ) {} + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( threshold_ ) + { + std::vector vars( function.num_vars() - std::min( *threshold_, function.num_vars() ) ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = negative_davio_decomposition( ntk, function, vars, std::vector>( begin, end ), *resyn_ ); + fn( f ); + } + else + { + std::vector vars( function.num_vars() ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = negative_davio_decomposition( ntk, function, vars, std::vector>( begin, end ) ); + fn( f ); + } + } + +private: + std::optional threshold_; + ResynFn* resyn_; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/direct.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/direct.hpp new file mode 100644 index 00000000000..0fdd83ae3ca --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/direct.hpp @@ -0,0 +1,264 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file direct.hpp + \brief Resynthesis by trying to directly add gates + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../../algorithms/akers_synthesis.hpp" +#include "../../networks/mig.hpp" + +namespace mockturtle +{ + +struct direct_resynthesis_params +{ + bool warn_on_unsupported{ false }; +}; + +/*! \brief Resynthesis function that creates a gate for each node. + * + * If it detects a function that can be constructed as a gate, it does so. + * Otherwise, it does not create a gate. In that case, a warning can be + * printed, if configured in the parameter struct. + * + * The function works with all 0-, 1-, and 2-input node functions and with + * some 3-input node functions, e.g., 3-input majority for MIGs and XMGs, or + * 3-input XOR for XMGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + direct_resynthesis resyn; + const auto mig = node_resynthesis( klut, resyn ); + \endverbatim + */ +template +class direct_resynthesis +{ +public: + direct_resynthesis( direct_resynthesis_params const& ps = {} ) + : ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + (void)end; + switch ( function.num_vars() ) + { + case 0u: + synthesize0( ntk, function, fn ); + break; + + case 1u: + synthesize1( ntk, function, *begin, fn ); + break; + + case 2u: + synthesize2( ntk, function, *begin, *( begin + 1 ), fn ); + break; + + case 3u: + synthesize3( ntk, function, *begin, *( begin + 1 ), *( begin + 2 ), fn ); + break; + } + } + +private: + template + void synthesize0( Ntk& ntk, kitty::dynamic_truth_table const& function, Fn&& fn ) const + { + fn( ntk.get_constant( kitty::is_const0( function ) ) ); + } + + template + void synthesize1( Ntk& ntk, kitty::dynamic_truth_table const& function, signal const& f, Fn&& fn ) const + { + switch ( *function.begin() ) + { + case 0b00: + fn( ntk.get_constant( false ) ); + break; + case 0b01: + fn( ntk.create_not( f ) ); + break; + case 0b10: + fn( f ); + break; + case 0b11: + fn( ntk.get_constant( true ) ); + break; + } + } + + template + void synthesize2( Ntk& ntk, kitty::dynamic_truth_table const& function, signal const& f, signal const& g, Fn&& fn ) const + { + switch ( *function.begin() ) + { + case 0b0000: + fn( ntk.get_constant( false ) ); + break; + case 0b0001: /* NOR */ + fn( ntk.create_not( ntk.create_or( f, g ) ) ); + break; + case 0b0010: /* AND(f, !g) */ + fn( ntk.create_and( f, ntk.create_not( g ) ) ); + break; + case 0b0011: /* !g */ + fn( ntk.create_not( g ) ); + break; + case 0b0100: /* AND(!f, g) */ + fn( ntk.create_and( ntk.create_not( f ), g ) ); + break; + case 0b0101: /* !f */ + fn( ntk.create_not( f ) ); + break; + case 0b0110: /* XOR */ + fn( ntk.create_xor( f, g ) ); + break; + case 0b0111: /* NAND */ + fn( ntk.create_not( ntk.create_and( f, g ) ) ); + break; + case 0b1000: /* AND */ + fn( ntk.create_and( f, g ) ); + break; + case 0b1001: /* XNOR */ + fn( ntk.create_not( ntk.create_xor( f, g ) ) ); + break; + case 0b1010: /* f */ + fn( f ); + break; + case 0b1011: /* OR(f, !g) */ + fn( ntk.create_or( f, ntk.create_not( g ) ) ); + break; + case 0b1100: /* g */ + fn( g ); + break; + case 0b1101: /* OR(!f, g) */ + fn( ntk.create_or( ntk.create_not( f ), g ) ); + break; + case 0b1110: /* OR(f, g) */ + fn( ntk.create_or( f, g ) ); + break; + case 0b1111: + fn( ntk.get_constant( true ) ); + break; + } + } + + template + void synthesize3( Ntk& ntk, kitty::dynamic_truth_table const& function, signal const& f, signal const& g, signal const& h, Fn&& fn ) const + { + // TODO? all contained extended 1-input and 2-input functions + // TODO create_ite + + const auto word = *function.begin(); + switch ( word ) + { + case 0x00: + fn( ntk.get_constant( false ) ); + break; + case 0xff: + fn( ntk.get_constant( true ) ); + break; + case 0xe8: /* */ + case 0xd4: /* */ + case 0xb2: /* */ + case 0x8e: /* */ + case 0x71: /* */ + case 0x4d: /* */ + case 0x2b: /* */ + case 0x17: /* */ + if constexpr ( has_create_maj_v ) + { + const auto _f = ( ( word == 0xd4 ) || ( word == 0x71 ) || ( word == 0x4d ) || ( word == 0x17 ) ) ? ntk.create_not( f ) : f; + const auto _g = ( ( word == 0xb2 ) || ( word == 0x71 ) || ( word == 0x2b ) || ( word == 0x17 ) ) ? ntk.create_not( g ) : g; + const auto _h = ( ( word == 0x8e ) || ( word == 0x4d ) || ( word == 0x2b ) || ( word == 0x17 ) ) ? ntk.create_not( h ) : h; + fn( ntk.create_maj( _f, _g, _h ) ); + } + else + { + if ( ps.warn_on_unsupported ) + { + std::cout << "[w] function " << kitty::to_hex( function ) << " cannot be synthesized as gate\n"; + } + } + break; + case 0x96: /* [abc] */ + case 0x69: /* ![abc] */ + if constexpr ( has_create_xor3_v ) + { + const auto o = ntk.create_xor3( f, g, h ); + fn( word == 0x69 ? ntk.create_not( o ) : o ); + } + else + { + if ( ps.warn_on_unsupported ) + { + std::cout << "[w] function " << kitty::to_hex( function ) << " cannot be synthesized as gate\n"; + } + } + break; + default: + std::cout << "[w] failed to synthesize function " << kitty::to_hex( function ) << "\n"; + } + } + +private: + direct_resynthesis_params ps; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/dsd.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/dsd.hpp new file mode 100644 index 00000000000..2fc1a0cc622 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/dsd.hpp @@ -0,0 +1,147 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dsd.hpp + \brief Use DSD as pre-process to resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include + +#include "../dsd_decomposition.hpp" +#include "traits.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for dsd_resynthesis function. */ +struct dsd_resynthesis_params +{ + /*! \brief Skip resynthesis on prime nodes, if it exceeds this limit. */ + std::optional prime_input_limit; + + /*! \brief DSD decomposition parameters */ + dsd_decomposition_params dsd_ps; +}; + +/*! \brief Resynthesis function based on DSD decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on DSD decomposition. Since DSD decomposition may not be + * able to decompose the whole truth table, a different fall-back resynthesis + * function must be passed to this function. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const aig_network aig = ...; + + exact_aig_resynthesis fallback; // fallback + dsd_resynthesis resyn( fallback ); + cut_rewriting( aig, resyn ); + aig = cleanup_dangling( aig ); + \endverbatim + * + */ +template +class dsd_resynthesis +{ +public: + explicit dsd_resynthesis( ResynthesisFn& resyn_fn, dsd_resynthesis_params const& ps = {} ) + : _resyn_fn( resyn_fn ), + _ps( ps ) + { + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + bool success{ true }; + const auto on_prime = [&]( kitty::dynamic_truth_table const& remainder, std::vector> const& leaves ) { + success = false; + signal f = ntk.get_constant( false ); + if ( _ps.prime_input_limit && leaves.size() > *_ps.prime_input_limit ) + { + return f; + } + + const auto on_signal = [&]( signal const& _f ) { + if ( !success ) + { + f = _f; + success = true; + } + return true; + }; + auto _leaves = leaves; + + if constexpr ( has_set_bounds_v ) + { + _resyn_fn.set_bounds( static_cast( _leaves.size() ), std::nullopt ); + } + _resyn_fn( ntk, remainder, _leaves.begin(), _leaves.end(), on_signal ); + return f; + }; + + const auto f = dsd_decomposition( ntk, function, std::vector>( begin, end ), on_prime, _ps.dsd_ps ); + if ( success ) + { + fn( f ); + } + } + + void clear_functions() + { + if constexpr ( has_clear_functions_v ) + { + _resyn_fn.clear_functions(); + } + } + + void add_function( signal const& s, kitty::dynamic_truth_table const& tt ) + { + if constexpr ( has_add_function_v ) + { + _resyn_fn.add_function( s, tt ); + } + } + +private: + ResynthesisFn& _resyn_fn; + dsd_resynthesis_params _ps; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/exact.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/exact.hpp new file mode 100644 index 00000000000..98920b4f8a7 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/exact.hpp @@ -0,0 +1,659 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file exact.hpp + \brief Replace with exact synthesis result + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../../networks/aig.hpp" +#include "../../networks/klut.hpp" +#include "../../networks/xmg.hpp" +#include "../../utils/include/percy.hpp" + +namespace mockturtle +{ + +struct exact_resynthesis_params +{ + using cache_map_t = std::unordered_map>; + using cache_t = std::shared_ptr; + + using blacklist_cache_map_t = std::unordered_map>; + using blacklist_cache_t = std::shared_ptr; + + cache_t cache; + blacklist_cache_t blacklist_cache; + + bool add_alonce_clauses{ true }; + bool add_colex_clauses{ true }; + bool add_lex_clauses{ false }; + bool add_lex_func_clauses{ true }; + bool add_nontriv_clauses{ true }; + bool add_noreapply_clauses{ true }; + bool add_symvar_clauses{ true }; + int conflict_limit{ 0 }; + + percy::SolverType solver_type = percy::SLV_BSAT2; + + percy::EncoderType encoder_type = percy::ENC_SSV; + + percy::SynthMethod synthesis_method = percy::SYNTH_STD; +}; + +/*! \brief Resynthesis function based on exact synthesis. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized in terms of an optimum size `k`-LUT network, where `k` is + * specified as input to the constructor. In order to guarantee a reasonable + * runtime, `k` should be 3 or 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + exact_resynthesis resyn( 3 ); + klut = cut_rewriting( klut, resyn ); + \endverbatim + * + * A cache can be passed as second parameter to the constructor, which will + * store optimum networks for all functions for which resynthesis is invoked + * for. The cache can be used to retrieve the computed network, which reduces + * runtime. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + exact_resynthesis_params ps; + ps.cache = std::make_shared(); + exact_resynthesis resyn( 3, ps ); + klut = cut_rewriting( klut, resyn ); + + The underlying engine for this resynthesis function is percy_. + + .. _percy: https://github.com/lsils/percy + \endverbatim + * + */ +template +class exact_resynthesis +{ +public: + explicit exact_resynthesis( uint32_t fanin_size = 3u, exact_resynthesis_params const& ps = {} ) + : _fanin_size( fanin_size ), + _ps( ps ) + { + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + operator()( ntk, function, function.construct(), begin, end, fn ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dont_cares, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( static_cast( function.num_vars() ) <= _fanin_size ) + { + fn( ntk.create_node( std::vector>( begin, end ), function ) ); + return; + } + + percy::spec spec; + spec.fanin = _fanin_size; + spec.verbosity = 0; + spec.add_alonce_clauses = _ps.add_alonce_clauses; + spec.add_colex_clauses = _ps.add_colex_clauses; + spec.add_lex_clauses = _ps.add_lex_clauses; + spec.add_lex_func_clauses = _ps.add_lex_func_clauses; + spec.add_nontriv_clauses = _ps.add_nontriv_clauses; + spec.add_noreapply_clauses = _ps.add_noreapply_clauses; + spec.add_symvar_clauses = _ps.add_symvar_clauses; + spec.conflict_limit = _ps.conflict_limit; + spec[0] = function; + bool with_dont_cares{ false }; + if ( !kitty::is_const0( dont_cares ) ) + { + spec.set_dont_care( 0, dont_cares ); + with_dont_cares = true; + } + + auto c = [&]() -> std::optional { + if ( !with_dont_cares && _ps.cache ) + { + const auto it = _ps.cache->find( function ); + if ( it != _ps.cache->end() ) + { + return it->second; + } + } + if ( !with_dont_cares && _ps.blacklist_cache ) + { + const auto it = _ps.blacklist_cache->find( function ); + if ( it != _ps.blacklist_cache->end() && ( it->second == 0 || _ps.conflict_limit <= it->second ) ) + { + return std::nullopt; + } + } + + percy::chain c; + if ( const auto result = percy::synthesize( spec, c, _ps.solver_type, + _ps.encoder_type, + _ps.synthesis_method ); + result != percy::success ) + { + if ( !with_dont_cares && _ps.blacklist_cache ) + { + ( *_ps.blacklist_cache )[function] = result == percy::timeout ? _ps.conflict_limit : 0; + } + return std::nullopt; + } + c.denormalize(); + if ( !with_dont_cares && _ps.cache ) + { + ( *_ps.cache )[function] = c; + } + return c; + }(); + + if ( !c ) + { + return; + } + + std::vector> signals( begin, end ); + + for ( auto i = 0; i < c->get_nr_steps(); ++i ) + { + std::vector> fanin; + for ( const auto& child : c->get_step( i ) ) + { + fanin.emplace_back( signals[child] ); + } + signals.emplace_back( ntk.create_node( fanin, c->get_operator( i ) ) ); + } + + fn( signals.back() ); + } + +private: + uint32_t _fanin_size{ 3u }; + exact_resynthesis_params _ps; +}; + +/*! \brief Resynthesis function based on exact synthesis for AIGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthesized in terms of an optimum size AIG network. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig = ...; + + exact_aig_resynthesis resyn; + aig = cut_rewriting( aig, resyn ); + \endverbatim + * + * A cache can be passed as second parameter to the constructor, which will + * store optimum networks for all functions for which resynthesis is invoked + * for. The cache can be used to retrieve the computed network, which reduces + * runtime. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig = ...; + + exact_resynthesis_params ps; + ps.cache = std::make_shared(); + exact_aig_resynthesis resyn( false, ps ); + aig = cut_rewriting( aig, resyn ); + + The underlying engine for this resynthesis function is percy_. + + .. _percy: https://github.com/lsils/percy + \endverbatim + * + */ +template +class exact_aig_resynthesis +{ +public: + using signal = typename Ntk::signal; + +public: + explicit exact_aig_resynthesis( bool _allow_xor = false, exact_resynthesis_params const& ps = {} ) + : _allow_xor( _allow_xor ), + _ps( ps ) + { + } + + void clear_functions() + { + existing_functions.clear(); + } + + void add_function( signal const& s, kitty::dynamic_truth_table const& tt ) + { + existing_functions.emplace_back( s, tt ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + operator()( ntk, function, function.construct(), begin, end, fn ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dont_cares, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + // TODO: special case for small functions (up to 2 variables)? + percy::spec spec; + if ( !_allow_xor ) + { + spec.set_primitive( percy::AIG ); + } + spec.fanin = 2; + spec.verbosity = 0; + spec.add_alonce_clauses = _ps.add_alonce_clauses; + spec.add_colex_clauses = _ps.add_colex_clauses; + spec.add_lex_clauses = _ps.add_lex_clauses; + spec.add_lex_func_clauses = _ps.add_lex_func_clauses; + spec.add_nontriv_clauses = _ps.add_nontriv_clauses; + spec.add_noreapply_clauses = _ps.add_noreapply_clauses; + spec.add_symvar_clauses = _ps.add_symvar_clauses; + spec.conflict_limit = _ps.conflict_limit; + if ( _lower_bound ) + { + spec.initial_steps = *_lower_bound; + } + if ( _upper_bound ) + { + spec.max_nr_steps = *_upper_bound; + } + spec[0] = function; + bool with_dont_cares{ false }; + if ( !kitty::is_const0( dont_cares ) ) + { + spec.set_dont_care( 0, dont_cares ); + with_dont_cares = true; + } + + /* add existing functions */ + for ( const auto& f : existing_functions ) + { + spec.add_function( f.second ); + } + + auto c = [&]() -> std::optional { + if ( !with_dont_cares && _ps.cache ) + { + const auto it = _ps.cache->find( function ); + if ( it != _ps.cache->end() ) + { + return it->second; + } + } + if ( !with_dont_cares && _ps.blacklist_cache ) + { + const auto it = _ps.blacklist_cache->find( function ); + if ( it != _ps.blacklist_cache->end() && ( it->second == 0 || _ps.conflict_limit <= it->second ) ) + { + return std::nullopt; + } + } + + percy::chain c; + if ( const auto result = percy::synthesize( spec, c, _ps.solver_type, + _ps.encoder_type, + _ps.synthesis_method ); + result != percy::success ) + { + if ( !with_dont_cares && _ps.blacklist_cache ) + { + ( *_ps.blacklist_cache )[function] = ( result == percy::timeout ) ? _ps.conflict_limit : 0; + } + return std::nullopt; + } + + assert( kitty::to_hex( c.simulate()[0u] ) == kitty::to_hex( function ) ); + + if ( !with_dont_cares && _ps.cache ) + { + ( *_ps.cache )[function] = c; + } + return c; + }(); + + if ( !c ) + { + return; + } + + std::vector signals( begin, end ); + for ( const auto& f : existing_functions ) + { + signals.emplace_back( f.first ); + } + + for ( auto i = 0; i < c->get_nr_steps(); ++i ) + { + auto const c1 = signals[c->get_step( i )[0]]; + auto const c2 = signals[c->get_step( i )[1]]; + + switch ( c->get_operator( i )._bits[0] ) + { + default: + std::cerr << "[e] unsupported operation " << kitty::to_hex( c->get_operator( i ) ) << "\n"; + assert( false ); + break; + case 0x8: + signals.emplace_back( ntk.create_and( c1, c2 ) ); + break; + case 0x4: + signals.emplace_back( ntk.create_and( !c1, c2 ) ); + break; + case 0x2: + signals.emplace_back( ntk.create_and( c1, !c2 ) ); + break; + case 0xe: + signals.emplace_back( !ntk.create_and( !c1, !c2 ) ); + break; + case 0x6: + signals.emplace_back( ntk.create_xor( c1, c2 ) ); + break; + } + } + + fn( c->is_output_inverted( 0 ) ? !signals.back() : signals.back() ); + } + + void set_bounds( std::optional const& lower_bound, std::optional const& upper_bound ) + { + _lower_bound = lower_bound; + _upper_bound = upper_bound; + } + +private: + bool _allow_xor = false; + exact_resynthesis_params _ps; + + std::vector> existing_functions; + + std::optional _lower_bound; + std::optional _upper_bound; +}; + +struct exact_xmg_resynthesis_params +{ + uint32_t num_candidates{ 10u }; + bool use_only_self_dual_gates{ false }; + bool use_xor3{ true }; + int conflict_limit{ 0 }; +}; + +/*! \brief Resynthesis function based on exact synthesis for XMGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthesized in terms of an optimum size XMG network. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + xmg_network aig = ...; + + exact_xmg_resynthesis resyn; + xmg = cut_rewriting( xmg, resyn ); + \endverbatim + * + * + The underlying engine for this resynthesis function is percy_. + \verbatim embed:rst + + .. _percy: https://github.com/lsils/percy + \endverbatim + * + */ +template +class exact_xmg_resynthesis +{ +public: + explicit exact_xmg_resynthesis( exact_xmg_resynthesis_params const& ps = {} ) + : ps( ps ) + { + } + + template + void operator()( Ntk& ntk, TT const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + static_assert( kitty::is_complete_truth_table::value, "Truth table must be complete" ); + + using signal = mockturtle::signal; + auto const tt = function.num_vars() < 3u ? kitty::extend_to( function, 3u ) : function; + bool const normal = kitty::is_normal( tt ); + + percy::chain chain; + percy::spec spec; + spec.verbosity = 0; + spec.fanin = 3; + spec.conflict_limit = ps.conflict_limit; + + /* specify local normalized gate primitives */ + kitty::dynamic_truth_table const0{ 3 }; + kitty::dynamic_truth_table a{ 3 }; + kitty::dynamic_truth_table b{ 3 }; + kitty::dynamic_truth_table c{ 3 }; + kitty::create_nth_var( a, 0 ); + kitty::create_nth_var( b, 1 ); + kitty::create_nth_var( c, 2 ); + + spec.add_primitive( const0 ); // 00 + spec.add_primitive( a ); // aa + spec.add_primitive( b ); // cc + spec.add_primitive( c ); // f0 + + /* add self dual gate functions */ + spec.add_primitive( kitty::ternary_majority( a, b, c ) ); // e8 + spec.add_primitive( kitty::ternary_majority( ~a, b, c ) ); // d4 + spec.add_primitive( kitty::ternary_majority( a, ~b, c ) ); // b2 + spec.add_primitive( kitty::ternary_majority( a, b, ~c ) ); // 8e + spec.add_primitive( a ^ b ); // 66 + spec.add_primitive( a ^ c ); // 3c + spec.add_primitive( b ^ c ); // 5a + if ( ps.use_xor3 ) + { + spec.add_primitive( a ^ b ^ c ); // 96 + } + + /* add non-self dual gate functions */ + if ( !ps.use_only_self_dual_gates ) + { + spec.add_primitive( kitty::ternary_majority( const0, b, c ) ); // c0 + spec.add_primitive( kitty::ternary_majority( ~const0, b, c ) ); // fc + spec.add_primitive( kitty::ternary_majority( const0, ~b, c ) ); // 30 + spec.add_primitive( kitty::ternary_majority( const0, b, ~c ) ); // 0c + spec.add_primitive( kitty::ternary_majority( a, const0, c ) ); // a0 + spec.add_primitive( kitty::ternary_majority( ~a, const0, c ) ); // 50 + spec.add_primitive( kitty::ternary_majority( a, ~const0, c ) ); // fa + spec.add_primitive( kitty::ternary_majority( a, const0, ~c ) ); // 0a + spec.add_primitive( kitty::ternary_majority( a, b, const0 ) ); // 88 + spec.add_primitive( kitty::ternary_majority( a, b, ~const0 ) ); // ee + spec.add_primitive( kitty::ternary_majority( ~a, b, const0 ) ); // 44 + spec.add_primitive( kitty::ternary_majority( a, ~b, const0 ) ); // 22 + } + + percy::bsat_wrapper solver; + percy::ssv_encoder encoder( solver ); + + spec[0] = normal ? tt : ~tt; + + for ( auto i = 0u; i < ps.num_candidates; ++i ) + { + auto const result = percy::next_struct_solution( spec, chain, solver, encoder ); + if ( result != percy::success ) + break; + + assert( result == percy::success ); + + auto const sim = chain.simulate(); + assert( chain.simulate()[0] == spec[0] ); + + std::vector signals( tt.num_vars(), ntk.get_constant( false ) ); + std::copy( begin, end, signals.begin() ); + + for ( auto i = 0; i < chain.get_nr_steps(); ++i ) + { + auto const c1 = signals[chain.get_step( i )[0]]; + auto const c2 = signals[chain.get_step( i )[1]]; + auto const c3 = signals[chain.get_step( i )[2]]; + + switch ( chain.get_operator( i )._bits[0] ) + { + case 0x00: + signals.emplace_back( ntk.get_constant( false ) ); + break; + case 0xe8: + signals.emplace_back( ntk.create_maj( c1, c2, c3 ) ); + break; + case 0xd4: + signals.emplace_back( ntk.create_maj( !c1, c2, c3 ) ); + break; + case 0xb2: + signals.emplace_back( ntk.create_maj( c1, !c2, c3 ) ); + break; + case 0x8e: + signals.emplace_back( ntk.create_maj( c1, c2, !c3 ) ); + break; + case 0x96: + signals.emplace_back( ntk.create_xor3( c1, c2, c3 ) ); + break; + case 0xc0: + signals.emplace_back( ntk.create_maj( ntk.get_constant( false ), c2, c3 ) ); // c0 + break; + case 0xfc: + signals.emplace_back( ntk.create_maj( !ntk.get_constant( false ), c2, c3 ) ); // fc + break; + case 0x30: + signals.emplace_back( ntk.create_maj( ntk.get_constant( false ), !c2, c3 ) ); // 30 + break; + case 0x0c: + signals.emplace_back( ntk.create_maj( ntk.get_constant( false ), c2, !c3 ) ); // 0c + break; + case 0xa0: + signals.emplace_back( ntk.create_maj( c1, ntk.get_constant( false ), c3 ) ); // 0a + break; + case 0x50: + signals.emplace_back( ntk.create_maj( !c1, ntk.get_constant( false ), c3 ) ); // 50 + break; + case 0xfa: + signals.emplace_back( ntk.create_maj( c1, !ntk.get_constant( false ), c3 ) ); // fa + break; + case 0x0a: + signals.emplace_back( ntk.create_maj( c1, ntk.get_constant( false ), !c3 ) ); // 0a + break; + case 0x88: + signals.emplace_back( ntk.create_maj( c1, c2, ntk.get_constant( false ) ) ); // 88 + break; + case 0xee: + signals.emplace_back( ntk.create_maj( c1, c2, !ntk.get_constant( false ) ) ); // ee + break; + case 0x44: + signals.emplace_back( ntk.create_maj( !c1, c2, ntk.get_constant( false ) ) ); // 44 + break; + case 0x22: + signals.emplace_back( ntk.create_maj( c1, !c2, ntk.get_constant( false ) ) ); // 22 + break; + case 0x66: + signals.emplace_back( ntk.create_xor( c1, c2 ) ); + break; + case 0x3c: + signals.emplace_back( ntk.create_xor( c2, c3 ) ); + break; + case 0x5a: + signals.emplace_back( ntk.create_xor( c1, c3 ) ); + break; + default: + std::cerr << "[e] unsupported operation " << kitty::to_hex( chain.get_operator( i ) ) << "\n"; + assert( false ); + break; + } + } + + assert( chain.get_outputs().size() > 0u ); + uint32_t const output_index = ( chain.get_outputs()[0u] >> 1u ); + auto const output_signal = output_index == 0u ? ntk.get_constant( false ) : signals[output_index - 1]; + if ( !fn( chain.is_output_inverted( 0 ) ^ normal ? output_signal : !output_signal ) ) + { + return; /* quit */ + } + } + } + +protected: + exact_xmg_resynthesis_params const ps; +}; /* exact_xmg_resynthesis */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/mig_npn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/mig_npn.hpp new file mode 100644 index 00000000000..c19014bb21f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/mig_npn.hpp @@ -0,0 +1,234 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_npn.hpp + \brief Replace with size-optimum MIGs from NPN + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../../algorithms/cleanup.hpp" +#include "../../networks/mig.hpp" +#include "../../traits.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on pre-computed size-optimum MIGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. It will produce an MIG based on + * pre-computed size-optimum MIGs with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + mig_npn_resynthesis resyn; + const auto mig = node_resynthesis( klut, resyn ); + \endverbatim + */ +class mig_npn_resynthesis +{ +public: + /*! \brief Default constructor. + * + * \param use_multiple If true, up to 10 structures are tried for each + * function. + */ + mig_npn_resynthesis( bool use_multiple = false ) + { + if ( use_multiple ) + { + build_db10(); + } + else + { + build_db(); + } + } + + template + void operator()( mig_network& mig, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 4 ); + const auto fe = kitty::extend_to( function, 4 ); + const auto config = kitty::exact_npn_canonization( fe ); + + const auto it = class2signal.find( static_cast( std::get<0>( config )._bits[0] ) ); + + std::vector pis( 4, mig.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::vector pis_perm( 4 ); + auto perm = std::get<2>( config ); + for ( auto i = 0; i < 4; ++i ) + { + pis_perm[i] = pis[perm[i]]; + } + + const auto& phase = std::get<1>( config ); + for ( auto i = 0; i < 4; ++i ) + { + if ( ( phase >> perm[i] ) & 1 ) + { + pis_perm[i] = !pis_perm[i]; + } + } + + for ( auto const& po : it->second ) + { + topo_view topo{ db, po }; + auto f = cleanup_dangling( topo, mig, pis_perm.begin(), pis_perm.end() ).front(); + + if ( !fn( ( ( phase >> 4 ) & 1 ) ? !f : f ) ) + { + return; /* quit */ + } + } + } + +private: + void build_db() + { + std::vector signals; + signals.push_back( db.get_constant( false ) ); + + auto p = nodes.begin(); + for ( auto i = 0u; i < *p; ++i ) + { + signals.push_back( db.create_pi() ); + } + + p++; /* point to number of outputs */ + p += *p + 1; /* move past number of output */ + + /* create nodes */ + while ( p != nodes.end() ) + { + const auto c1 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c2 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c3 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + signals.push_back( db.create_maj( c1, c2, c3 ) ); + } + + /* create POs */ + p = nodes.begin() + 2; + for ( auto i = 0u; i < nodes[1]; ++i ) + { + const auto driver = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + db.create_po( driver ); + class2signal[classes[i]].push_back( driver ); + } + } + + void build_db10() + { + std::vector signals; + signals.push_back( db.get_constant( false ) ); + + auto p = nodes10.begin(); + for ( auto i = 0u; i < *p; ++i ) + { + signals.push_back( db.create_pi() ); + } + + p++; /* point to number of outputs */ + const auto num_functions = *p++; + + for ( auto i = 0u; i < num_functions; ++i ) + { + p += *p + 1; + } + + /* create nodes */ + while ( p != nodes10.end() ) + { + const auto c1 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c2 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c3 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + signals.push_back( db.create_maj( c1, c2, c3 ) ); + } + + /* create PIs */ + p = nodes10.begin() + 2; + for ( auto i = 0u; i < nodes10[1]; ++i ) + { + const auto functions = *p++; + for ( auto j = 0u; j < functions; ++j ) + { + const auto driver = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + db.create_po( driver ); + class2signal[classes[i]].push_back( driver ); + } + } + } + + mig_network db; + std::unordered_map> class2signal; + + inline static const std::vector classes{ { 0x1ee1, 0x1be4, 0x1bd8, 0x18e7, 0x17e8, 0x17ac, 0x1798, 0x1796, 0x178e, 0x177e, 0x16e9, 0x16bc, 0x169e, 0x003f, 0x0359, 0x0672, 0x07e9, 0x0693, 0x0358, 0x01bf, 0x6996, 0x0356, 0x01bd, 0x001f, 0x01ac, 0x001e, 0x0676, 0x01ab, 0x01aa, 0x001b, 0x07e1, 0x07e0, 0x0189, 0x03de, 0x035a, 0x1686, 0x0186, 0x03db, 0x0357, 0x01be, 0x1683, 0x0368, 0x0183, 0x03d8, 0x07e6, 0x0182, 0x03d7, 0x0181, 0x03d6, 0x167e, 0x016a, 0x007e, 0x0169, 0x006f, 0x0069, 0x0168, 0x0001, 0x019a, 0x036b, 0x1697, 0x0369, 0x0199, 0x0000, 0x169b, 0x003d, 0x036f, 0x0666, 0x019b, 0x0187, 0x03dc, 0x0667, 0x0003, 0x168e, 0x06b6, 0x01eb, 0x07e2, 0x017e, 0x07b6, 0x007f, 0x19e3, 0x06b7, 0x011a, 0x077e, 0x018b, 0x00ff, 0x0673, 0x01a8, 0x000f, 0x1696, 0x036a, 0x011b, 0x0018, 0x0117, 0x1698, 0x036c, 0x01af, 0x0016, 0x067a, 0x0118, 0x0017, 0x067b, 0x0119, 0x169a, 0x003c, 0x036e, 0x07e3, 0x017f, 0x03d4, 0x06f0, 0x011e, 0x037c, 0x012c, 0x19e6, 0x01ef, 0x16a9, 0x037d, 0x006b, 0x012d, 0x012f, 0x01fe, 0x0019, 0x03fc, 0x179a, 0x013c, 0x016b, 0x06f2, 0x03c0, 0x033c, 0x1668, 0x0669, 0x019e, 0x013d, 0x0006, 0x019f, 0x013e, 0x0776, 0x013f, 0x016e, 0x03c3, 0x3cc3, 0x033f, 0x166b, 0x016f, 0x011f, 0x035e, 0x0690, 0x0180, 0x03d5, 0x06f1, 0x06b0, 0x037e, 0x03c1, 0x03c5, 0x03c6, 0x01a9, 0x166e, 0x03cf, 0x03d9, 0x07bc, 0x01bc, 0x1681, 0x03dd, 0x03c7, 0x06f9, 0x0660, 0x0196, 0x0661, 0x0197, 0x0662, 0x07f0, 0x0198, 0x0663, 0x07f1, 0x0007, 0x066b, 0x033d, 0x1669, 0x066f, 0x01ad, 0x0678, 0x01ae, 0x0679, 0x067e, 0x168b, 0x035f, 0x0691, 0x0696, 0x0697, 0x06b1, 0x0778, 0x16ac, 0x06b2, 0x0779, 0x16ad, 0x01e8, 0x06b3, 0x0116, 0x077a, 0x01e9, 0x06b4, 0x19e1, 0x01ea, 0x06b5, 0x01ee, 0x06b9, 0x06bd, 0x06f6, 0x07b0, 0x07b1, 0x07b4, 0x07b5, 0x07f2, 0x07f8, 0x018f, 0x0ff0, 0x166a, 0x035b, 0x1687, 0x1689, 0x036d, 0x069f, 0x1699 } }; + inline static const std::vector nodes{ { 4, 222, 17, 24, 34, 41, 46, 56, 68, 76, 84, 96, 109, 116, 122, 127, 137, 142, 151, 157, 166, 173, 182, 188, 193, 199, 208, 214, 220, 227, 232, 239, 247, 256, 259, 264, 272, 278, 286, 293, 297, 300, 307, 312, 321, 328, 336, 344, 351, 355, 362, 372, 378, 384, 387, 389, 393, 398, 401, 408, 417, 421, 425, 433, 0, 439, 445, 451, 454, 459, 467, 472, 475, 477, 482, 486, 491, 498, 502, 506, 509, 517, 523, 526, 532, 537, 9, 545, 548, 335, 554, 560, 563, 568, 573, 576, 580, 583, 586, 594, 596, 599, 603, 605, 612, 616, 622, 627, 629, 630, 634, 638, 640, 644, 650, 657, 665, 669, 675, 677, 679, 686, 691, 696, 702, 708, 713, 718, 722, 728, 738, 747, 750, 755, 756, 761, 766, 770, 773, 778, 785, 789, 159, 797, 801, 803, 810, 812, 820, 827, 831, 836, 844, 853, 857, 862, 869, 876, 879, 887, 892, 900, 911, 915, 921, 927, 930, 938, 941, 951, 954, 960, 966, 971, 975, 977, 985, 991, 1003, 1007, 1011, 1014, 1020, 1027, 1030, 1037, 1039, 1041, 1044, 1049, 1053, 1060, 1066, 1070, 1073, 1077, 1082, 1093, 1096, 1100, 1107, 1112, 1119, 1124, 1135, 1138, 1141, 1147, 1148, 1152, 1159, 1166, 1175, 1178, 1186, 1191, 1194, 1202, 1205, 1213, 1221, 1229, 1231, 1237, 1, 2, 4, 6, 8, 11, 9, 10, 12, 7, 12, 14, 0, 2, 7, 8, 10, 19, 8, 10, 21, 18, 20, 23, 5, 6, 8, 2, 4, 6, 0, 26, 28, 1, 26, 28, 0, 31, 32, 6, 9, 28, 8, 29, 36, 7, 36, 38, 0, 8, 28, 0, 8, 43, 28, 43, 44, 0, 5, 8, 4, 7, 48, 0, 2, 8, 2, 6, 48, 50, 53, 54, 0, 4, 9, 0, 2, 59, 0, 2, 58, 7, 8, 62, 2, 5, 6, 61, 64, 66, 0, 6, 9, 1, 4, 8, 2, 71, 72, 29, 70, 74, 0, 4, 8, 2, 4, 7, 0, 3, 8, 79, 80, 82, 0, 7, 8, 2, 7, 86, 4, 6, 87, 0, 6, 8, 2, 4, 92, 88, 90, 95, 0, 2, 4, 1, 6, 98, 2, 4, 99, 8, 100, 103, 101, 102, 104, 9, 104, 106, 1, 4, 6, 0, 9, 110, 2, 8, 110, 29, 112, 114, 0, 3, 6, 4, 52, 118, 80, 118, 121, 0, 4, 6, 1, 8, 124, 0, 2, 9, 0, 4, 128, 6, 9, 130, 4, 6, 131, 128, 133, 134, 0, 2, 5, 3, 6, 78, 93, 138, 140, 0, 9, 28, 2, 4, 9, 0, 6, 147, 145, 146, 148, 4, 8, 71, 2, 4, 8, 28, 152, 155, 4, 6, 8, 0, 8, 159, 2, 5, 158, 2, 6, 83, 160, 163, 164, 1, 2, 6, 0, 3, 4, 8, 168, 170, 3, 6, 18, 1, 18, 174, 4, 9, 176, 5, 8, 176, 177, 178, 180, 2, 82, 110, 2, 83, 110, 82, 185, 186, 2, 7, 78, 99, 158, 190, 0, 5, 6, 0, 3, 194, 6, 8, 197, 4, 8, 52, 4, 7, 8, 2, 6, 200, 1, 202, 204, 0, 201, 206, 0, 6, 10, 6, 9, 10, 0, 211, 212, 6, 9, 98, 1, 80, 216, 0, 99, 218, 4, 6, 53, 3, 52, 222, 1, 52, 224, 3, 6, 170, 2, 8, 228, 8, 128, 231, 2, 6, 8, 3, 4, 8, 1, 234, 236, 1, 6, 146, 6, 146, 241, 0, 9, 242, 0, 240, 245, 2, 5, 8, 4, 6, 248, 1, 8, 250, 0, 9, 252, 251, 252, 254, 10, 131, 194, 4, 7, 248, 0, 6, 249, 79, 260, 262, 0, 4, 7, 6, 8, 266, 3, 6, 8, 18, 269, 270, 2, 7, 8, 4, 82, 275, 5, 80, 276, 2, 8, 266, 4, 8, 281, 2, 7, 282, 0, 281, 284, 5, 8, 28, 0, 4, 29, 110, 288, 290, 0, 2, 110, 8, 110, 294, 1, 6, 236, 128, 159, 298, 4, 6, 83, 2, 4, 303, 274, 302, 305, 6, 58, 128, 8, 159, 308, 129, 308, 310, 5, 6, 170, 2, 7, 170, 4, 8, 316, 1, 314, 318, 2, 6, 9, 0, 249, 322, 0, 110, 325, 8, 324, 327, 2, 9, 170, 4, 6, 171, 1, 6, 8, 330, 333, 334, 2, 5, 266, 6, 8, 338, 2, 8, 267, 0, 341, 342, 3, 4, 6, 0, 8, 110, 110, 347, 348, 4, 8, 170, 125, 168, 352, 0, 9, 346, 1, 2, 8, 4, 194, 358, 356, 358, 361, 3, 6, 266, 1, 2, 266, 0, 2, 6, 4, 8, 368, 364, 366, 371, 7, 26, 128, 3, 6, 374, 27, 374, 376, 4, 9, 66, 0, 67, 380, 5, 380, 382, 2, 145, 346, 8, 66, 139, 7, 66, 346, 1, 8, 390, 6, 8, 80, 0, 28, 395, 8, 395, 396, 1, 10, 12, 0, 3, 26, 2, 9, 402, 0, 6, 26, 402, 404, 407, 4, 6, 129, 8, 128, 410, 4, 7, 412, 5, 410, 414, 2, 8, 158, 8, 28, 419, 4, 71, 128, 5, 410, 422, 1, 6, 10, 3, 8, 10, 0, 5, 10, 426, 428, 430, 3, 4, 86, 2, 26, 434, 87, 434, 436, 2, 7, 266, 8, 267, 440, 4, 267, 442, 4, 6, 369, 8, 368, 446, 5, 446, 448, 2, 4, 93, 3, 138, 452, 2, 8, 194, 3, 10, 456, 0, 8, 154, 6, 155, 460, 0, 7, 154, 1, 462, 464, 4, 9, 196, 4, 194, 469, 8, 468, 471, 1, 12, 98, 1, 4, 334, 1, 6, 80, 2, 8, 479, 48, 80, 481, 2, 29, 70, 10, 29, 484, 0, 4, 70, 52, 346, 489, 0, 8, 93, 2, 93, 492, 4, 7, 92, 170, 494, 497, 3, 6, 160, 146, 159, 500, 1, 2, 202, 29, 70, 504, 8, 98, 334, 4, 6, 78, 4, 6, 9, 3, 78, 512, 154, 511, 514, 0, 2, 67, 8, 171, 518, 6, 67, 520, 0, 2, 27, 26, 235, 524, 6, 8, 524, 5, 26, 524, 4, 529, 530, 3, 4, 194, 1, 456, 534, 1, 4, 270, 5, 6, 538, 0, 8, 540, 271, 538, 542, 1, 2, 110, 112, 358, 547, 4, 9, 82, 2, 6, 29, 29, 550, 552, 4, 128, 202, 4, 202, 557, 128, 557, 558, 3, 10, 234, 0, 5, 346, 4, 9, 346, 347, 564, 566, 4, 6, 358, 1, 234, 570, 0, 6, 29, 43, 154, 574, 5, 86, 368, 4, 371, 578, 6, 129, 190, 4, 9, 168, 0, 29, 584, 6, 8, 98, 2, 118, 589, 4, 8, 98, 589, 590, 592, 130, 159, 270, 1, 8, 28, 0, 4, 271, 8, 465, 600, 10, 248, 346, 0, 2, 249, 6, 8, 249, 1, 2, 608, 29, 606, 610, 6, 8, 195, 4, 194, 615, 5, 8, 128, 8, 128, 158, 4, 618, 621, 0, 147, 240, 202, 240, 624, 2, 8, 346, 1, 160, 356, 8, 81, 98, 8, 70, 633, 3, 4, 26, 159, 524, 636, 26, 58, 589, 0, 28, 159, 159, 236, 642, 5, 8, 18, 2, 8, 18, 146, 646, 649, 4, 6, 139, 4, 8, 138, 5, 652, 654, 3, 8, 110, 2, 111, 658, 0, 8, 513, 658, 660, 663, 2, 6, 73, 71, 158, 666, 0, 6, 67, 3, 4, 66, 8, 671, 672, 66, 158, 369, 6, 129, 154, 0, 4, 169, 8, 169, 680, 8, 680, 683, 168, 682, 685, 4, 8, 19, 2, 99, 688, 1, 8, 110, 0, 9, 692, 111, 692, 694, 7, 8, 128, 0, 4, 129, 346, 698, 701, 9, 194, 202, 2, 5, 202, 3, 704, 706, 2, 9, 124, 2, 346, 711, 4, 8, 70, 1, 2, 714, 29, 70, 716, 6, 26, 407, 0, 407, 720, 0, 4, 159, 7, 8, 724, 6, 159, 726, 6, 8, 159, 2, 4, 730, 1, 158, 732, 158, 732, 735, 0, 734, 737, 2, 4, 335, 2, 5, 334, 3, 740, 742, 1, 92, 744, 2, 7, 72, 9, 402, 748, 0, 2, 29, 1, 158, 752, 0, 29, 146, 4, 7, 358, 8, 10, 759, 4, 8, 66, 8, 66, 763, 266, 763, 764, 5, 6, 274, 93, 170, 768, 2, 129, 158, 0, 4, 235, 2, 9, 774, 235, 248, 776, 5, 6, 78, 1, 4, 780, 7, 780, 782, 5, 6, 202, 9, 202, 786, 0, 3, 158, 6, 202, 791, 2, 159, 790, 0, 792, 795, 0, 9, 146, 2, 346, 799, 6, 8, 10, 4, 8, 92, 4, 6, 805, 0, 3, 806, 274, 805, 808, 29, 70, 154, 6, 8, 81, 1, 6, 814, 0, 7, 816, 815, 816, 818, 4, 8, 269, 2, 266, 823, 1, 268, 824, 0, 8, 81, 71, 146, 828, 0, 2, 71, 4, 6, 832, 70, 154, 835, 5, 6, 128, 4, 8, 839, 4, 8, 838, 838, 840, 843, 0, 4, 202, 2, 6, 203, 1, 4, 848, 5, 846, 850, 0, 9, 18, 59, 110, 854, 0, 4, 275, 4, 93, 274, 5, 858, 860, 1, 4, 66, 0, 7, 66, 129, 864, 866, 6, 8, 791, 0, 4, 870, 2, 4, 159, 790, 873, 874, 1, 78, 194, 2, 8, 99, 4, 7, 98, 1, 86, 98, 880, 882, 885, 8, 111, 170, 6, 111, 170, 112, 888, 891, 3, 8, 512, 0, 4, 894, 0, 7, 894, 512, 897, 898, 2, 4, 147, 6, 8, 903, 7, 146, 904, 0, 8, 903, 904, 906, 909, 2, 8, 98, 2, 268, 913, 1, 8, 18, 4, 194, 916, 1, 194, 918, 2, 4, 155, 0, 7, 922, 8, 465, 924, 2, 4, 334, 0, 95, 928, 3, 6, 72, 0, 9, 932, 4, 6, 935, 128, 932, 937, 1, 12, 740, 1, 2, 170, 5, 170, 942, 6, 8, 944, 7, 942, 946, 945, 946, 948, 2, 93, 158, 0, 95, 952, 6, 8, 93, 8, 92, 98, 0, 956, 959, 4, 8, 195, 2, 9, 194, 11, 962, 964, 4, 270, 539, 92, 538, 969, 0, 7, 146, 1, 92, 972, 1, 334, 928, 6, 8, 589, 3, 4, 978, 0, 4, 978, 588, 980, 983, 1, 4, 274, 4, 8, 159, 158, 986, 989, 2, 8, 155, 4, 6, 992, 1, 154, 994, 4, 992, 997, 0, 155, 996, 6, 998, 1001, 0, 99, 102, 6, 8, 1005, 2, 6, 266, 168, 268, 1009, 0, 6, 155, 154, 589, 1012, 1, 8, 158, 3, 4, 1016, 128, 159, 1018, 1, 6, 154, 2, 4, 1023, 8, 465, 1024, 4, 7, 18, 6, 589, 1028, 6, 124, 237, 4, 6, 82, 236, 1032, 1035, 8, 110, 368, 87, 236, 706, 3, 4, 70, 2, 29, 1042, 2, 6, 138, 28, 248, 1047, 3, 6, 48, 71, 146, 1050, 7, 8, 98, 0, 6, 99, 8, 99, 1056, 9, 1054, 1058, 4, 52, 81, 1, 4, 234, 0, 1063, 1064, 0, 3, 154, 66, 93, 1068, 99, 588, 740, 2, 8, 111, 49, 1050, 1074, 3, 358, 570, 0, 9, 570, 571, 1078, 1080, 2, 8, 124, 7, 124, 1084, 4, 8, 1086, 4, 1084, 1089, 8, 1089, 1090, 159, 248, 346, 0, 235, 1094, 0, 358, 589, 6, 589, 1098, 0, 8, 478, 2, 4, 81, 478, 1102, 1105, 4, 8, 81, 0, 3, 80, 334, 1109, 1110, 4, 71, 138, 5, 6, 1114, 235, 1114, 1116, 1, 6, 26, 2, 6, 26, 128, 1120, 1123, 3, 4, 52, 6, 52, 1127, 5, 8, 52, 2, 6, 53, 1129, 1130, 1132, 1, 4, 698, 128, 155, 1136, 87, 236, 646, 3, 6, 52, 5, 6, 52, 248, 1142, 1145, 10, 29, 70, 70, 147, 358, 7, 70, 1150, 2, 4, 86, 4, 18, 1155, 8, 87, 1156, 0, 8, 237, 6, 52, 236, 6, 236, 1161, 1160, 1163, 1164, 0, 3, 146, 6, 8, 1168, 6, 1168, 1171, 146, 1170, 1173, 0, 29, 274, 9, 334, 1176, 7, 8, 28, 0, 29, 1180, 1, 6, 1180, 9, 1182, 1184, 2, 8, 170, 1, 314, 1188, 0, 8, 87, 6, 86, 1193, 2, 6, 158, 0, 154, 1197, 1, 2, 158, 155, 1198, 1200, 19, 234, 266, 4, 8, 235, 0, 6, 234, 3, 4, 1208, 6, 1206, 1211, 1, 6, 922, 8, 154, 1215, 9, 1214, 1216, 155, 1216, 1218, 2, 9, 368, 4, 7, 1222, 4, 9, 368, 6, 1224, 1227, 3, 28, 288, 4, 86, 237, 2, 4, 1233, 86, 1233, 1234 } }; + inline static const std::vector nodes10{ { 4, 222, 10, 17, 21, 25, 29, 33, 35, 17, 29, 21, 37, 10, 46, 56, 66, 70, 74, 78, 82, 84, 88, 90, 10, 100, 108, 112, 116, 118, 122, 126, 130, 140, 144, 10, 153, 159, 163, 167, 171, 175, 177, 181, 185, 189, 10, 194, 198, 198, 204, 206, 208, 212, 204, 214, 206, 2, 224, 230, 10, 240, 250, 258, 264, 276, 280, 286, 294, 296, 300, 10, 308, 316, 320, 326, 330, 334, 336, 340, 344, 350, 4, 352, 360, 362, 364, 10, 374, 384, 390, 392, 398, 404, 414, 398, 392, 422, 10, 431, 439, 443, 453, 463, 473, 481, 489, 499, 443, 2, 506, 510, 10, 514, 518, 526, 530, 536, 538, 542, 546, 550, 552, 4, 557, 559, 561, 563, 9, 571, 579, 587, 593, 599, 607, 613, 617, 623, 2, 626, 632, 10, 639, 643, 639, 643, 649, 651, 653, 655, 655, 649, 10, 663, 673, 679, 685, 689, 693, 697, 701, 705, 709, 10, 718, 728, 738, 744, 754, 758, 764, 768, 776, 780, 2, 783, 785, 10, 792, 800, 804, 812, 804, 816, 824, 824, 832, 840, 10, 846, 852, 856, 860, 864, 868, 872, 876, 878, 882, 10, 889, 893, 899, 903, 909, 913, 917, 923, 929, 935, 10, 937, 941, 943, 945, 949, 953, 955, 957, 961, 965, 10, 970, 980, 988, 996, 1000, 1006, 1014, 1024, 1032, 1038, 10, 1044, 1046, 1050, 1052, 1056, 1060, 1062, 1064, 1068, 1062, 10, 1070, 1074, 1080, 1070, 1082, 1084, 1088, 1096, 1102, 1108, 10, 1111, 1115, 1119, 1123, 1129, 1131, 1135, 1141, 1145, 1149, 10, 1156, 1164, 1168, 1170, 1174, 1178, 1180, 1182, 1184, 1186, 2, 1191, 1193, 10, 1197, 1203, 1205, 1209, 1211, 1203, 1215, 1217, 1221, 1223, 10, 1228, 1236, 1240, 1244, 1252, 1256, 1264, 1268, 1274, 1278, 10, 1283, 1287, 1293, 1297, 1305, 1311, 1319, 1323, 1325, 1327, 8, 1332, 1338, 1344, 1350, 1358, 1364, 1368, 1374, 10, 1378, 1380, 1384, 1388, 1394, 1396, 1402, 1406, 1410, 1412, 10, 1418, 1422, 1428, 1432, 1436, 1440, 1442, 1444, 1448, 1452, 2, 1458, 1462, 10, 1469, 1473, 1477, 1483, 1487, 1489, 1493, 1487, 1497, 1501, 7, 875, 1503, 1507, 1509, 845, 1511, 1513, 8, 1516, 1524, 1530, 1538, 1542, 1550, 1558, 1566, 10, 1573, 1577, 1581, 1589, 1593, 1597, 1601, 1607, 1611, 1615, 10, 1620, 1630, 1636, 1642, 1648, 1654, 1658, 1664, 1668, 1674, 10, 1681, 1685, 1691, 1697, 1701, 1707, 1711, 1713, 1717, 1719, 10, 1724, 1728, 1732, 1734, 1738, 1744, 1750, 1752, 1760, 1764, 2, 1768, 1772, 10, 1778, 1782, 1786, 1790, 1798, 1806, 1810, 1812, 1816, 1822, 6, 1825, 1829, 1833, 1837, 1841, 1843, 10, 1845, 1849, 1853, 1859, 1863, 1865, 1869, 1875, 1879, 1885, 10, 1888, 1892, 1898, 1904, 1908, 1912, 1920, 1926, 1934, 1938, 10, 1944, 1948, 1956, 1962, 1962, 1972, 1976, 1986, 1990, 1994, 10, 2002, 2006, 2010, 2016, 2020, 2024, 2016, 2028, 2032, 2036, 10, 2042, 2044, 2046, 2048, 2050, 2052, 2054, 2056, 2058, 2060, 9, 2063, 2063, 2067, 2069, 2071, 2073, 2069, 2077, 2071, 2, 2079, 2081, 10, 2085, 809, 809, 2089, 2085, 789, 2093, 797, 797, 2097, 10, 2100, 2104, 2108, 2114, 2120, 2124, 2130, 2134, 2136, 2140, 10, 2143, 2145, 2147, 2151, 2153, 2155, 2159, 2161, 2165, 2167, 10, 2172, 2176, 2172, 2182, 2186, 2190, 2190, 2194, 2186, 2200, 10, 2207, 2211, 2217, 2225, 2227, 2231, 2233, 2237, 2239, 2241, 10, 2245, 2249, 2251, 2253, 2255, 2259, 2263, 2267, 2269, 2271, 10, 2277, 2281, 2283, 2285, 2287, 2289, 2293, 2297, 2301, 2303, 10, 2309, 2315, 2321, 2329, 2331, 2335, 2331, 2337, 2343, 2345, 1, 0, 10, 2351, 2355, 2359, 2363, 2371, 2375, 2379, 2381, 2383, 2387, 10, 2391, 2395, 2399, 2403, 2407, 2411, 2413, 2417, 2419, 2425, 10, 2431, 2435, 2441, 2445, 2447, 2447, 2445, 2453, 2459, 2465, 10, 2468, 2474, 2478, 2482, 2484, 2486, 2490, 2492, 2494, 2498, 7, 2503, 2505, 2507, 2511, 2513, 2515, 2521, 10, 2527, 2533, 2535, 2541, 2543, 2545, 2549, 2551, 2555, 2557, 10, 2560, 2564, 2566, 2568, 2570, 2578, 2584, 2590, 2594, 2596, 10, 2601, 2603, 2607, 2609, 2611, 2603, 2615, 2617, 2619, 2617, 4, 2621, 2623, 1535, 1547, 5, 2626, 2632, 2636, 2640, 2632, 10, 2644, 2650, 2656, 2660, 2662, 2666, 2670, 2672, 2674, 2678, 10, 2683, 2687, 2693, 2697, 2701, 2707, 2711, 2717, 2721, 2729, 10, 2732, 2738, 2746, 2752, 2758, 2764, 2772, 2780, 2786, 2790, 10, 2794, 2800, 2806, 2808, 2810, 2814, 2816, 2822, 2828, 2830, 8, 2832, 2834, 2832, 2834, 2834, 2832, 2832, 2834, 10, 2837, 2841, 2845, 2847, 2849, 2851, 2855, 2857, 2859, 2861, 10, 2867, 2877, 2885, 2889, 2893, 2897, 2905, 2911, 2915, 2919, 10, 2923, 2931, 2937, 2941, 2947, 2953, 2959, 2963, 2969, 2971, 10, 2972, 2978, 2980, 2982, 2972, 2986, 2988, 2994, 2998, 2982, 10, 3006, 3014, 3018, 3022, 3026, 3030, 3034, 3038, 3042, 3046, 5, 3049, 3051, 3053, 3057, 3059, 1, 9, 10, 3065, 3069, 3077, 3069, 3081, 3087, 3095, 3099, 3081, 3103, 10, 3104, 3112, 3114, 3120, 3122, 3128, 3136, 3138, 3142, 3144, 1, 657, 10, 3146, 3154, 3160, 3164, 3172, 3180, 3186, 3190, 3192, 3194, 10, 3196, 3200, 3202, 3204, 3206, 3214, 3218, 3220, 3222, 3228, 6, 3233, 3235, 3237, 3239, 3241, 3243, 10, 3246, 3248, 3250, 3252, 3254, 3256, 3260, 3262, 3268, 3270, 10, 3273, 3277, 487, 3281, 3283, 3285, 3287, 3291, 3293, 441, 2, 3294, 3294, 10, 3296, 3302, 3306, 3310, 3314, 3296, 3318, 3324, 3330, 3332, 10, 3335, 3339, 3341, 3345, 3349, 3351, 3355, 3359, 3361, 3365, 10, 3366, 3368, 3370, 3372, 3376, 3378, 3366, 3378, 3380, 3370, 10, 3386, 3392, 3396, 3400, 3410, 3414, 3418, 3426, 3432, 3436, 10, 3440, 3442, 3446, 3452, 3454, 3458, 3462, 3464, 3468, 3474, 1, 191, 6, 3477, 3479, 3477, 3485, 3477, 3491, 10, 3493, 3495, 3497, 3499, 3503, 3507, 3511, 3513, 3515, 3519, 10, 3524, 3530, 3534, 3540, 3548, 3556, 3562, 3570, 3574, 3582, 10, 3584, 3586, 3588, 3590, 3584, 3592, 3594, 3596, 3598, 3600, 10, 3604, 3608, 3612, 3616, 3618, 3622, 3628, 3634, 3640, 3646, 10, 3653, 3653, 3657, 3663, 3665, 3667, 3669, 3671, 3673, 3679, 4, 1997, 1425, 2133, 2041, 10, 3680, 3682, 3684, 3688, 3690, 3694, 3696, 3700, 3702, 3704, 10, 3706, 3712, 3716, 3720, 3724, 3728, 3706, 3734, 3736, 3740, 10, 3744, 3748, 3752, 3756, 3760, 3764, 3766, 3772, 3776, 3782, 10, 3788, 3792, 3796, 3800, 3802, 3806, 3792, 3808, 3814, 3800, 10, 3820, 3824, 3830, 3834, 3836, 3838, 3844, 3848, 3852, 3860, 10, 3868, 3872, 3880, 3884, 3892, 3900, 3908, 3916, 3922, 3926, 10, 3929, 3933, 3939, 3941, 3943, 3947, 3949, 3951, 3953, 3957, 10, 3963, 3973, 3981, 3989, 3995, 4001, 4009, 4013, 4019, 4025, 10, 4027, 4033, 4027, 4039, 4047, 4051, 4055, 4061, 4065, 4067, 10, 4071, 4075, 4077, 4081, 4083, 4087, 4091, 4087, 4077, 4097, 3, 4099, 4101, 4103, 2, 4105, 4107, 10, 4108, 4110, 4112, 4116, 4120, 4124, 4130, 4136, 4142, 4148, 10, 4153, 4157, 4161, 4165, 4169, 4173, 4177, 4179, 4181, 4187, 10, 4188, 4192, 4194, 4196, 4198, 4200, 4204, 4206, 4198, 4200, 1, 4210, 10, 4214, 4216, 4222, 4226, 4230, 4234, 4238, 4242, 4246, 4252, 6, 4257, 4259, 4261, 4265, 4267, 4269, 10, 4270, 4276, 4280, 4284, 4288, 4294, 4294, 4270, 4288, 4300, 10, 4302, 4304, 4306, 4308, 4310, 4316, 4320, 4324, 4326, 4330, 10, 4332, 4336, 4340, 4346, 4350, 4356, 4360, 4364, 4368, 4374, 10, 4382, 4388, 4394, 4400, 4404, 4408, 4414, 4416, 4418, 4422, 10, 4433, 4437, 4447, 4453, 4457, 4459, 4465, 4467, 4471, 4475, 5, 4480, 4486, 4490, 4496, 4498, 10, 4501, 4505, 4507, 4509, 4511, 4513, 4517, 4519, 4525, 4529, 2, 4530, 4532, 10, 4537, 4539, 4541, 4543, 4545, 4549, 4551, 4555, 4559, 4563, 10, 4568, 4574, 4578, 4582, 4588, 4594, 4602, 4608, 4588, 4594, 10, 4610, 4612, 4614, 4616, 4618, 4622, 4624, 4626, 4628, 4630, 10, 4635, 4637, 4639, 4641, 4643, 4645, 4649, 4651, 4653, 4655, 10, 4660, 4664, 4666, 4672, 4678, 4682, 4688, 4692, 4696, 4700, 10, 4705, 4707, 4709, 4711, 4715, 4719, 4723, 4727, 4729, 4731, 10, 4735, 4737, 4739, 4741, 4743, 4743, 4739, 4737, 4745, 4745, 1, 927, 10, 4751, 4755, 4757, 4763, 4765, 4769, 4773, 4775, 4773, 4775, 10, 4777, 4781, 4785, 4787, 4789, 4793, 4795, 4797, 4799, 4803, 1, 19, 10, 4808, 4812, 4816, 4824, 4830, 4834, 4838, 4842, 4850, 4858, 4, 4860, 4862, 4864, 4866, 10, 4870, 4876, 4880, 4886, 4888, 4894, 4896, 4902, 4908, 4914, 10, 4921, 4927, 4931, 4935, 4937, 4927, 4943, 4949, 4953, 4959, 7, 4963, 4967, 4971, 4973, 4967, 4975, 4979, 2, 4982, 4984, 10, 4990, 4994, 4996, 5002, 5004, 5010, 5014, 5018, 5024, 5028, 10, 5031, 5039, 5043, 5051, 5055, 5059, 5061, 5063, 5067, 5071, 10, 5077, 5081, 5085, 5089, 5085, 5089, 5093, 5097, 5101, 5105, 10, 5110, 5114, 5118, 5110, 5122, 5128, 5132, 5136, 5138, 5144, 10, 5149, 5155, 5157, 5161, 5163, 5167, 5171, 5173, 5177, 5181, 10, 5186, 5192, 5198, 5186, 5204, 5208, 5212, 5218, 5224, 5204, 10, 5227, 5229, 5231, 5233, 5235, 5237, 5239, 5241, 5243, 5239, 10, 5249, 5255, 5259, 5267, 5271, 5277, 5283, 5287, 5297, 5305, 10, 5312, 5314, 5318, 5322, 5330, 5336, 5340, 5344, 5348, 5352, 3, 5358, 5362, 5366, 10, 5373, 5381, 5387, 5391, 5393, 5397, 5397, 5407, 5417, 5421, 10, 5423, 5427, 5429, 5435, 5441, 5445, 5449, 5455, 5457, 5461, 10, 5463, 5467, 5473, 5479, 5485, 5489, 5493, 5497, 5505, 5509, 10, 5515, 5517, 5521, 5521, 5527, 5531, 5535, 5539, 5545, 5549, 10, 5550, 5556, 5560, 5564, 5564, 5560, 5566, 5570, 5572, 5576, 10, 5580, 5590, 5594, 5600, 5604, 5608, 5612, 5620, 5626, 5630, 5, 5635, 5639, 5635, 5639, 5641, 10, 5647, 5653, 5657, 5661, 5663, 5669, 5675, 5681, 5689, 5695, 8, 5696, 5698, 5696, 5702, 5706, 5698, 5710, 5714, 10, 5718, 5722, 5724, 5728, 5734, 5740, 5746, 5752, 5758, 5764, 10, 5770, 5774, 5778, 5784, 5788, 5790, 5794, 5798, 5800, 5806, 10, 5809, 5811, 5811, 5809, 5815, 5819, 5823, 5825, 5829, 5815, 10, 5833, 5835, 5835, 5839, 5841, 1207, 5843, 5845, 5847, 5845, 10, 5849, 5851, 5853, 5855, 5857, 5859, 5863, 5865, 5867, 5869, 10, 5875, 5883, 5887, 5893, 5897, 5901, 5907, 5913, 5915, 5917, 10, 5919, 5923, 5929, 5935, 5941, 5947, 5955, 5961, 5967, 5973, 10, 5979, 5985, 5991, 5997, 6003, 6009, 6015, 6021, 6025, 6025, 10, 6029, 6033, 6033, 6037, 6037, 5977, 829, 829, 6029, 821, 10, 6039, 6045, 6047, 6051, 6057, 6063, 6065, 6069, 6071, 6073, 9, 6078, 6084, 6086, 6088, 6092, 6094, 6096, 6098, 6102, 10, 6106, 6110, 6112, 6118, 6122, 6126, 6134, 6138, 6142, 6144, 7, 6151, 6155, 6157, 6157, 6161, 6163, 6155, 10, 6166, 6170, 6172, 6178, 6180, 6184, 6186, 6192, 6194, 6196, 10, 6207, 6211, 6217, 6221, 6231, 6237, 6241, 6249, 6253, 6259, 2, 1387, 4837, 10, + 6265, 6269, 6273, 6277, 6283, 6285, 6287, 6291, 6293, 6295, 10, 6296, 6298, 6300, 6304, 6306, 6308, 6310, 6296, 6312, 6314, 10, 6319, 6323, 6327, 6331, 6335, 6339, 6341, 6343, 6349, 6353, 2, 6357, 6361, 10, 6362, 6366, 6370, 6376, 6380, 6386, 6390, 6394, 6398, 6402, 10, 6408, 6414, 6420, 6426, 6432, 6438, 6442, 6450, 6456, 6460, 10, 6462, 6464, 6462, 6468, 6472, 6474, 6462, 6478, 6462, 6482, 10, 6489, 6493, 6495, 6497, 6499, 6503, 6505, 6507, 6499, 6509, 10, 6515, 6517, 6521, 6523, 6525, 6529, 6531, 6537, 6539, 6545, 1, 6550, 10, 6555, 6559, 6563, 6567, 6573, 6577, 6581, 6583, 6585, 6589, 10, 6592, 6594, 6602, 6608, 6614, 6618, 6620, 6626, 6632, 6636, 10, 6640, 6646, 6652, 6658, 6662, 6664, 6668, 6672, 6676, 6680, 10, 6683, 6689, 6691, 6697, 6703, 6709, 6715, 6721, 6723, 6725, 2, 6728, 6728, 10, 6731, 6737, 6741, 6749, 6755, 6759, 6763, 6769, 6775, 6781, 10, 6784, 6788, 6794, 6798, 6784, 6802, 6804, 6810, 6812, 6816, 10, 6823, 6831, 6837, 6845, 6851, 6857, 6865, 6869, 6879, 6885, 10, 6886, 6892, 6898, 6902, 6906, 6910, 6912, 6914, 6916, 6920, 6, 6925, 6929, 6933, 6935, 6937, 6941, 4, 6945, 6947, 6949, 6953, 4, 6954, 6956, 6958, 6960, 10, 6964, 6968, 6970, 6974, 6980, 6984, 6988, 6990, 6994, 6998, 10, 7003, 7009, 7011, 7015, 7023, 7027, 7029, 7031, 7035, 7039, 10, 7046, 7054, 7060, 7064, 7068, 7072, 7076, 7082, 7088, 7096, 10, 7103, 7107, 7115, 7119, 7123, 7127, 7131, 7133, 7135, 7139, 10, 7142, 7144, 7148, 7154, 7158, 7162, 7166, 7172, 7176, 7180, 10, 7186, 7190, 7196, 7200, 7206, 7208, 7214, 7218, 7222, 7226, 10, 7229, 7231, 7233, 7235, 7237, 7239, 7241, 7243, 7245, 7247, 10, 7248, 7250, 7252, 7254, 7250, 7254, 7252, 5760, 5760, 7256, 10, 7262, 7268, 7272, 7276, 7280, 7284, 7286, 7288, 7290, 7292, 5, 7295, 7297, 7299, 7301, 7303, 9, 7305, 7307, 7311, 7317, 7319, 7323, 7329, 7333, 7339, 10, 7343, 7349, 7349, 7355, 7359, 7363, 7367, 7371, 7349, 7375, 7, 7381, 7383, 7387, 7391, 7393, 7399, 7403, 10, 7405, 7407, 7405, 7411, 7413, 7415, 7417, 7419, 7413, 7421, 10, 7425, 7431, 7437, 7441, 7443, 7447, 7453, 7459, 7463, 7467, 1, 2, 4, 7, 8, 10, 6, 8, 11, 9, 12, 14, 6, 8, 10, 6, 12, 19, 8, 10, 13, 6, 12, 23, 6, 9, 10, 7, 14, 26, 7, 8, 26, 11, 26, 30, 11, 12, 26, 10, 14, 19, 0, 4, 7, 2, 4, 6, 9, 38, 40, 8, 38, 40, 8, 42, 45, 0, 3, 4, 0, 2, 6, 9, 48, 50, 8, 48, 50, 8, 52, 55, 0, 5, 6, 3, 4, 6, 9, 58, 60, 8, 58, 60, 8, 62, 65, 48, 50, 53, 8, 52, 69, 58, 60, 63, 8, 62, 73, 8, 49, 50, 48, 55, 76, 8, 59, 60, 58, 65, 80, 51, 52, 76, 9, 48, 76, 51, 76, 86, 61, 62, 80, 2, 4, 8, 3, 6, 8, 0, 93, 94, 0, 92, 94, 92, 96, 99, 5, 6, 8, 0, 40, 103, 0, 41, 102, 1, 104, 106, 0, 92, 95, 1, 96, 110, 1, 40, 102, 41, 104, 114, 103, 106, 114, 1, 40, 106, 103, 106, 120, 1, 92, 94, 95, 96, 124, 1, 92, 96, 95, 96, 128, 2, 5, 8, 2, 6, 9, 0, 132, 135, 0, 133, 134, 1, 136, 138, 1, 132, 134, 135, 138, 142, 2, 5, 6, 3, 8, 146, 2, 8, 146, 2, 148, 151, 7, 8, 40, 6, 8, 40, 6, 154, 157, 6, 8, 41, 9, 154, 160, 6, 9, 40, 41, 154, 164, 6, 9, 154, 41, 154, 168, 2, 9, 146, 147, 148, 172, 7, 160, 164, 2, 8, 147, 3, 172, 178, 8, 146, 149, 2, 148, 183, 8, 40, 155, 6, 154, 187, 1, 8, 40, 8, 40, 191, 0, 190, 193, 0, 8, 40, 0, 190, 197, 0, 8, 41, 0, 9, 40, 1, 200, 202, 9, 190, 200, 41, 190, 202, 0, 9, 190, 41, 190, 210, 40, 197, 200, 0, 2, 8, 0, 5, 8, 2, 6, 218, 4, 7, 218, 217, 220, 222, 3, 6, 216, 4, 6, 217, 218, 227, 228, 0, 7, 8, 2, 4, 232, 5, 6, 232, 2, 216, 236, 234, 236, 239, 1, 2, 8, 5, 8, 242, 0, 6, 245, 8, 41, 246, 242, 245, 248, 0, 9, 92, 7, 8, 252, 6, 41, 254, 1, 252, 256, 2, 4, 255, 6, 254, 261, 1, 252, 262, 0, 2, 4, 0, 9, 266, 7, 8, 268, 2, 4, 271, 6, 270, 273, 1, 268, 274, 6, 41, 270, 1, 268, 278, 0, 6, 9, 8, 41, 282, 1, 252, 284, 0, 9, 234, 2, 4, 233, 6, 232, 291, 1, 288, 292, 1, 252, 292, 6, 41, 232, 1, 252, 298, 0, 4, 9, 7, 242, 302, 2, 4, 305, 6, 304, 307, 1, 4, 8, 0, 2, 9, 6, 311, 312, 41, 310, 314, 7, 310, 312, 5, 60, 318, 1, 8, 60, 5, 312, 322, 7, 60, 324, 7, 312, 322, 5, 60, 328, 6, 243, 302, 41, 242, 332, 41, 312, 322, 4, 6, 61, 312, 322, 339, 2, 4, 319, 6, 318, 343, 4, 6, 313, 1, 8, 346, 41, 312, 348, 41, 310, 312, 0, 3, 8, 0, 4, 8, 2, 4, 7, 354, 357, 358, 217, 218, 358, 41, 242, 302, 2, 6, 49, 3, 8, 48, 0, 4, 369, 6, 8, 370, 366, 368, 373, 0, 2, 5, 4, 6, 377, 5, 8, 376, 6, 8, 266, 378, 380, 383, 0, 2, 378, 6, 8, 386, 378, 380, 389, 366, 368, 383, 0, 6, 11, 1, 8, 10, 383, 394, 396, 0, 4, 366, 6, 8, 400, 366, 368, 403, 0, 6, 359, 7, 8, 358, 2, 4, 409, 0, 8, 410, 406, 408, 413, 2, 7, 48, 1, 48, 102, 2, 8, 48, 416, 418, 421, 1, 6, 266, 9, 10, 424, 10, 424, 427, 8, 426, 429, 1, 2, 60, 8, 338, 432, 9, 338, 432, 8, 435, 436, 8, 10, 424, 8, 426, 441, 2, 4, 11, 1, 6, 444, 9, 10, 446, 8, 10, 446, 8, 448, 451, 0, 5, 10, 2, 6, 455, 9, 10, 456, 8, 10, 456, 8, 458, 461, 0, 3, 10, 4, 6, 465, 9, 10, 466, 8, 10, 466, 8, 468, 471, 4, 6, 49, 9, 10, 474, 8, 10, 474, 8, 476, 479, 2, 6, 377, 9, 10, 482, 8, 10, 482, 8, 484, 487, 1, 2, 6, 4, 6, 490, 9, 10, 492, 8, 10, 492, 8, 494, 497, 1, 4, 6, 0, 9, 500, 2, 8, 500, 41, 502, 504, 3, 8, 40, 200, 500, 509, 4, 312, 491, 41, 490, 512, 5, 312, 490, 4, 41, 516, 1, 6, 358, 0, 9, 358, 3, 520, 522, 5, 358, 524, 5, 520, 522, 3, 358, 528, 2, 9, 376, 4, 6, 376, 60, 532, 535, 41, 520, 522, 4, 8, 48, 60, 416, 541, 3, 6, 48, 358, 541, 544, 2, 6, 517, 4, 516, 549, 41, 378, 532, 0, 4, 6, 1, 8, 554, 4, 8, 39, 6, 8, 59, 6, 8, 310, 4, 7, 266, 2, 9, 564, 6, 8, 564, 267, 566, 568, 1, 2, 38, 2, 8, 38, 6, 8, 38, 572, 575, 576, 2, 9, 266, 4, 6, 267, 6, 9, 266, 580, 582, 585, 2, 8, 267, 7, 8, 266, 564, 588, 591, 4, 7, 580, 6, 8, 580, 267, 594, 596, 1, 4, 312, 5, 6, 312, 6, 9, 312, 600, 602, 605, 3, 8, 38, 7, 8, 38, 572, 608, 611, 6, 8, 267, 582, 588, 615, 4, 6, 312, 6, 8, 312, 600, 619, 620, 2, 7, 356, 267, 282, 624, 0, 6, 8, 3, 6, 356, 376, 629, 630, 2, 4, 9, 0, 6, 635, 203, 634, 636, 0, 7, 634, 6, 203, 640, 1, 6, 634, 0, 8, 644, 41, 644, 646, 8, 191, 644, 0, 203, 644, 41, 196, 644, 1, 6, 8, 0, 4, 656, 3, 4, 8, 40, 659, 660, 2, 6, 8, 4, 9, 664, 1, 6, 666, 4, 664, 669, 8, 666, 671, 0, 6, 358, 4, 8, 675, 359, 634, 676, 0, 8, 147, 4, 6, 681, 132, 147, 682, 4, 8, 283, 359, 634, 686, 4, 8, 555, 359, 634, 690, 6, 9, 310, 132, 147, 694, 6, 9, 500, 132, 147, 698, 7, 8, 500, 359, 634, 702, 7, 8, 310, 359, 634, 706, 6, 8, 313, 4, 7, 710, 0, 5, 712, 312, 712, 714, 9, 710, 716, 0, 3, 134, 5, 8, 720, 1, 4, 722, 134, 722, 724, 7, 720, 726, 6, 8, 39, 0, 3, 730, 6, 9, 38, 2, 730, 734, 7, 732, 736, 2, 9, 614, 7, 218, 266, 3, 740, 742, 0, 3, 6, 1, 8, 746, 5, 6, 748, 9, 266, 746, 7, 750, 752, 4, 7, 748, 5, 752, 756, 0, 5, 590, 9, 590, 746, 1, 760, 762, 1, 4, 590, 5, 762, 766, 6, 8, 565, 0, 3, 770, 9, 266, 772, 7, 770, 774, 9, 574, 732, 7, 730, 778, 8, 48, 490, 8, 10, 746, 6, 41, 358, 1, 8, 786, 0, 8, 786, 0, 788, 791, 3, 146, 358, 1, 8, 794, 0, 8, 794, 0, 796, 799, 8, 786, 789, 0, 788, 803, 4, 41, 146, 1, 8, 806, 8, 806, 809, 0, 808, 811, 0, 8, 806, 0, 808, 815, 1, 48, 376, 6, 8, 819, 6, 8, 818, 818, 820, 823, 5, 10, 48, 6, 8, 827, 6, 8, 826, 826, 828, 831, 5, 10, 464, 6, 8, 835, 6, 8, 834, 834, 836, 839, 2, 355, 500, 2, 354, 500, 354, 842, 845, 8, 313, 500, 9, 312, 848, 501, 848, 850, 3, 354, 842, 501, 842, 854, 3, 354, 500, 501, 842, 858, 9, 312, 500, 501, 848, 862, 2, 354, 501, 3, 842, 866, 8, 312, 501, 9, 848, 870, 8, 312, 500, 312, 848, 875, 2, 845, 858, 354, 500, 859, 2, 858, 881, 4, 7, 10, 5, 10, 746, 8, 884, 886, 7, 8, 132, 48, 490, 890, 5, 6, 490, 7, 8, 894, 48, 490, 896, 2, 8, 41, 48, 490, 900, 4, 6, 9, 2, 8, 905, 48, 490, 906, 5, 8, 358, 48, 490, 910, 7, 8, 146, 48, 490, 914, 4, 7, 490, 5, 8, 918, 48, 490, 920, 2, 8, 376, 4, 6, 8, 51, 924, 926, 6, 8, 48, 1, 2, 930, 905, 930, 932, 8, 10, 656, 2, 6, 48, 1, 8, 938, 4, 132, 656, 2, 656, 660, 0, 6, 10, 1, 8, 946, 4, 6, 50, 1, 8, 950, 8, 634, 641, 1, 8, 534, 0, 6, 634, 1, 8, 958, 1, 2, 660, 6, 8, 962, 0, 2, 474, 8, 48, 475, 421, 966, 968, 2, 7, 8, 0, 3, 972, 4, 7, 974, 2, 973, 976, 357, 974, 978, 9, 38, 664, 0, 8, 983, 38, 665, 984, 39, 982, 986, 9, 38, 50, 3, 8, 990, 6, 39, 992, 7, 990, 994, 39, 50, 992, 7, 990, 998, 7, 8, 990, 2, 39, 1002, 3, 990, 1004, 8, 747, 904, 2, 4, 1008, 0, 8, 1011, 9, 1008, 1012, 4, 6, 747, 8, 747, 1016, 2, 4, 1018, 0, 8, 1021, 9, 1018, 1022, 6, 9, 48, 0, 2, 1026, 4, 7, 8, 421, 1028, 1030, 217, 218, 904, 0, 2, 1034, 7, 1034, 1036, 0, 3, 146, 4, 9, 146, 7, 1040, 1042, 7, 26, 394, 6, 9, 634, 7, 636, 1048, 9, 12, 394, 7, 8, 634, 9, 636, 1054, 0, 7, 10, 9, 394, 1058, 9, 636, 640, 9, 14, 1058, 6, 8, 635, 9, 640, 1066, 60, 376, 383, 0, 5, 490, 48, 629, 1072, 9, 358, 656, 2, 4, 1077, 0, 1076, 1079, 376, 544, 629, 282, 445, 1058, 2, 60, 267, 0, 383, 1086, 0, 8, 359, 6, 358, 1091, 2, 4, 1093, 0, 1092, 1095, 9, 358, 520, 2, 4, 1099, 0, 1098, 1101, 6, 358, 629, 2, 4, 1105, 0, 1104, 1107, 216, 313, 500, 0, 2, 60, 60, 216, 1113, 0, 2, 500, 216, 500, 1117, 0, 3, 432, 8, 432, 1120, 0, 3, 500, 1, 2, 1124, 8, 1124, 1126, 3, 216, 520, 3, 6, 38, 1, 216, 1132, 4, 7, 216, 3, 6, 1136, 1, 216, 1138, 1, 6, 1136, 3, 216, 1142, 1, 4, 146, 3, 216, 1146, 3, 8, 60, 0, 61, 1150, 1, 2, 1152, 9, 1152, 1154, 0, 3, 132, 0, 7, 132, 1, 2, 1160, 9, 1158, 1162, 3, 4, 58, 312, 354, 1167, 312, 354, 631, 1, 6, 92, 242, 312, 1173, 0, 9, 242, 242, 1173, 1176, 242, 312, 433, 242, 312, 1147, 242, 1147, 1176, 312, 354, 1125, 0, 2, 7, 8, 10, 1189, 1, 660, 664, 0, 283, 634, 1, 636, 1194, 7, 628, 634, 6, 628, 635, 1, 1198, 1200, 635, 644, 1198, 7, 628, 644, 635, 644, 1206, 1, 636, 1198, 1, 6, 1198, 635, 1198, 1212, 1, 640, 1200, 8, 634, 657, 1, 636, 1218, 635, 644, 1218, 0, 6, 148, 0, 7, 148, 6, 1225, 1226, 2, 6, 660, 1, 8, 1230, 0, 9, 1232, 1231, 1232, 1234, 0, 9, 1230, 1231, 1232, 1238, 1, 6, 148, 0, 1225, 1242, 5, 8, 60, 0, 6, 1246, 1, 6, 1246, 0, 1249, 1250, 1, 8, 634, 282, 645, 1254, 2, 4, 282, 1, 6, 1258, 1, 8, 1258, 282, 1261, 1262, 0, 7, 40, 202, 232, 1267, 0, 7, 290, 0, 9, 290, 232, 1271, 1272, 0, 8, 1230, 0, 1232, 1277, 2, 5, 356, 60, 1113, 1280, 5, 356, 490, 1, 48, 1284, 4, 9, 48, 0, 2, 1288, 48, 366, 1291, 2, 6, 1289, 48, 1291, 1294, 4, 9, 746, 0, 2, 1298, + 2, 4, 747, 746, 1301, 1302, 5, 8, 10, 0, 2, 1307, 10, 394, 1309, 5, 8, 490, 0, 2, 1313, 0, 4, 491, 490, 1315, 1316, 5, 216, 490, 1, 48, 1320, 3, 10, 1320, 3, 10, 1284, 1, 6, 132, 4, 6, 132, 302, 1328, 1331, 0, 6, 243, 4, 7, 242, 357, 1334, 1336, 0, 7, 242, 4, 6, 243, 357, 1340, 1342, 0, 6, 242, 5, 6, 242, 302, 1347, 1348, 2, 8, 303, 1, 6, 1352, 4, 6, 1352, 302, 1354, 1357, 0, 6, 1352, 5, 6, 1352, 302, 1361, 1362, 4, 6, 133, 357, 1160, 1366, 0, 6, 133, 4, 7, 132, 357, 1370, 1372, 5, 134, 310, 7, 720, 1376, 357, 1334, 1340, 0, 7, 102, 51, 134, 1382, 6, 242, 356, 242, 1334, 1387, 6, 242, 357, 0, 7, 1390, 243, 1390, 1392, 0, 1387, 1390, 7, 8, 312, 6, 218, 313, 9, 1398, 1400, 1, 102, 134, 7, 720, 1404, 1, 134, 722, 7, 720, 1408, 7, 1334, 1390, 5, 312, 664, 3, 4, 1414, 7, 1414, 1416, 3, 522, 926, 5, 358, 1420, 6, 8, 358, 3, 522, 1424, 5, 358, 1426, 5, 522, 1424, 3, 358, 1430, 5, 312, 1424, 3, 358, 1434, 5, 522, 664, 3, 358, 1438, 3, 358, 1414, 7, 60, 1414, 2, 155, 200, 4, 41, 1446, 4, 155, 200, 2, 41, 1450, 3, 6, 1336, 4, 8, 1455, 0, 1336, 1457, 4, 8, 1188, 0, 1336, 1461, 2, 9, 1370, 0, 4, 1465, 6, 1371, 1466, 1, 132, 608, 6, 38, 1470, 8, 267, 572, 6, 38, 1474, 2, 8, 58, 0, 134, 1479, 4, 58, 1481, 0, 4, 135, 132, 500, 1484, 38, 582, 588, 1, 6, 1484, 132, 1484, 1490, 0, 6, 132, 1, 1484, 1494, 1, 4, 134, 135, 1494, 1498, 6, 38, 242, 0, 4, 242, 6, 242, 1504, 0, 242, 500, 8, 500, 1116, 4, 58, 242, 3, 6, 310, 312, 927, 1514, 1, 4, 354, 2, 7, 1518, 5, 6, 354, 9, 1520, 1522, 3, 4, 242, 0, 7, 1526, 9, 1348, 1528, 0, 3, 102, 1, 4, 102, 2, 7, 1534, 9, 1532, 1536, 1, 6, 660, 312, 927, 1540, 0, 3, 1030, 1, 6, 1030, 2, 5, 1546, 9, 1544, 1548, 1, 2, 1030, 3, 6, 1030, 0, 5, 1554, 9, 1552, 1556, 1, 2, 102, 3, 4, 102, 0, 7, 1562, 9, 1560, 1564, 4, 6, 355, 6, 92, 1569, 8, 1568, 1571, 3, 6, 132, 7, 1568, 1574, 6, 8, 93, 7, 1568, 1578, 3, 8, 554, 4, 7, 1582, 4, 6, 1582, 6, 1584, 1587, 4, 146, 1569, 8, 1568, 1591, 6, 358, 1569, 8, 1568, 1595, 2, 60, 1569, 8, 1568, 1599, 4, 6, 354, 2, 1569, 1602, 8, 1568, 1605, 2, 926, 1569, 8, 1568, 1609, 5, 6, 1582, 4, 1587, 1612, 8, 312, 927, 6, 313, 1616, 302, 1616, 1618, 4, 8, 313, 5, 6, 1622, 0, 9, 1624, 4, 312, 1626, 7, 1624, 1628, 4, 312, 1624, 0, 9, 1632, 7, 1624, 1634, 2, 5, 310, 6, 310, 1638, 0, 383, 1640, 0, 2, 383, 4, 6, 1644, 218, 383, 1646, 0, 2, 346, 4, 8, 1651, 282, 347, 1652, 6, 8, 1651, 302, 347, 1656, 7, 102, 312, 6, 313, 1660, 302, 1660, 1662, 6, 102, 313, 302, 1660, 1666, 5, 312, 1030, 6, 313, 1670, 302, 1670, 1672, 5, 6, 216, 0, 146, 1677, 4, 1676, 1679, 6, 216, 267, 1, 38, 1682, 0, 5, 1146, 6, 8, 1686, 147, 1146, 1688, 6, 9, 376, 0, 2, 1692, 376, 500, 1695, 0, 2, 590, 267, 500, 1698, 2, 6, 376, 0, 9, 1702, 376, 500, 1705, 0, 4, 146, 216, 500, 1709, 216, 500, 675, 0, 4, 147, 102, 1146, 1714, 406, 520, 1030, 8, 377, 490, 0, 9, 1720, 501, 1720, 1722, 4, 8, 58, 0, 1720, 1727, 0, 8, 500, 0, 1720, 1731, 0, 577, 1720, 0, 4, 753, 752, 1030, 1737, 0, 134, 1281, 6, 8, 357, 7, 1740, 1742, 2, 6, 357, 0, 133, 1746, 7, 1742, 1748, 7, 138, 1742, 6, 92, 377, 0, 8, 1754, 1, 8, 1754, 0, 1757, 1758, 6, 8, 1737, 7, 752, 1762, 4, 9, 376, 483, 656, 1766, 2, 9, 48, 475, 656, 1770, 6, 8, 217, 2, 5, 1774, 0, 1677, 1776, 2, 7, 558, 0, 575, 1780, 2, 5, 560, 0, 1479, 1784, 6, 8, 1527, 0, 1348, 1789, 2, 8, 39, 2, 5, 38, 6, 8, 1794, 0, 1792, 1797, 2, 8, 59, 2, 7, 58, 4, 8, 1802, 0, 1800, 1805, 3, 4, 656, 312, 501, 1808, 312, 501, 1514, 6, 8, 377, 312, 501, 1814, 4, 8, 217, 2, 7, 1818, 0, 1137, 1820, 61, 500, 1730, 1, 8, 1802, 4, 58, 1826, 1, 8, 1794, 6, 38, 1830, 0, 8, 60, 61, 500, 1834, 0, 9, 60, 4, 58, 1839, 6, 38, 1839, 216, 432, 1113, 3, 216, 432, 61, 432, 1846, 3, 60, 216, 61, 432, 1850, 5, 6, 48, 2, 7, 540, 1, 1854, 1856, 2, 61, 216, 1, 1850, 1860, 51, 500, 924, 2, 8, 1188, 267, 500, 1866, 0, 8, 1146, 5, 146, 1870, 147, 1146, 1872, 0, 2, 61, 1, 1850, 1876, 4, 7, 376, 6, 313, 376, 1, 1880, 1882, 3, 4, 282, 357, 1160, 1886, 5, 58, 132, 59, 1838, 1890, 0, 5, 318, 8, 312, 1895, 6, 318, 1897, 0, 2, 323, 9, 500, 1900, 322, 501, 1902, 7, 38, 1830, 6, 1833, 1906, 5, 58, 1826, 4, 1829, 1910, 0, 8, 501, 0, 2, 501, 9, 60, 1916, 1, 1914, 1918, 4, 6, 1839, 8, 312, 1923, 1, 1838, 1924, 0, 9, 346, 4, 6, 1929, 8, 312, 1931, 1, 1928, 1932, 6, 39, 1830, 38, 1833, 1936, 4, 146, 197, 4, 146, 196, 0, 1940, 1943, 2, 60, 197, 3, 1876, 1946, 4, 6, 41, 0, 3, 1950, 0, 2, 1951, 197, 1952, 1954, 6, 196, 358, 6, 197, 358, 0, 1959, 1960, 5, 6, 376, 0, 2, 1965, 6, 8, 1966, 4, 376, 1969, 377, 1964, 1970, 2, 4, 382, 500, 1644, 1975, 2, 4, 267, 0, 7, 1978, 8, 266, 1981, 0, 1978, 1982, 6, 1980, 1985, 0, 10, 382, 6, 1058, 1989, 2, 48, 382, 6, 416, 1993, 4, 8, 146, 6, 8, 147, 0, 2, 1999, 1997, 1998, 2000, 6, 103, 312, 3, 1660, 2004, 4, 312, 1031, 3, 1670, 2008, 2, 354, 927, 3, 354, 926, 9, 2012, 2014, 9, 312, 926, 3, 1616, 2018, 3, 8, 2018, 927, 2018, 2022, 2, 9, 2014, 927, 2014, 2026, 3, 6, 1660, 103, 1660, 2030, 3, 4, 1670, 1031, 1670, 2034, 2, 9, 60, 2, 8, 60, 0, 2038, 2041, 0, 432, 2041, 60, 1876, 2041, 0, 1146, 1997, 0, 520, 1425, 358, 406, 1425, 146, 1714, 1997, 48, 134, 927, 9, 1150, 1876, 3, 1876, 2038, 2, 60, 203, 0, 9, 338, 2, 60, 2065, 6, 203, 358, 4, 146, 203, 2, 203, 1950, 2, 4, 203, 6, 203, 2074, 8, 49, 60, 8, 146, 377, 2, 41, 60, 1, 8, 2082, 2, 60, 339, 1, 8, 2086, 5, 60, 358, 1, 8, 2090, 7, 60, 146, 1, 8, 2094, 3, 40, 1030, 5, 202, 2098, 4, 8, 147, 5, 202, 2102, 1, 8, 338, 0, 2041, 2106, 5, 8, 202, 2, 6, 2110, 202, 2110, 2113, 5, 8, 40, 2, 6, 2116, 202, 2116, 2119, 2, 4, 154, 154, 202, 2123, 7, 8, 202, 2, 4, 2126, 202, 2126, 2129, 6, 8, 92, 0, 190, 2133, 0, 190, 1425, 2, 6, 203, 132, 202, 2139, 1, 2, 1150, 1, 10, 14, 1, 8, 26, 2, 8, 61, 1, 60, 2148, 1, 8, 1042, 1, 146, 2102, 5, 8, 146, 1, 4, 2156, 1, 8, 2038, 6, 9, 358, 1, 8, 2162, 1, 6, 12, 5, 8, 58, 0, 3, 2168, 2, 1479, 2170, 8, 58, 2171, 2, 2170, 2175, 6, 102, 1561, 2, 8, 2178, 0, 1560, 2181, 0, 2, 2169, 1479, 2168, 2184, 1, 2, 2168, 0, 1479, 2188, 2, 9, 58, 59, 2170, 2192, 5, 8, 2192, 0, 3, 2196, 59, 2192, 2198, 5, 312, 346, 7, 8, 346, 346, 2202, 2204, 4, 7, 620, 313, 602, 2208, 6, 102, 312, 4, 7, 2212, 313, 2212, 2214, 4, 6, 501, 0, 2, 2218, 2, 9, 2218, 500, 2220, 2223, 346, 602, 2204, 4, 313, 602, 602, 1030, 2228, 346, 602, 1030, 4, 312, 1030, 313, 602, 2234, 60, 382, 555, 346, 605, 2202, 2, 5, 408, 4, 359, 2242, 3, 6, 2156, 2, 147, 2246, 8, 338, 2087, 8, 40, 807, 8, 40, 2083, 4, 41, 664, 8, 40, 2257, 6, 41, 92, 8, 40, 2261, 2, 41, 926, 8, 40, 2265, 8, 40, 1425, 8, 40, 787, 0, 9, 618, 4, 312, 2273, 6, 619, 2274, 4, 7, 312, 303, 346, 2278, 4, 203, 602, 4, 602, 2273, 203, 312, 346, 312, 346, 2273, 4, 6, 2273, 312, 2273, 2290, 4, 6, 203, 203, 312, 2294, 302, 312, 2279, 6, 2278, 2299, 6, 2273, 2278, 6, 8, 356, 2, 5, 2304, 1, 48, 2306, 0, 2, 1306, 6, 1306, 2310, 3, 10, 2312, 8, 10, 267, 0, 6, 2316, 10, 2316, 2318, 0, 8, 490, 3, 4, 2322, 2, 5, 2322, 1, 2324, 2326, 10, 660, 1964, 5, 6, 1188, 10, 660, 2332, 10, 544, 1306, 1, 2, 48, 5, 8, 2338, 544, 2338, 2340, 10, 660, 1072, 6, 8, 235, 2, 232, 2346, 4, 235, 2348, 2, 102, 232, 3, 290, 2352, 3, 4, 232, 102, 290, 2356, 3, 232, 290, 102, 290, 2360, 2, 5, 232, 6, 8, 2364, 2, 232, 2367, 4, 2364, 2369, 2, 103, 232, 4, 2364, 2373, 2, 102, 233, 102, 290, 2377, 233, 2352, 2356, 4, 2352, 2373, 3, 4, 2352, 233, 2352, 2384, 2, 4, 58, 2, 560, 2389, 1, 4, 906, 6, 906, 2392, 1, 4, 900, 6, 900, 2396, 1, 4, 910, 6, 910, 2400, 1, 4, 914, 6, 914, 2404, 1, 4, 890, 6, 890, 2408, 4, 890, 1328, 4, 7, 1328, 8, 1328, 2414, 8, 103, 1328, 2, 6, 39, 5, 6, 38, 8, 2420, 2423, 4, 7, 50, 4, 9, 50, 6, 2426, 2429, 5, 8, 346, 312, 346, 2432, 4, 6, 51, 5, 8, 2436, 50, 2436, 2438, 5, 6, 50, 8, 2436, 2442, 8, 346, 602, 0, 9, 10, 2, 6, 11, 4, 2449, 2450, 4, 8, 50, 5, 6, 2454, 51, 2454, 2456, 4, 8, 312, 5, 6, 2460, 313, 2460, 2462, 2, 4, 628, 0, 10, 2467, 2, 4, 629, 0, 3, 2470, 5, 2470, 2472, 2, 377, 628, 4, 376, 2477, 4, 49, 628, 2, 48, 2481, 3, 376, 2470, 5, 48, 2470, 2, 4, 14, 0, 10, 2489, 48, 376, 629, 0, 2467, 2470, 2, 5, 628, 48, 629, 2496, 2, 8, 1854, 1, 48, 2500, 1, 48, 1478, 3, 10, 1478, 3, 8, 10, 10, 58, 2508, 10, 58, 2193, 4, 267, 1478, 0, 6, 267, 2, 8, 2516, 4, 267, 2518, 1, 6, 1460, 2, 4, 2523, 1460, 2522, 2525, 0, 93, 1172, 6, 9, 2528, 1172, 2528, 2531, 500, 523, 1188, 3, 6, 1876, 4, 8, 1876, 1, 2536, 2538, 267, 424, 1460, 267, 424, 574, 3, 6, 376, 1, 1460, 2546, 1, 1460, 1854, 5, 6, 746, 1, 1460, 2552, 1, 574, 2546, 5, 8, 746, 302, 629, 2558, 0, 9, 1514, 310, 927, 2562, 310, 927, 1838, 232, 357, 1298, 357, 1030, 1838, 4, 9, 58, 0, 3, 2572, 5, 8, 2574, 59, 2572, 2576, 0, 5, 1030, 1, 60, 1030, 9, 2580, 2582, 0, 5, 322, 4, 7, 322, 9, 2586, 2588, 4, 9, 2170, 59, 2168, 2592, 302, 322, 927, 6, 8, 1979, 1, 266, 2598, 4, 14, 49, 6, 8, 2339, 4, 49, 2604, 4, 14, 465, 2, 14, 377, 1, 2, 14, 4, 14, 2612, 1, 14, 266, 1, 14, 444, 1, 8, 904, 1, 8, 500, 0, 5, 1424, 217, 358, 2624, 6, 8, 216, 0, 5, 2628, 217, 358, 2630, 4, 8, 521, 354, 358, 2635, 0, 3, 1424, 357, 358, 2638, 0, 9, 146, 267, 358, 2642, 1, 4, 282, 2, 7, 2646, 5, 1886, 2648, 4, 8, 267, 0, 6, 2653, 267, 358, 2654, 6, 48, 629, 5, 358, 2658, 7, 146, 2658, 6, 48, 628, 48, 146, 2665, 2, 283, 2646, 41, 282, 2668, 41, 282, 2648, 282, 1259, 2648, 2, 4, 746, 282, 358, 2677, 4, 8, 216, 60, 555, 2680, 2, 41, 1124, 8, 500, 2684, 4, 6, 1125, 2, 1124, 2689, 8, 500, 2690, 5, 358, 1124, 8, 500, 2694, 7, 146, 1124, 8, 500, 2698, 2, 5, 1124, 7, 1124, 2702, 8, 500, 2704, 1, 58, 358, 8, 1166, 2708, 8, 41, 500, 0, 2, 2712, 500, 2712, 2714, 1, 38, 146, 8, 1132, 2718, 0, 2, 927, 4, 6, 2723, 8, 927, 2724, 2722, 2724, 2726, 4, 7, 628, 974, 2470, 2731, 1, 2, 232, 267, 554, 2734, 9, 232, 2736, 3, 232, 376, 4, 6, 232, 9, 376, 2742, 1, 2740, 2744, 4, 6, 2740, 9, 376, 2748, 1, 2740, 2750, 2, 5, 354, 1, 1602, 2754, 9, 232, 2756, 7, 354, 376, 1, 6, 2760, 1766, 2760, 2762, 4, 282, 376, 3, 8, 2766, 7, 376, 2768, 1, 2766, 2770, 9, 376, 554, 3, 8, 2774, 7, 376, 2776, 1, 2774, 2778, 7, 8, 2774, 3, 376, 2782, 1, 2774, 2784, 8, 38, 233, 48, 2734, 2789, 1, 60, 242, 0, 2041, 2792, 0, 8, 2039, + 2, 61, 2796, 3, 2038, 2798, 9, 60, 1876, 3, 8, 2802, 61, 2802, 2804, 61, 1150, 2802, 0, 2041, 2142, 1, 6, 408, 0, 1425, 2812, 0, 1997, 2158, 8, 358, 1425, 0, 7, 2818, 6, 1425, 2820, 8, 146, 1997, 0, 5, 2824, 4, 1997, 2826, 94, 927, 2448, 146, 629, 1544, 41, 282, 1552, 8, 377, 1702, 0, 7, 266, 8, 266, 2839, 0, 7, 92, 8, 92, 2843, 8, 242, 554, 4, 8, 1348, 6, 8, 1336, 0, 6, 266, 1, 8, 2852, 8, 60, 242, 8, 266, 656, 8, 242, 926, 0, 2, 102, 4, 283, 2862, 102, 133, 2864, 3, 4, 134, 1, 2, 2868, 0, 7, 2868, 5, 2870, 2872, 8, 2868, 2874, 1, 2, 1366, 0, 7, 1366, 5, 2878, 2880, 8, 1366, 2882, 8, 38, 2878, 5, 1366, 2886, 8, 2878, 2880, 5, 1366, 2890, 8, 38, 2870, 5, 2868, 2894, 4, 9, 60, 0, 7, 2898, 8, 10, 2900, 5, 2898, 2902, 3, 4, 26, 8, 10, 38, 5, 2906, 2908, 9, 10, 160, 38, 160, 2912, 9, 10, 1066, 38, 1066, 2916, 10, 41, 50, 8, 40, 2921, 5, 8, 554, 3, 6, 2924, 2, 555, 2924, 4, 2926, 2928, 3, 4, 702, 2, 501, 702, 6, 2932, 2934, 8, 10, 50, 8, 40, 2939, 8, 147, 500, 2, 6, 2942, 501, 2942, 2944, 8, 359, 554, 2, 4, 2948, 555, 2948, 2950, 1, 8, 1188, 4, 6, 1188, 359, 2954, 2956, 1, 8, 38, 40, 573, 2960, 0, 5, 508, 0, 6, 509, 40, 2964, 2967, 61, 226, 1818, 7, 134, 1532, 5, 8, 134, 0, 3, 2974, 7, 134, 2976, 0, 665, 1560, 2, 665, 1532, 0, 2, 103, 102, 665, 2984, 9, 972, 1532, 5, 6, 972, 0, 3, 2990, 9, 972, 2992, 6, 8, 1533, 2, 1532, 2997, 0, 4, 95, 3, 94, 3000, 6, 8, 3000, 2, 3002, 3005, 2, 4, 657, 1, 656, 3008, 6, 8, 3008, 0, 3010, 3013, 0, 656, 3013, 3008, 3013, 3016, 2, 94, 3005, 3000, 3005, 3020, 0, 656, 3009, 3008, 3013, 3024, 2, 94, 3001, 3000, 3005, 3028, 6, 8, 3029, 3000, 3028, 3033, 6, 8, 3025, 3008, 3024, 3037, 7, 8, 3000, 9, 3028, 3040, 7, 8, 3008, 9, 3024, 3044, 216, 267, 500, 1, 1166, 1478, 10, 58, 313, 3, 4, 216, 1, 1676, 3054, 313, 376, 500, 1, 4, 1090, 2, 9, 358, 1090, 3060, 3062, 0, 4, 3062, 1, 1090, 3066, 0, 4, 94, 6, 94, 3071, 0, 8, 3072, 4, 3071, 3074, 1, 8, 358, 359, 3066, 3078, 2, 9, 3078, 0, 4, 3082, 359, 3078, 3084, 1, 4, 94, 5, 6, 3088, 0, 8, 3090, 95, 3088, 3092, 0, 8, 358, 8, 3066, 3097, 1, 8, 3066, 359, 3066, 3100, 354, 734, 1133, 2, 8, 501, 0, 3, 3106, 1, 2, 3108, 3107, 3108, 3110, 217, 1116, 1914, 0, 2, 228, 0, 8, 229, 217, 3116, 3118, 354, 1116, 1731, 0, 9, 504, 1, 8, 3124, 505, 3124, 3126, 3, 8, 500, 0, 501, 3130, 1, 500, 3132, 3131, 3132, 3134, 312, 1914, 1917, 2, 6, 38, 354, 577, 3140, 354, 1727, 2388, 0, 2082, 2255, 0, 2, 661, 5, 6, 3148, 4, 7, 3148, 3, 3150, 3152, 0, 2, 2103, 4, 7, 3156, 3, 146, 3158, 4, 7, 2000, 3, 146, 3162, 4, 8, 359, 0, 2, 3167, 5, 6, 3168, 3, 358, 3170, 6, 8, 359, 0, 2, 3175, 5, 6, 3176, 3, 358, 3178, 0, 2, 1151, 4, 7, 3182, 5, 60, 3184, 5, 6, 3182, 7, 60, 3188, 5, 60, 3152, 7, 60, 3150, 355, 2012, 2014, 4, 313, 1030, 5, 2008, 3198, 7, 1666, 2004, 1030, 2008, 2235, 6, 1660, 2213, 2, 9, 926, 0, 3, 3208, 1, 8, 3208, 927, 3210, 3212, 5, 8, 312, 618, 621, 3216, 312, 2235, 3198, 312, 1666, 2213, 0, 243, 926, 0, 242, 926, 242, 3224, 3227, 3, 6, 10, 8, 10, 3230, 6, 10, 135, 8, 10, 973, 3, 10, 664, 4, 267, 664, 1, 48, 664, 0, 7, 164, 41, 164, 3244, 11, 38, 134, 11, 904, 1188, 38, 661, 746, 0, 151, 172, 0, 157, 164, 4, 8, 60, 0, 2898, 3259, 58, 266, 665, 0, 5, 60, 8, 60, 3265, 4, 3264, 3267, 2, 151, 1040, 1, 40, 1996, 1, 2, 926, 8, 40, 3274, 1, 2, 1950, 8, 40, 3278, 8, 40, 432, 6, 10, 190, 6, 190, 358, 1, 4, 664, 8, 40, 3288, 8, 40, 1146, 41, 252, 1172, 232, 2428, 2455, 5, 50, 232, 4, 9, 3298, 51, 3298, 3300, 4, 51, 232, 9, 3298, 3304, 5, 8, 50, 9, 3304, 3308, 4, 8, 51, 9, 3298, 3312, 4, 50, 232, 232, 2428, 3317, 0, 7, 3308, 4, 51, 3320, 9, 3308, 3322, 0, 7, 3312, 5, 50, 3326, 9, 3312, 3328, 4, 2455, 3298, 1, 746, 2680, 1, 2, 218, 8, 490, 3337, 8, 746, 1159, 2, 8, 1316, 3, 490, 3342, 4, 8, 1072, 3, 490, 3346, 1, 216, 630, 3, 6, 2680, 1, 216, 3352, 1, 6, 2680, 3, 216, 3356, 3, 490, 2680, 0, 8, 1302, 1, 746, 3362, 0, 41, 1042, 0, 339, 2038, 0, 41, 2162, 0, 41, 1048, 2, 9, 1950, 0, 41, 3374, 0, 41, 2038, 0, 26, 41, 5, 8, 266, 2, 614, 3383, 0, 383, 3384, 0, 7, 3274, 2, 628, 926, 6, 3388, 3391, 0, 6, 3275, 3274, 3391, 3394, 0, 146, 927, 665, 3274, 3398, 0, 2, 926, 6, 926, 3403, 1, 2, 3404, 6, 8, 3402, 0, 3406, 3409, 0, 3, 3404, 2, 3409, 3412, 0, 2, 3405, 3404, 3409, 3416, 4, 8, 266, 6, 267, 3420, 1, 2, 3422, 0, 383, 3424, 9, 3274, 3388, 2, 926, 3429, 6, 3388, 3431, 2, 746, 927, 665, 3274, 3434, 1, 2, 2156, 0, 151, 3438, 9, 590, 1532, 9, 102, 266, 7, 1532, 3444, 0, 5, 94, 9, 94, 266, 7, 3448, 3450, 9, 590, 3448, 7, 102, 266, 9, 1532, 3456, 7, 94, 266, 9, 3448, 3460, 2991, 2992, 2994, 9, 972, 2990, 2991, 2992, 3466, 2, 5, 926, 6, 972, 3471, 0, 927, 3472, 8, 2843, 3000, 8, 625, 3008, 1, 6, 624, 2, 4, 3481, 8, 625, 3482, 3, 6, 2842, 0, 4, 3487, 8, 2843, 3488, 8, 591, 1978, 2, 267, 3258, 4, 150, 267, 10, 146, 660, 3, 4, 18, 5, 10, 3500, 3, 4, 664, 5, 10, 3504, 2, 4, 383, 267, 382, 3508, 1, 376, 3504, 2, 267, 3504, 3, 4, 382, 2, 267, 3516, 2, 58, 357, 7, 356, 3520, 60, 3520, 3522, 0, 8, 48, 2, 7, 3526, 60, 541, 3528, 5, 312, 432, 60, 1398, 3532, 3, 146, 232, 102, 232, 3537, 2, 3536, 3539, 1, 2, 302, 4, 6, 3542, 302, 500, 3543, 2, 3545, 3546, 3, 302, 500, 4, 6, 3551, 302, 500, 3552, 2, 3550, 3555, 6, 61, 356, 2, 58, 3559, 356, 3559, 3560, 3, 58, 356, 4, 6, 3565, 5, 356, 3566, 2, 3564, 3569, 58, 356, 3566, 2, 3564, 3573, 4, 8, 282, 2, 6, 3576, 3, 282, 926, 2, 3579, 3580, 0, 904, 927, 5, 38, 904, 6, 38, 559, 4, 58, 561, 4, 58, 927, 6, 38, 927, 0, 500, 927, 0, 555, 904, 9, 38, 58, 4, 9, 312, 7, 1624, 3602, 4, 8, 2450, 0, 396, 3607, 7, 10, 660, 5, 2448, 3610, 3, 10, 1030, 5, 2448, 3614, 9, 712, 3216, 9, 312, 1622, 7, 1624, 3620, 7, 8, 2448, 3, 10, 3624, 5, 2448, 3626, 3, 8, 2448, 7, 10, 3630, 5, 2448, 3632, 6, 313, 3216, 4, 7, 3636, 9, 3216, 3638, 7, 8, 3602, 5, 312, 3642, 313, 3602, 3644, 0, 634, 1031, 0, 634, 1030, 6, 3649, 3650, 0, 635, 1030, 636, 1030, 3655, 4, 8, 640, 0, 634, 3659, 6, 640, 3661, 6, 640, 3649, 1, 636, 3650, 635, 644, 3650, 636, 644, 1030, 6, 959, 3650, 4, 8, 959, 0, 634, 3674, 6, 959, 3676, 347, 348, 1928, 0, 322, 2305, 0, 322, 577, 0, 8, 346, 0, 348, 3687, 8, 1928, 3687, 5, 6, 310, 7, 1838, 3692, 60, 1382, 1835, 4, 6, 242, 282, 310, 3699, 302, 656, 3699, 60, 1835, 2580, 92, 282, 2467, 2, 4, 656, 8, 40, 3709, 0, 656, 3711, 8, 359, 3008, 0, 656, 3715, 8, 266, 358, 267, 282, 3718, 8, 267, 282, 282, 358, 3722, 8, 266, 359, 0, 656, 3727, 0, 6, 92, 2, 4, 3730, 92, 282, 3733, 10, 282, 291, 2, 4, 522, 282, 358, 3739, 0, 3, 656, 927, 3008, 3742, 0, 10, 657, 19, 656, 3746, 0, 10, 103, 19, 102, 3750, 0, 10, 95, 19, 94, 3754, 0, 10, 15, 14, 19, 3758, 6, 8, 3758, 14, 3758, 3763, 358, 1425, 3448, 2, 4, 95, 6, 8, 3768, 3448, 3768, 3771, 6, 8, 634, 634, 3448, 3775, 2, 4, 3449, 6, 8, 3778, 3448, 3778, 3781, 0, 5, 614, 4, 7, 614, 9, 3784, 3786, 6, 8, 2984, 102, 302, 3791, 4, 8, 3403, 282, 927, 3794, 6, 8, 3403, 302, 927, 3798, 218, 383, 904, 6, 218, 267, 9, 222, 3804, 102, 302, 383, 2, 4, 731, 6, 8, 3810, 38, 730, 3813, 0, 6, 376, 4, 8, 376, 1030, 3816, 3819, 0, 665, 1030, 5, 666, 3822, 5, 50, 354, 4, 7, 3826, 9, 3826, 3828, 4, 7, 354, 5, 2428, 3832, 7, 1522, 2428, 7, 904, 3826, 0, 2, 904, 5, 354, 3840, 7, 904, 3842, 4, 242, 1189, 0, 927, 3846, 1, 6, 376, 102, 1766, 3851, 3, 8, 904, 0, 7, 3854, 4, 6, 3854, 904, 3856, 3859, 4, 7, 746, 3, 8, 3862, 2, 8, 3862, 2, 3864, 3867, 3, 8, 640, 634, 3659, 3870, 3, 4, 1188, 2, 8, 3875, 2, 8, 3874, 3874, 3876, 3879, 2, 9, 3874, 8, 3879, 3882, 2, 8, 1189, 3, 4, 3886, 5, 1188, 3886, 9, 3888, 3890, 4, 8, 1189, 2, 5, 3894, 3, 1188, 3894, 9, 3896, 3898, 3, 8, 1188, 2, 5, 3902, 4, 1189, 3902, 9, 3904, 3906, 5, 8, 1188, 3, 4, 3910, 2, 1189, 3910, 9, 3912, 3914, 4, 9, 1876, 5, 8, 1876, 1877, 3918, 3920, 5, 8, 3918, 1877, 3918, 3924, 5, 378, 3818, 1, 6, 10, 8, 394, 3930, 1, 4, 746, 2, 5, 746, 8, 3934, 3936, 8, 432, 3264, 58, 92, 313, 0, 8, 10, 1, 394, 3944, 3, 366, 420, 11, 3930, 3944, 49, 420, 544, 2, 8, 544, 49, 544, 3954, 1, 6, 3106, 4, 9, 3958, 3106, 3130, 3960, 0, 8, 555, 4, 6, 555, 2, 3964, 3966, 2, 3965, 3966, 8, 3969, 3970, 0, 7, 3130, 5, 8, 3974, 500, 3131, 3976, 2, 3130, 3979, 4, 6, 3106, 1, 2, 3982, 9, 500, 3984, 3, 3106, 3986, 1, 2, 2218, 9, 500, 3990, 3, 3106, 3992, 1, 2, 904, 9, 500, 3996, 3, 3106, 3998, 1, 4, 3106, 9, 500, 3106, 6, 4002, 4004, 3, 3106, 4006, 4, 3958, 4004, 3, 3106, 4010, 1, 4, 4004, 6, 4004, 4014, 3, 3106, 4016, 3, 8, 3966, 3964, 3966, 4021, 2, 4020, 4023, 303, 926, 2984, 6, 8, 303, 0, 2, 4029, 303, 926, 4030, 1, 4, 2984, 5, 6, 2984, 8, 4034, 4036, 0, 2, 1031, 1, 6, 4040, 4, 7, 4040, 8, 4042, 4044, 2, 38, 103, 39, 576, 4048, 6, 8, 4048, 39, 4048, 4052, 4, 8, 59, 2, 6, 4057, 59, 1726, 4058, 4, 8, 4058, 59, 4058, 4062, 39, 576, 3810, 0, 3, 40, 8, 1950, 4069, 0, 3, 338, 8, 60, 4073, 8, 60, 4069, 0, 3, 926, 8, 60, 4079, 8, 60, 3071, 0, 6, 660, 8, 60, 4085, 0, 6, 48, 8, 60, 4089, 2, 5, 60, 0, 6, 4093, 8, 60, 4095, 60, 490, 905, 51, 146, 926, 41, 490, 926, 8, 60, 490, 6, 92, 313, 217, 502, 3106, 312, 1150, 1835, 312, 1731, 3130, 3, 8, 346, 312, 3687, 4114, 2, 9, 500, 217, 1914, 4118, 0, 8, 61, 217, 2038, 4122, 0, 8, 3931, 0, 9, 3930, 1, 4126, 4128, 0, 8, 433, 0, 9, 432, 1, 4132, 4134, 0, 8, 1147, 0, 9, 1146, 1, 4138, 4140, 0, 8, 521, 0, 9, 520, 1, 4144, 4146, 4, 6, 48, 1, 132, 4150, 3, 4, 50, 1, 132, 4154, 1, 4, 132, 60, 132, 4158, 0, 133, 1188, 4, 132, 4163, 4, 6, 491, 1, 132, 4166, 0, 2, 1030, 8, 10, 4171, 0, 4, 972, 8, 10, 4175, 1, 132, 3258, 132, 1189, 4158, 2, 7, 376, 4, 8, 4183, 1, 376, 4184, 0, 1731, 2622, 8, 38, 611, 6, 610, 4191, 8, 577, 734, 302, 656, 927, 9, 610, 730, 39, 610, 734, 6, 9, 610, 39, 610, 4202, 7, 730, 734, 4, 8, 243, 60, 1340, 4209, 0, 8, 3275, 500, 927, 4212, 927, 3584, 3854, 0, 3, 906, 5, 6, 4218, 7, 904, 4220, 0, 5, 1578, 4, 927, 4224, 0, 5, 3174, 4, 927, 4228, 0, 7, 2102, 6, 927, 4232, 1, 4, 1578, 0, 927, 4236, 1, 6, 2102, 0, 927, 4240, 1, 4, 3174, 0, 927, 4244, 0, 8, 3997, 5, 6, 4248, 7, 904, 4250, 2, 9, 554, 4, 146, 4255, 6, 358, 4255, 2, 60, 4255, 2, 8, 554, 60, 555, 4262, 60, 555, 2040, 8, 60, 1583, 282, 358, 3067, 3, 8, 282, 0, 4, 4273, 282, 358, + 4275, 2, 7, 3420, 267, 282, 4278, 2, 7, 3576, 267, 282, 4282, 1, 2, 3576, 41, 282, 4286, 4, 8, 40, 1, 2, 4290, 41, 282, 4292, 2, 9, 10, 4, 6, 4296, 10, 282, 4299, 218, 554, 629, 302, 501, 656, 282, 310, 501, 59, 218, 282, 39, 232, 302, 0, 9, 926, 1, 8, 4312, 927, 4312, 4314, 1, 6, 1382, 103, 1382, 4318, 1, 4, 2580, 1031, 2580, 4322, 0, 558, 577, 6, 8, 311, 0, 3692, 4329, 38, 102, 927, 0, 5, 1546, 9, 1030, 4334, 0, 9, 1546, 5, 1030, 4338, 0, 4, 103, 6, 9, 4342, 7, 102, 4344, 0, 7, 1534, 9, 102, 4348, 0, 6, 1031, 5, 8, 4352, 9, 1030, 4354, 4, 9, 4352, 5, 1030, 4358, 0, 9, 1534, 7, 102, 4362, 7, 8, 4342, 9, 102, 4366, 6, 8, 927, 1, 4, 4370, 0, 927, 4372, 2, 8, 1950, 1, 40, 4376, 0, 41, 4376, 4377, 4378, 4380, 0, 338, 2040, 0, 338, 2041, 2040, 4385, 4386, 1, 40, 2132, 0, 40, 2132, 0, 4390, 4393, 1, 40, 1424, 0, 40, 1424, 0, 4396, 4399, 0, 41, 2132, 40, 4393, 4402, 0, 41, 1424, 40, 4399, 4406, 1, 338, 2040, 338, 2040, 4411, 0, 4410, 4413, 2133, 4390, 4402, 1425, 4396, 4406, 0, 41, 4390, 2133, 4390, 4420, 1, 8, 282, 2, 4, 4425, 2, 4, 4424, 7, 282, 4428, 4424, 4426, 4431, 1, 628, 3008, 5, 1808, 4434, 6, 8, 629, 3, 4, 4438, 2, 4, 4439, 1, 628, 4442, 5, 4440, 4444, 3, 4, 4424, 6, 283, 4426, 5, 4448, 4450, 2, 5, 4424, 3, 4450, 4454, 4, 4431, 4454, 2, 5, 656, 7, 282, 3708, 4, 4460, 4463, 2, 4431, 4448, 2, 5, 4438, 3, 4444, 4468, 4439, 4440, 4468, 1, 628, 4472, 0, 3, 4460, 4, 7, 4460, 9, 4476, 4478, 1, 2, 3448, 4, 7, 4482, 9, 3448, 4484, 0, 5, 1454, 9, 1336, 4488, 0, 5, 1808, 2, 7, 1808, 9, 4492, 4494, 9, 1336, 3448, 2, 926, 2389, 2, 6, 266, 2, 926, 4503, 2, 926, 3141, 2, 926, 1117, 2, 926, 3841, 905, 926, 3996, 4, 6, 3997, 905, 3996, 4514, 1, 926, 1916, 2, 6, 267, 4, 7, 4520, 8, 4520, 4522, 4, 7, 3850, 8, 3850, 4526, 0, 93, 358, 0, 41, 634, 1, 4, 376, 8, 2546, 4534, 8, 10, 2546, 8, 10, 2516, 8, 10, 1854, 8, 10, 2552, 5, 6, 464, 8, 10, 4546, 8, 10, 406, 0, 6, 93, 8, 10, 4552, 0, 6, 445, 8, 10, 4556, 3, 6, 454, 8, 10, 4560, 0, 5, 2162, 8, 359, 4564, 7, 2162, 4566, 7, 312, 660, 5, 6, 4570, 9, 4570, 4572, 3, 38, 972, 9, 2990, 4576, 3, 58, 132, 9, 1372, 4580, 8, 38, 147, 9, 38, 146, 5, 4584, 4586, 8, 58, 359, 9, 58, 358, 7, 4590, 4592, 2, 4, 59, 8, 58, 4597, 9, 58, 4596, 7, 4598, 4600, 8, 38, 2421, 9, 38, 2420, 5, 4604, 4606, 48, 629, 2156, 376, 629, 1562, 376, 629, 1150, 232, 267, 1042, 267, 282, 1372, 2, 7, 660, 267, 282, 4620, 232, 267, 2038, 267, 282, 408, 267, 282, 1054, 232, 267, 2162, 1, 4, 216, 6, 8, 4632, 4, 8, 3850, 4, 8, 2420, 4, 8, 4520, 6, 8, 4596, 8, 926, 3274, 5, 6, 10, 4, 8, 4646, 6, 8, 884, 1, 216, 926, 8, 10, 926, 4, 8, 93, 0, 2, 4657, 2133, 4656, 4658, 8, 927, 2722, 3, 634, 4662, 4, 93, 4662, 0, 2, 2133, 8, 2133, 4668, 4, 93, 4670, 0, 4, 665, 8, 665, 4674, 5, 634, 4676, 0, 665, 1254, 5, 634, 4680, 0, 8, 635, 3, 4, 4684, 634, 927, 4686, 0, 927, 1254, 3, 634, 4690, 4, 665, 4684, 5, 634, 4694, 5, 972, 3148, 9, 660, 4698, 0, 9, 554, 0, 500, 4703, 1, 58, 2730, 58, 1030, 2581, 6, 38, 4703, 0, 5, 500, 500, 1030, 4712, 0, 7, 500, 102, 500, 4716, 1, 6, 38, 38, 102, 4720, 5, 6, 356, 1, 38, 4724, 8, 500, 559, 38, 303, 500, 6, 8, 103, 4, 102, 4733, 4, 102, 927, 6, 927, 1030, 9, 102, 1030, 8, 904, 927, 5, 904, 1030, 6, 1030, 4079, 0, 3, 4746, 926, 4746, 4748, 3, 2722, 3274, 6, 1030, 4753, 2723, 3402, 4738, 1, 2, 4738, 0, 3, 4758, 926, 4758, 4760, 3, 3402, 4758, 0, 3, 4738, 1, 3402, 4766, 6, 1030, 3275, 1, 3402, 4770, 3, 3402, 4746, 2, 346, 635, 0, 4, 2041, 60, 2040, 4779, 0, 2, 1997, 146, 1996, 4783, 5, 92, 346, 8, 146, 3934, 2, 5, 346, 8, 346, 4790, 49, 60, 420, 49, 60, 2040, 92, 313, 602, 5, 6, 92, 92, 313, 4800, 4, 8, 1346, 242, 904, 4805, 0, 1347, 4806, 0, 3, 2622, 1188, 1731, 4810, 0, 1189, 3130, 1188, 1731, 4814, 1, 242, 500, 6, 8, 500, 0, 243, 4820, 4818, 4821, 4822, 0, 4, 2039, 2, 6, 4826, 2038, 4122, 4829, 51, 904, 972, 5, 302, 4832, 8, 50, 500, 0, 4818, 4837, 5, 310, 746, 7, 2162, 4840, 1, 4, 1348, 9, 1348, 4844, 6, 242, 4847, 0, 4844, 4849, 0, 3, 904, 4, 6, 972, 0, 8, 4854, 972, 4852, 4857, 133, 146, 232, 282, 358, 635, 60, 232, 661, 41, 92, 282, 1, 2, 346, 312, 348, 4869, 0, 7, 1574, 1, 6, 4872, 1575, 4872, 4874, 0, 4, 973, 49, 974, 4878, 0, 5, 2102, 1, 4, 4882, 2103, 4882, 4884, 354, 1112, 1835, 0, 2, 1602, 0, 8, 1602, 354, 4890, 4893, 354, 4079, 4312, 0, 3, 1602, 0, 9, 1602, 354, 4899, 4900, 0, 9, 1996, 1, 8, 4904, 1997, 4904, 4906, 0, 147, 2156, 1, 146, 4910, 2157, 4910, 4912, 4, 242, 1031, 0, 5, 4916, 1030, 4916, 4918, 8, 927, 3274, 9, 926, 3274, 0, 4922, 4924, 0, 2, 559, 1, 576, 4928, 0, 2, 561, 1, 1726, 4932, 9, 576, 1830, 0, 927, 3208, 1, 8, 4938, 926, 4938, 4940, 1, 8, 2722, 0, 9, 4944, 926, 4944, 4946, 0, 9, 4922, 926, 4922, 4950, 2, 8, 4313, 0, 9, 4954, 926, 4954, 4956, 0, 358, 656, 8, 3008, 4961, 0, 358, 635, 8, 640, 4965, 6, 10, 635, 8, 644, 4969, 283, 634, 1090, 14, 283, 634, 6, 10, 232, 8, 290, 4977, 2, 8, 2646, 41, 282, 4980, 282, 635, 2648, 6, 8, 2278, 6, 8, 2279, 2278, 4987, 4988, 6, 313, 1030, 9, 1670, 4992, 604, 927, 1622, 2, 4, 354, 2, 7, 354, 904, 4999, 5000, 904, 1340, 1505, 9, 312, 346, 8, 313, 346, 347, 5006, 5008, 8, 313, 5006, 347, 5006, 5012, 4, 7, 3216, 9, 3636, 5016, 6, 9, 2278, 7, 8, 5020, 2279, 5020, 5022, 7, 312, 1622, 9, 1624, 5026, 58, 283, 1498, 2, 4, 103, 0, 7, 5032, 1, 6, 5032, 102, 5034, 5036, 1, 6, 5034, 102, 5034, 5040, 2, 6, 1031, 0, 5, 5044, 1, 4, 5044, 1030, 5046, 5048, 1, 4, 5046, 1030, 5046, 5052, 5, 6, 640, 1, 3658, 5056, 356, 555, 644, 555, 628, 1498, 1, 4, 2192, 58, 283, 5064, 0, 5, 4118, 283, 500, 5068, 0, 103, 972, 1, 6, 5072, 102, 5072, 5074, 7, 312, 356, 1, 4724, 5078, 0, 2, 657, 303, 500, 5082, 1, 102, 972, 6, 5072, 5086, 9, 356, 972, 1, 4724, 5090, 9, 356, 624, 1, 4724, 5094, 0, 6, 5086, 103, 5086, 5098, 9, 58, 1188, 1, 1726, 5102, 1, 4, 972, 4, 628, 972, 0, 5106, 5109, 0, 5, 972, 629, 4878, 5112, 4, 629, 972, 5, 4878, 5116, 4, 628, 4879, 972, 4878, 5121, 6, 8, 5107, 4, 972, 5124, 0, 5106, 5127, 5, 972, 4878, 629, 4878, 5130, 5, 628, 972, 629, 4878, 5134, 0, 4175, 5116, 6, 8, 4174, 4, 972, 5141, 0, 4175, 5142, 4, 58, 312, 2, 1166, 5147, 3, 216, 228, 1, 2, 5150, 229, 5150, 5152, 2, 3117, 5150, 8, 501, 1126, 1124, 1126, 5158, 1124, 1126, 3106, 8, 500, 1125, 8, 1126, 5165, 9, 500, 1124, 1124, 1126, 5169, 503, 1124, 1126, 0, 2, 735, 1, 1132, 5174, 0, 2, 2573, 1, 1166, 5178, 0, 3, 664, 0, 4, 664, 2470, 5182, 5185, 1, 2, 382, 0, 4, 383, 1975, 5188, 5190, 0, 3, 382, 0, 4, 382, 3508, 5194, 5197, 4, 376, 382, 4, 376, 383, 382, 5201, 5202, 2, 48, 383, 382, 1993, 5206, 1, 2, 628, 2467, 4674, 5210, 6, 8, 5210, 0, 4, 5215, 2467, 5210, 5216, 6, 8, 2466, 0, 4, 5221, 2467, 5210, 5222, 4, 58, 303, 6, 38, 303, 0, 303, 500, 8, 311, 500, 8, 58, 219, 6, 356, 555, 6, 303, 356, 1, 58, 356, 5, 356, 500, 4, 9, 588, 0, 7, 5244, 582, 588, 5246, 6, 132, 134, 1, 4, 5250, 132, 1484, 5252, 5, 628, 634, 135, 1498, 5256, 4, 135, 628, 5, 134, 628, 0, 2, 5262, 1, 5260, 5264, 7, 500, 580, 267, 1730, 5268, 2, 9, 1730, 7, 500, 5272, 267, 1730, 5274, 2, 9, 576, 1, 38, 5278, 267, 576, 5280, 0, 2, 310, 572, 576, 5285, 6, 9, 132, 2, 4, 5289, 0, 3, 5290, 1, 4, 5288, 132, 5292, 5294, 0, 6, 581, 4, 267, 5298, 9, 266, 5298, 580, 5300, 5303, 4, 7, 200, 1, 2, 5306, 8, 40, 5309, 6, 5306, 5311, 512, 656, 927, 8, 40, 2735, 4, 236, 5317, 8, 40, 1553, 0, 1546, 5321, 4, 243, 1188, 7, 8, 5324, 6, 8, 5324, 6, 5326, 5329, 0, 2, 905, 6, 660, 5332, 0, 2620, 5335, 8, 5324, 5327, 6, 5326, 5339, 6, 8, 5325, 5324, 5329, 5342, 4, 216, 746, 232, 904, 5347, 4, 8, 746, 228, 232, 5351, 0, 5, 3854, 0, 6, 3854, 904, 5354, 5357, 0, 5, 660, 904, 4085, 5360, 0, 4, 3854, 904, 3856, 5365, 5, 6, 660, 6, 634, 5361, 7, 5368, 5370, 7, 8, 92, 6, 9, 92, 1, 634, 5376, 93, 5374, 5378, 6, 9, 5374, 1, 634, 5382, 93, 5374, 5384, 8, 93, 644, 7, 5376, 5388, 201, 408, 3174, 9, 40, 520, 7, 3174, 5394, 3, 8, 634, 4, 7, 5398, 6, 5399, 5400, 1, 634, 5402, 5, 5400, 5404, 5, 8, 634, 2, 7, 5408, 6, 5409, 5410, 1, 634, 5412, 3, 5410, 5414, 9, 92, 644, 7, 1578, 5418, 0, 1352, 1915, 0, 2, 311, 1, 1730, 5424, 1, 1726, 5424, 2, 9, 2304, 1, 4, 5430, 5, 2304, 5432, 2, 9, 1726, 1, 4, 5436, 5, 1726, 5438, 1, 356, 5430, 5, 2304, 5442, 5, 356, 5430, 1, 2304, 5446, 2, 9, 356, 5, 628, 5450, 1, 356, 5452, 1, 356, 5256, 2, 4, 219, 5, 2304, 5458, 356, 500, 4151, 4, 8, 5082, 5, 500, 5464, 7, 8, 242, 0, 4, 5468, 5, 500, 5470, 7, 8, 490, 0, 4, 5474, 5, 500, 5476, 2, 8, 51, 0, 4, 5480, 5, 500, 5482, 4, 61, 1726, 1, 58, 5486, 4, 61, 1834, 1, 58, 5490, 4, 61, 64, 1, 58, 5494, 5, 8, 500, 1, 2, 5498, 7, 5498, 5500, 0, 500, 5502, 6, 40, 357, 356, 555, 5506, 8, 10, 41, 8, 10, 40, 40, 5510, 5513, 358, 3719, 3726, 9, 266, 358, 267, 3726, 5518, 8, 11, 40, 9, 10, 40, 41, 5522, 5524, 9, 10, 5522, 41, 5522, 5528, 8, 267, 358, 359, 5518, 5532, 9, 266, 5532, 359, 5532, 5536, 8, 38, 1189, 9, 38, 1188, 39, 5540, 5542, 9, 1188, 5540, 39, 5540, 5546, 0, 2467, 3708, 0, 665, 926, 0, 664, 926, 664, 5552, 5555, 0, 664, 927, 1, 5552, 5558, 0, 359, 634, 1, 4964, 5562, 0, 18, 383, 1, 358, 634, 359, 4964, 5568, 635, 5562, 5568, 1, 358, 5562, 635, 5562, 5574, 0, 9, 1348, 1526, 3699, 5578, 1, 8, 312, 4, 6, 5583, 3, 5582, 5584, 7, 312, 5586, 5, 5584, 5588, 5, 312, 5586, 7, 5584, 5592, 3, 242, 1342, 5, 312, 5596, 7, 1342, 5598, 5, 312, 1454, 243, 1336, 5602, 7, 312, 5596, 5, 1342, 5606, 7, 312, 1526, 243, 1348, 5610, 5, 6, 5582, 3, 4, 5582, 7, 312, 5616, 5583, 5614, 5618, 2, 6, 3078, 5, 6, 3078, 522, 5623, 5624, 0, 9, 3078, 5623, 5624, 5628, 2, 4, 19, 1, 14, 5632, 6, 8, 3709, 1, 3008, 5636, 1, 14, 3008, 2, 8, 2853, 6, 216, 2853, 4, 5642, 5644, 9, 60, 216, 4, 6, 5649, 216, 1113, 5650, 4, 7, 930, 102, 932, 5654, 4, 7, 48, 102, 932, 5658, 932, 1030, 1854, 4, 8, 1854, 1, 2, 5664, 1030, 1854, 5666, 4, 8, 2546, 1, 2, 5670, 1030, 2546, 5672, 8, 48, 1854, 1, 2, 5676, 1030, 1854, 5678, 0, 3, 482, 4, 7, 5682, 1, 376, 5684, 8, 482, 5686, 3, 216, 3274, 4, 6, 3274, 927, 5690, 5692, 376, 629, 3070, 376, 629, 4078, 2, 267, 926, 0, 383, 5700, 5, 10, 94, 0, 15, 5704, 2, 629, 926, 0, 2467, 5708, 4, 95, 628, 0, 2470, 5713, 8, 629, 1258, 282, 1259, 5716, 1, 6, + 2740, 9, 2740, 5720, 9, 232, 5720, 1, 6, 3536, 9, 232, 5726, 8, 282, 1259, 0, 7, 5730, 1, 282, 5732, 0, 40, 232, 8, 233, 5736, 6, 232, 5739, 0, 232, 634, 8, 233, 5742, 6, 232, 5745, 0, 232, 290, 8, 233, 5748, 6, 232, 5751, 5, 60, 232, 1, 6, 5754, 9, 232, 5756, 1, 232, 282, 2, 5, 282, 3, 5760, 5762, 2, 8, 395, 4, 9, 394, 11, 5766, 5768, 3, 8, 746, 266, 5351, 5772, 3, 8, 58, 266, 1727, 5776, 3, 8, 2516, 4, 8, 2516, 266, 5780, 5783, 2, 8, 746, 266, 2558, 5787, 266, 1479, 2168, 5, 8, 2516, 266, 2519, 5792, 2, 8, 747, 11, 1298, 5796, 11, 1800, 2572, 2, 9, 746, 4, 8, 747, 11, 5802, 5804, 628, 3071, 3088, 102, 3000, 3088, 4, 94, 628, 628, 3088, 5813, 0, 94, 102, 4, 3071, 5816, 6, 8, 3448, 4, 3071, 5820, 1, 3000, 5820, 5, 94, 628, 1, 3000, 5826, 1, 6, 640, 8, 640, 5830, 8, 640, 644, 0, 7, 644, 8, 644, 5836, 628, 634, 959, 1, 628, 640, 283, 628, 634, 283, 634, 636, 1, 6, 590, 1, 40, 160, 1, 266, 614, 1, 6, 178, 1, 8, 482, 1, 8, 474, 4, 8, 61, 1, 6, 5860, 1, 6, 154, 1, 8, 492, 1, 266, 656, 1, 2, 94, 4, 664, 5871, 1, 3448, 5872, 0, 4, 4733, 3, 102, 5876, 1, 2, 5878, 4732, 5878, 5880, 2, 94, 3288, 664, 3088, 5885, 0, 2, 4733, 2, 103, 5876, 102, 5889, 5890, 4, 927, 2984, 102, 2723, 5894, 4, 628, 3742, 628, 1808, 5899, 0, 3, 3708, 4, 656, 3709, 628, 5903, 5904, 6, 8, 657, 2, 658, 5908, 659, 1808, 5910, 664, 3088, 3275, 664, 3088, 3709, 926, 2863, 3470, 1, 8, 5332, 9, 926, 5920, 7, 926, 3274, 5, 8, 5924, 9, 926, 5926, 5, 926, 3274, 7, 8, 5930, 9, 926, 5932, 2, 905, 926, 1, 8, 5936, 9, 926, 5938, 5, 6, 3208, 0, 7, 5942, 1, 926, 5944, 2, 7, 926, 4, 9, 5948, 0, 5, 5950, 1, 926, 5952, 6, 9, 3470, 0, 7, 5956, 1, 926, 5958, 9, 926, 3470, 0, 7, 5962, 1, 926, 5964, 9, 926, 5948, 0, 5, 5968, 1, 926, 5970, 0, 10, 267, 6, 8, 5975, 10, 441, 5976, 7, 10, 828, 8, 826, 5980, 9, 828, 5982, 7, 10, 836, 8, 834, 5986, 9, 836, 5988, 6, 191, 358, 1, 8, 5992, 787, 5992, 5994, 4, 146, 191, 1, 8, 5998, 807, 5998, 6000, 0, 201, 806, 1, 8, 6004, 807, 6004, 6006, 0, 201, 786, 1, 8, 6010, 787, 6010, 6012, 0, 8, 2083, 9, 1112, 2082, 1, 6016, 6018, 0, 8, 787, 788, 2162, 6022, 3, 10, 376, 6, 8, 6027, 2, 48, 267, 6, 8, 6031, 4, 267, 376, 6, 8, 6035, 1, 630, 5332, 4, 8, 5332, 3, 6, 6040, 1, 5332, 6042, 630, 905, 3996, 8, 358, 376, 3, 490, 6048, 3, 6, 1916, 8, 500, 6052, 1, 1916, 6054, 3, 6, 1730, 2, 501, 1730, 1, 6058, 6060, 6, 624, 3841, 2, 356, 905, 6, 3841, 6066, 51, 3818, 3850, 51, 3944, 4646, 2, 4, 1173, 6, 8, 6074, 0, 1172, 6077, 2, 4, 614, 0, 7, 6080, 9, 614, 6082, 6, 383, 2842, 0, 383, 1172, 8, 266, 2843, 6, 2842, 6091, 92, 383, 4552, 9, 614, 2842, 267, 584, 2842, 6, 9, 2842, 267, 2842, 6100, 2, 310, 927, 9, 354, 6104, 2, 7, 1540, 9, 5360, 6108, 9, 1544, 1638, 1, 4, 1544, 2, 5, 6114, 9, 1544, 6116, 2, 7, 4158, 9, 1158, 6120, 0, 7, 1514, 9, 1638, 6124, 2, 8, 49, 0, 3, 6128, 7, 48, 6130, 9, 6128, 6132, 2, 357, 1030, 3, 312, 6136, 0, 93, 1030, 1, 312, 6140, 0, 217, 6136, 1, 6, 2842, 2, 4, 6147, 8, 2843, 6148, 0, 7, 3708, 8, 3008, 6153, 8, 2843, 6074, 0, 7, 1172, 8, 6074, 6159, 8, 2843, 3008, 5, 6, 1644, 4, 383, 6164, 1, 2, 582, 0, 383, 6168, 0, 383, 432, 2, 4, 394, 6, 8, 6174, 10, 394, 6177, 376, 378, 389, 0, 2, 583, 383, 582, 6182, 60, 383, 1876, 0, 2, 2333, 6, 8, 6188, 4, 2332, 6191, 4, 1964, 1969, 383, 500, 1644, 0, 3, 92, 4, 6, 6199, 8, 92, 6201, 8, 93, 6200, 9, 6202, 6204, 2, 491, 1312, 3, 1568, 6208, 6, 133, 354, 0, 132, 6213, 3, 1568, 6214, 0, 132, 747, 3, 1568, 6218, 4, 8, 354, 0, 7, 6222, 3, 4, 6224, 5, 6, 6222, 355, 6226, 6228, 0, 7, 660, 3, 4, 6232, 355, 5368, 6234, 3, 4, 1160, 355, 1574, 6238, 1, 92, 628, 0, 3, 6242, 4, 6, 6245, 8, 6243, 6246, 2, 8, 3935, 3, 1568, 6250, 3, 4, 490, 2, 8, 6255, 3, 1568, 6256, 8, 282, 358, 2, 4, 6261, 8, 6261, 6262, 6, 9, 432, 61, 660, 6266, 4, 146, 232, 4, 132, 6271, 2, 8, 2743, 3, 290, 6274, 2, 6, 232, 4, 8, 6279, 5, 290, 6280, 233, 660, 6274, 233, 2356, 6274, 1, 6, 2038, 61, 660, 6288, 61, 644, 660, 61, 644, 5398, 41, 282, 358, 7, 1886, 5762, 7, 60, 5762, 2, 5, 1886, 7, 1886, 6302, 5, 358, 1886, 3, 358, 5762, 282, 358, 1259, 2, 41, 1886, 4, 41, 5762, 8, 50, 358, 8, 40, 6317, 2, 51, 132, 3, 40, 6320, 4, 51, 660, 5, 40, 6324, 4, 283, 660, 5, 40, 6328, 2, 102, 283, 3, 40, 6332, 4, 94, 283, 5, 40, 6336, 2, 5368, 6324, 2, 5368, 6328, 3, 4, 2954, 2, 5, 2954, 6, 6344, 6346, 2, 5, 6344, 6, 6344, 6350, 2, 7, 218, 233, 660, 6354, 3, 6, 218, 283, 634, 6358, 0, 383, 5852, 0, 267, 5848, 9, 590, 6364, 0, 7, 5852, 9, 614, 6368, 8, 266, 383, 0, 7, 6372, 6, 383, 6374, 0, 6, 6373, 383, 6372, 6378, 1, 8, 584, 0, 267, 6382, 7, 584, 6384, 0, 9, 5852, 7, 614, 6388, 0, 6, 591, 383, 590, 6392, 0, 7, 6382, 267, 584, 6396, 0, 9, 5848, 267, 590, 6400, 0, 5, 664, 4, 217, 6404, 358, 6404, 6406, 3, 312, 500, 2, 7, 6410, 218, 6410, 6412, 9, 354, 500, 2, 7, 6416, 218, 6416, 6418, 5, 302, 664, 4, 7, 6422, 217, 6422, 6424, 7, 218, 228, 2, 9, 6428, 228, 6428, 6430, 1, 38, 664, 0, 9, 6434, 41, 6434, 6436, 7, 218, 1124, 2, 5168, 6440, 3, 4, 218, 7, 218, 6444, 9, 500, 6444, 2, 6446, 6448, 7, 60, 218, 2, 9, 6452, 500, 6452, 6454, 2, 9, 6446, 500, 6446, 6458, 146, 629, 6198, 146, 629, 3526, 1, 2, 356, 41, 282, 6466, 5, 8, 216, 146, 232, 6471, 146, 232, 3383, 2, 8, 11, 146, 232, 6477, 0, 8, 49, 146, 232, 6481, 2, 4, 615, 7, 266, 614, 8, 6484, 6487, 6, 8, 6484, 267, 6484, 6490, 267, 382, 3008, 267, 3008, 3012, 6, 5518, 5532, 6, 8, 5518, 267, 5518, 6500, 6, 585, 5518, 267, 382, 5518, 8, 591, 6484, 2, 6, 219, 3, 8, 6510, 501, 6510, 6512, 3, 3106, 6510, 2, 218, 3107, 6, 3106, 6519, 8, 505, 6510, 501, 3130, 6510, 3, 6, 3106, 219, 3106, 6526, 219, 3106, 6358, 0, 5, 504, 2, 6, 6533, 8, 505, 6534, 6, 221, 3106, 1, 4, 220, 2, 8, 6541, 6, 221, 6542, 0, 9, 3698, 3, 242, 6546, 3699, 6546, 6548, 9, 660, 1188, 310, 5368, 6552, 9, 356, 656, 40, 660, 6557, 4, 8, 2853, 359, 5518, 6560, 9, 266, 6560, 359, 6560, 6564, 6, 635, 3288, 8, 665, 3288, 6, 6569, 6570, 1, 6, 302, 660, 6354, 6574, 6, 266, 310, 93, 706, 6578, 310, 640, 3174, 310, 640, 1578, 6, 555, 634, 147, 2924, 6586, 9, 2102, 2156, 0, 41, 6590, 41, 134, 6140, 0, 5, 26, 0, 8, 11, 3, 26, 6598, 7, 6596, 6600, 0, 3, 26, 5, 26, 6598, 7, 6604, 6606, 0, 3, 2162, 5, 1090, 2162, 7, 6610, 6612, 3, 1090, 2162, 7, 4564, 6616, 19, 132, 6604, 8, 10, 19, 0, 3, 6622, 19, 146, 6624, 2, 4, 94, 10, 94, 665, 0, 6629, 6630, 10, 656, 665, 0, 3709, 6634, 0, 2, 95, 94, 3005, 6638, 7, 8, 2984, 8, 2984, 6643, 6, 6642, 6645, 2, 9, 3742, 5, 656, 3742, 7, 6648, 6650, 0, 9, 5870, 5, 94, 5870, 7, 6654, 6656, 0, 2, 615, 383, 614, 6660, 267, 590, 5802, 7, 8, 5802, 267, 5802, 6666, 6, 9, 2984, 2985, 6642, 6670, 7, 8, 6670, 2985, 6670, 6674, 7, 8, 1188, 267, 584, 6678, 41, 3930, 3944, 0, 3, 520, 5, 358, 6684, 8, 520, 6686, 41, 196, 432, 0, 3, 1146, 7, 146, 6692, 8, 1146, 6694, 0, 7, 1146, 3, 146, 6698, 8, 1146, 6700, 0, 7, 432, 5, 60, 6704, 8, 432, 6706, 0, 5, 432, 7, 60, 6710, 8, 432, 6712, 0, 5, 520, 3, 358, 6716, 8, 520, 6718, 0, 203, 432, 60, 203, 1876, 0, 3, 358, 656, 3167, 6726, 26, 157, 3944, 0, 10, 283, 2, 6, 661, 3, 6732, 6734, 4, 282, 2869, 0, 2870, 6739, 4, 9, 94, 1, 2, 6742, 4, 282, 6743, 0, 6744, 6747, 1, 2, 2898, 4, 282, 2899, 0, 6750, 6753, 0, 3, 2898, 2, 6753, 6756, 0, 3, 6742, 2, 6747, 6760, 0, 2, 12, 5, 6, 6764, 12, 133, 6766, 7, 8, 2870, 5, 2868, 6770, 0, 2870, 6772, 7, 8, 6744, 5, 6742, 6776, 0, 6744, 6778, 8, 926, 2722, 926, 4662, 6783, 9, 926, 2722, 8, 6783, 6786, 2, 9, 4078, 2, 8, 4078, 8, 6790, 6793, 2, 8, 4079, 3, 6790, 6796, 3, 8, 6790, 4079, 6790, 6800, 4078, 6793, 6796, 0, 9, 3274, 1, 8, 6806, 3275, 6806, 6808, 2723, 4662, 6786, 9, 926, 4662, 2723, 4662, 6814, 2, 218, 283, 6, 218, 661, 6, 6818, 6821, 0, 2, 4801, 1, 6, 6824, 92, 4800, 6825, 8, 6826, 6829, 0, 5, 5480, 3, 6, 6832, 634, 5480, 6834, 2, 8, 283, 0, 5, 6838, 3, 6, 6840, 634, 6838, 6842, 0, 5, 5474, 3, 6, 6846, 634, 5474, 6848, 0, 5, 2954, 3, 6, 6852, 634, 2954, 6854, 4, 9, 358, 0, 2, 6858, 8, 675, 6858, 359, 6860, 6862, 6, 8, 6829, 1, 6824, 6866, 5, 8, 282, 1, 6, 6870, 0, 3, 6872, 2, 6, 6871, 283, 6874, 6876, 0, 7, 6510, 6, 218, 660, 1, 6880, 6882, 242, 302, 2041, 2, 8, 4675, 2, 9, 4674, 665, 6888, 6890, 0, 8, 2471, 0, 9, 2470, 629, 6894, 6896, 9, 664, 4674, 665, 6888, 6900, 9, 628, 2470, 629, 6894, 6904, 6, 8, 1188, 132, 302, 6909, 132, 302, 2629, 132, 302, 5787, 8, 1770, 3955, 6, 48, 94, 94, 1770, 6919, 2, 232, 500, 8, 290, 6923, 4, 93, 1188, 8, 2843, 6926, 2, 500, 635, 8, 644, 6931, 233, 660, 3910, 233, 660, 3106, 4, 233, 1188, 8, 235, 6938, 4, 6, 219, 660, 6510, 6943, 223, 660, 6354, 132, 226, 1677, 4, 6, 218, 132, 221, 6950, 48, 146, 629, 10, 41, 282, 60, 376, 629, 267, 282, 358, 6, 242, 635, 9, 232, 6962, 9, 146, 1188, 232, 1189, 6966, 282, 291, 2734, 8, 41, 746, 282, 747, 6972, 2, 7, 656, 8, 40, 6977, 0, 656, 6979, 8, 146, 1189, 7, 282, 6982, 6, 216, 661, 217, 232, 6986, 9, 232, 6982, 8, 1189, 5762, 7, 282, 6992, 0, 7, 6982, 9, 6982, 6996, 1, 6, 6926, 8, 6926, 7000, 0, 3, 634, 7, 634, 7004, 6, 283, 7006, 1, 628, 7006, 6, 283, 634, 283, 7004, 7012, 2, 7, 282, 0, 6, 7017, 4, 9, 7016, 283, 7018, 7020, 0, 51, 634, 1, 628, 7024, 1, 628, 6926, 8, 644, 7024, 2, 7, 628, 233, 660, 7032, 0, 3, 7012, 283, 7012, 7036, 4, 41, 232, 1, 2, 7040, 8, 40, 7043, 6, 7040, 7045, 7, 266, 310, 6, 266, 311, 0, 9, 7050, 267, 7048, 7052, 7, 242, 634, 242, 282, 635, 243, 7056, 7058, 7, 60, 6870, 282, 747, 7062, 7, 1886, 6870, 282, 747, 7066, 7, 1886, 2558, 282, 747, 7070, 7, 60, 2558, 282, 747, 7074, 267, 2652, 2654, 1, 6, 7078, 7, 2654, 7080, 5, 266, 282, 6, 267, 310, 7, 7084, 7086, 1, 8, 48, 0, 6, 7091, 0, 475, 7090, 1, 7092, 7094, 6, 8, 242, 6, 242, 634, 8, 7099, 7100, 4, 1188, 3887, 6, 2954, 7104, 6, 8, 146, 0, 2, 7109, 0, 9, 7108, 6, 7110, 7113, 0, 9, 4800, 6, 6824, 7117, 3, 634, 1188, 6, 2954, 7120, 6, 634, 7005, 8, 7006, 7124, 6, 9, 7004, 6, 7006, 7129, 6, 2954, 6926, 6, 5480, 7024, 9, 1188, 3874, 6, 2954, 7136, 6, 242, 266, 242, 282, 7141, 9, 656, 2760, 7, 376, 3742, 9, 656, 7146, 1, 94, 146, 0, 7, 7150, 9, 7150, 7152, 0, 41, 972, 9, 656, 7156, 0, 41, 6976, 9, 656, 7160, 0, 94, 147, 146, 629, 7164, 6, 242, 267, + 0, 7, 7168, 9, 7168, 7170, 0, 95, 146, 94, 629, 7174, 6, 8, 7151, 0, 7150, 7179, 0, 41, 656, 7, 40, 656, 9, 7182, 7184, 0, 8, 475, 9, 5858, 7188, 0, 9, 424, 0, 8, 425, 1, 7192, 7194, 1, 40, 232, 9, 298, 7198, 1, 6, 200, 7, 40, 200, 9, 7202, 7204, 268, 614, 629, 0, 9, 2466, 6, 8, 2467, 629, 7210, 7212, 0, 9, 492, 493, 5866, 7216, 1, 8, 7216, 493, 7216, 7220, 0, 8, 267, 584, 629, 7224, 6, 675, 3096, 8, 406, 1091, 1, 406, 3096, 8, 1172, 4552, 8, 406, 520, 6, 523, 3096, 358, 406, 523, 92, 253, 4552, 1, 2546, 3818, 359, 520, 3096, 0, 656, 5909, 0, 629, 656, 8, 282, 629, 6, 232, 629, 9, 232, 656, 0, 2, 4732, 4, 102, 7258, 4732, 5888, 7261, 2, 926, 3275, 6, 1030, 7264, 0, 3274, 7267, 6, 1030, 3402, 0, 3274, 7271, 4, 102, 3402, 0, 3274, 7275, 8, 40, 3402, 0, 3274, 7279, 8, 554, 3402, 0, 3274, 7283, 2, 4078, 7283, 2, 4078, 7279, 2, 4078, 7271, 2, 4078, 7275, 6, 721, 1484, 38, 664, 1189, 6, 572, 608, 50, 135, 500, 491, 500, 664, 40, 94, 1603, 61, 94, 1568, 4, 6, 1835, 61, 94, 7308, 0, 8, 146, 2, 6, 7313, 102, 147, 7314, 359, 972, 1568, 2, 6, 200, 40, 102, 7321, 2, 5, 1602, 2, 8, 1603, 6, 7325, 7326, 4, 6, 200, 40, 94, 7331, 3, 4, 972, 4, 354, 973, 6, 7334, 7337, 8, 92, 644, 92, 5388, 7341, 2, 132, 645, 2, 133, 644, 3, 7344, 7346, 5, 644, 660, 4, 645, 660, 661, 7350, 7352, 3, 132, 644, 133, 7344, 7356, 2, 645, 7356, 133, 7356, 7360, 4, 645, 7350, 661, 7350, 7364, 8, 92, 645, 9, 5388, 7368, 4, 644, 661, 5, 7352, 7372, 2, 9, 50, 5, 6, 7376, 8, 3309, 7378, 51, 2454, 7378, 4, 8, 7378, 51, 7378, 7384, 4, 7, 7376, 6, 2429, 7388, 4, 2429, 7378, 2, 9, 2454, 5, 6, 7394, 51, 2454, 7396, 4, 6, 7377, 2429, 7376, 7400, 6, 132, 660, 5, 40, 660, 3, 4, 132, 6, 132, 7408, 8, 40, 359, 8, 40, 93, 2, 359, 660, 6, 61, 660, 8, 359, 634, 5, 8, 290, 290, 2356, 7422, 8, 232, 290, 3, 4, 7426, 5, 290, 7428, 5, 232, 290, 2, 8, 7432, 3, 290, 7434, 2, 5, 7426, 3, 290, 7438, 290, 2360, 7422, 4, 8, 2360, 5, 290, 7444, 4, 232, 660, 2, 5, 7448, 233, 7448, 7450, 2, 132, 232, 3, 4, 7454, 233, 7454, 7456, 4, 232, 661, 2, 7448, 7461, 2, 133, 232, 4, 7454, 7465 } }; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/null.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/null.hpp new file mode 100644 index 00000000000..8a71dd0e315 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/null.hpp @@ -0,0 +1,57 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file null.hpp + \brief No resynthesis (as default synthesis engine) + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../../traits.hpp" +#include + +namespace mockturtle +{ + +template +class null_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + (void)ntk; + (void)function; + (void)begin; + (void)end; + (void)fn; + } +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/shannon.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/shannon.hpp new file mode 100644 index 00000000000..d4ed62e44eb --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/shannon.hpp @@ -0,0 +1,100 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file shannon.hpp + \brief Use Shannon decomposition for resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../../traits.hpp" +#include "../decomposition.hpp" +#include "null.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on Shannon decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on Shanon decomposition. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + shannon_resynthesis resyn; + auto xag = node_resynthesis( klut, resyn ); + \endverbatim + * + */ +template> +class shannon_resynthesis +{ +public: + shannon_resynthesis( std::optional const& threshold = {}, ResynFn* resyn = nullptr ) + : threshold_( threshold ), + resyn_( resyn ) {} + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( threshold_ ) + { + std::vector vars( function.num_vars() - std::min( *threshold_, function.num_vars() ) ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = shannon_decomposition( ntk, function, vars, std::vector>( begin, end ), *resyn_ ); + fn( f ); + } + else + { + std::vector vars( function.num_vars() ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = shannon_decomposition( ntk, function, vars, std::vector>( begin, end ) ); + fn( f ); + } + } + +private: + std::optional threshold_; + ResynFn* resyn_; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/sop_factoring.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/sop_factoring.hpp new file mode 100644 index 00000000000..22adf0d9966 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/sop_factoring.hpp @@ -0,0 +1,453 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sop_factoring.hpp + \brief Resynthesis with SOP factoring + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../../utils/sop_utils.hpp" +#include "../../utils/stopwatch.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for sop_factoring function. */ +struct sop_factoring_params +{ + /*! \brief Select divisors with a quick algorithm. */ + bool use_quick_factoring{ true }; + + /*! \brief Factoring is also tried for the negated TT. */ + bool try_both_polarities{ true }; + + /*! \brief Factoring considers input and output inverters as additional cost. */ + bool consider_inverter_cost{ false }; +}; + +/*! \brief Resynthesis function based on SOP factoring. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The method converts a + * given truth table in an ISOP, then factors the ISOP, and + * returns the factored form. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig = ...; + + sop_factoring resyn; + refactoring( aig, resyn ); + \endverbatim + * + */ +template +class sop_factoring +{ +public: + using signal = typename Ntk::signal; + using sop_t = std::vector; + +public: + explicit sop_factoring( sop_factoring_params const& ps = {} ) + : _ps( ps ) {} + +public: + template + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 31 ); + + if ( kitty::is_const0( function ) ) + { + /* constant 0 */ + fn( dest.get_constant( false ) ); + return; + } + else if ( kitty::is_const0( ~function ) ) + { + /* constant 1 */ + fn( dest.get_constant( true ) ); + return; + } + + /* derive ISOP */ + bool negated; + auto cubes = get_isop( function, negated ); + + /* create literal form of SOP */ + sop_t sop = cubes_to_sop( cubes, function.num_vars() ); + + /* derive the factored form */ + signal f = gen_factor_rec( dest, { begin, end }, sop, 2 * function.num_vars() ); + + fn( negated ? !f : f ); + } + + template + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dc, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 31 ); + + if ( kitty::is_const0( function & ( ~dc ) ) ) + { + /* constant 0 */ + fn( dest.get_constant( false ) ); + return; + } + else if ( kitty::is_const0( ~( function | dc ) ) ) + { + /* constant 1 */ + fn( dest.get_constant( true ) ); + return; + } + + /* derive ISOP */ + bool negated; + auto cubes = get_isop_dc( function, dc, negated ); + + /* create literal form of SOP */ + sop_t sop = cubes_to_sop( cubes, function.num_vars() ); + + /* derive the factored form */ + signal f = gen_factor_rec( dest, { begin, end }, sop, 2 * function.num_vars() ); + + fn( negated ? !f : f ); + } + +private: + std::vector get_isop( kitty::dynamic_truth_table const& function, bool& negated ) const + { + std::vector cubes = kitty::isop( function ); + + if ( _ps.try_both_polarities ) + { + std::vector n_cubes = kitty::isop( ~function ); + + if ( _ps.consider_inverter_cost ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + kitty::cube n_term; + kitty::cube term; + for ( auto const& c : n_cubes ) + { + n_term._mask |= c._mask & ( ~c )._bits; + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + term._mask |= c._mask & ( ~c )._bits; + lit += c.num_literals(); + } + + /* positive cost: cubes + input negations + output negation */ + uint32_t positive_cost = cubes.size() + term.num_literals() + 1; + /* negative cost: cubes + input negations */ + uint32_t negative_cost = n_cubes.size() + n_term.num_literals(); + + if ( negative_cost < positive_cost ) + { + negated = true; + return n_cubes; + } + } + else + { + if ( n_cubes.size() < cubes.size() ) + { + negated = true; + return n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + negated = true; + return n_cubes; + } + } + } + } + + negated = false; + return cubes; + } + + std::vector get_isop_dc( kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dc, bool& negated ) const + { + std::vector cubes; + kitty::detail::isop_rec( function & ~dc, function | dc, function.num_vars(), cubes ); + + if ( _ps.try_both_polarities ) + { + std::vector n_cubes; + kitty::detail::isop_rec( ~function & ~dc, ~function | dc, function.num_vars(), n_cubes ); + + if ( _ps.consider_inverter_cost ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + kitty::cube n_term; + kitty::cube term; + for ( auto const& c : n_cubes ) + { + n_term._mask |= c._mask & ( ~c )._bits; + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + term._mask |= c._mask & ( ~c )._bits; + lit += c.num_literals(); + } + + /* positive cost: cubes + input negations + output negation */ + uint32_t positive_cost = cubes.size() + term.num_literals() + 1; + /* negative cost: cubes + input negations */ + uint32_t negative_cost = n_cubes.size() + n_term.num_literals(); + + if ( negative_cost < positive_cost ) + { + negated = true; + return n_cubes; + } + } + else + { + if ( n_cubes.size() < cubes.size() ) + { + negated = true; + return n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + negated = true; + return n_cubes; + } + } + } + } + + negated = false; + return cubes; + } + +#pragma region SOP factoring + signal gen_factor_rec( Ntk& ntk, std::vector const& children, sop_t& sop, uint32_t const num_lit ) const + { + sop_t divisor, quotient, reminder; + + assert( sop.size() ); + + /* compute the divisor */ + bool success = _ps.use_quick_factoring ? sop_quick_divisor( sop, divisor, num_lit ) : sop_good_divisor( sop, divisor, num_lit ); + if ( !success ) + { + /* generate trivial sop circuit */ + return gen_andor_circuit_rec( ntk, children, sop.begin(), sop.end(), num_lit ); + } + + /* divide the SOP by the divisor */ + sop_divide( sop, divisor, quotient, reminder ); + + assert( quotient.size() > 0 ); + + if ( quotient.size() == 1 ) + { + return lit_factor_rec( ntk, children, sop, quotient[0], num_lit ); + } + sop_make_cube_free( quotient ); + + /* divide the SOP by the quotient */ + sop_divide( sop, quotient, divisor, reminder ); + + if ( sop_is_cube_free( divisor ) ) + { + signal div_s = gen_factor_rec( ntk, children, divisor, num_lit ); + signal quot_s = gen_factor_rec( ntk, children, quotient, num_lit ); + + /* build (D)*(Q) + R */ + signal dq_and = ntk.create_and( div_s, quot_s ); + + if ( reminder.size() ) + { + signal rem_s = gen_factor_rec( ntk, children, reminder, num_lit ); + return ntk.create_or( dq_and, rem_s ); + } + + return dq_and; + } + + /* get the common cube */ + uint64_t cube = UINT64_MAX; + for ( auto const& c : divisor ) + { + cube &= c; + } + + return lit_factor_rec( ntk, children, sop, cube, num_lit ); + } + + signal lit_factor_rec( Ntk& ntk, std::vector const& children, sop_t const& sop, uint64_t const c_sop, uint32_t const num_lit ) const + { + sop_t divisor, quotient, reminder; + + /* extract the best literal */ + detail::sop_best_literal( sop, divisor, c_sop, num_lit ); + + /* divide SOP by the literal */ + sop_divide_by_cube( sop, divisor, quotient, reminder ); + + /* create the divisor: cube */ + signal div_s = gen_and_circuit_rec( ntk, children, divisor[0], 0, num_lit ); + + /* factor the quotient */ + signal quot_s = gen_factor_rec( ntk, children, quotient, num_lit ); + + /* build l*Q + R */ + signal dq_and = ntk.create_and( div_s, quot_s ); + + /* factor the reminder */ + if ( reminder.size() != 0 ) + { + signal rem_s = gen_factor_rec( ntk, children, reminder, num_lit ); + return ntk.create_or( dq_and, rem_s ); + } + + return dq_and; + } +#pragma endregion + +#pragma region Circuit generation from SOP + signal gen_and_circuit_rec( Ntk& ntk, std::vector const& children, uint64_t const cube, uint32_t const begin, uint32_t const end ) const + { + /* count set literals */ + uint32_t num_lit = 0; + uint32_t lit = begin; + uint32_t i; + for ( i = begin; i < end; ++i ) + { + if ( detail::cube_has_lit( cube, i ) ) + { + ++num_lit; + lit = i; + } + } + + assert( num_lit > 0 ); + + if ( num_lit == 1 ) + { + /* return the coprresponding signal with the correct polarity */ + if ( lit % 2 == 1 ) + return children[lit / 2]; + else + return !children[lit / 2]; + } + + /* find splitting point */ + uint32_t count_lit = 0; + for ( i = begin; i < end; ++i ) + { + if ( detail::cube_has_lit( cube, i ) ) + { + if ( count_lit >= num_lit / 2 ) + break; + + ++count_lit; + } + } + + signal tree1 = gen_and_circuit_rec( ntk, children, cube, begin, i ); + signal tree2 = gen_and_circuit_rec( ntk, children, cube, i, end ); + + return ntk.create_and( tree1, tree2 ); + } + + signal gen_andor_circuit_rec( Ntk& ntk, std::vector const& children, sop_t::const_iterator const& begin, sop_t::const_iterator const& end, uint32_t const num_lit ) const + { + auto num_prod = std::distance( begin, end ); + + assert( num_prod > 0 ); + + if ( num_prod == 1 ) + return gen_and_circuit_rec( ntk, children, *begin, 0, num_lit ); + + /* create or tree */ + signal tree1 = gen_andor_circuit_rec( ntk, children, begin, begin + num_prod / 2, num_lit ); + signal tree2 = gen_andor_circuit_rec( ntk, children, begin + num_prod / 2, end, num_lit ); + + return ntk.create_or( tree1, tree2 ); + } +#pragma endregion + +private: + sop_factoring_params const& _ps; + + mutable stopwatch<>::duration time_factoring{}; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/traits.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/traits.hpp new file mode 100644 index 00000000000..418786c4d2f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/traits.hpp @@ -0,0 +1,92 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file traits.hpp + \brief Traits for additional node_resynthesis methods + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../../traits.hpp" + +#include + +#include +#include +#include + +namespace mockturtle +{ + +#pragma region has_set_bounds +template +struct has_set_bounds : std::false_type +{ +}; + +template +struct has_set_bounds().set_bounds( std::optional(), std::optional() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_bounds_v = has_set_bounds::value; +#pragma endregion + +#pragma region has_clear_functions +template +struct has_clear_functions : std::false_type +{ +}; + +template +struct has_clear_functions().clear_functions() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_functions_v = has_clear_functions::value; +#pragma endregion + +#pragma region has_add_function +template +struct has_add_function : std::false_type +{ +}; + +template +struct has_add_function().add_function( signal(), kitty::dynamic_truth_table() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_function_v = has_add_function::value; +#pragma endregion + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc.hpp new file mode 100644 index 00000000000..b51a7575e00 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc.hpp @@ -0,0 +1,472 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_minmc.hpp + \brief XAG resynthesis + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/cut_view.hpp" +#include "../cleanup.hpp" +#include "../simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for xag_minmc_resynthesis. */ +struct xag_minmc_resynthesis_params +{ + /*! \brief Print statistics when resynthesis object is destroyed. */ + bool print_stats{ false }; + + /*! \brief Threshold for exhaustive don't care search. + * + * If the don't care set is smaller than this size, all possible covers with + * respect to the don't cares are explored. Otherwise all covers are created + * that the on-set is extended by at most one element from the don't care set. + */ + uint32_t exhaustive_dc_limit{ 10u }; + + /*! \brief Verify database when parsing. */ + bool verify_database{ false }; +}; + +/*! \brief Statistics for xag_minmc_resynthesis. */ +struct xag_minmc_resynthesis_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time to parse database. */ + stopwatch<>::duration time_parse_db{ 0 }; + + /*! \brief Overall time to classify functions. */ + stopwatch<>::duration time_classify{ 0 }; + + /*! \brief Overall time to construct candidate. */ + stopwatch<>::duration time_construct{ 0 }; + + /*! \brief Cache hits for classified functions. */ + uint32_t cache_hits{ 0 }; + + /*! \brief Cache misses for classified functions. */ + uint32_t cache_misses{ 0 }; + + /*! \brief Number of aborts due to classification. */ + uint32_t classify_aborts{ 0 }; + + /*! \brief Number of aborts due to unknown function. */ + uint32_t unknown_function_aborts{ 0 }; + + /*! \brief Total number of don't cares considered. */ + uint32_t dont_cares{ 0 }; + + /*! \brief Prints report. */ + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] parse db time = {:>5.2f} secs\n", to_seconds( time_parse_db ) ); + std::cout << fmt::format( "[i] classify time = {:>5.2f} secs\n", to_seconds( time_classify ) ); + std::cout << fmt::format( "[i] - aborts = {:>5}\n", classify_aborts ); + std::cout << fmt::format( "[i] construct time = {:>5.2f} secs\n", to_seconds( time_construct ) ); + std::cout << fmt::format( "[i] cache hits = {:>5}\n", cache_hits ); + std::cout << fmt::format( "[i] cache misses = {:>5}\n", cache_misses ); + std::cout << fmt::format( "[i] unknown func. = {:>5}\n", unknown_function_aborts ); + std::cout << fmt::format( "[i] don't cares = {:>5}\n", dont_cares ); + } +}; + +/*! \brief Resynthesis function to minimize multiplicative complexity in XAGs. + * + * This resynthesis function can be passed to ``cut_rewriting`` with a cut size + * of at most 6. It will produce an XAG based on pre-computed XAGs with a + * minimum multiplicative complexity. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const xag_network xag = ...; + xag_minmc_resynthesis resyn; + xag = cut_rewriting( xag, resyn ); + \endverbatim + */ +class xag_minmc_resynthesis +{ +public: + /*! \brief Default constructor. + * + * \param filename Database file with precomputed functions (information to be added) + * \param ps Parameters + * \param pst Statistics + */ + xag_minmc_resynthesis( std::string const& filename, xag_minmc_resynthesis_params const& ps = {}, xag_minmc_resynthesis_stats* pst = nullptr ) + : ps( ps ), + pst( pst ), + db( std::make_shared() ), + db_pis( std::make_shared( 6u ) ), + func_mc( std::make_shared() ), + classify_cache( std::make_shared() ) + { + build_db( filename ); + } + + virtual ~xag_minmc_resynthesis() + { + if ( ps.print_stats ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + + template + void operator()( xag_network& xag, kitty::dynamic_truth_table function, kitty::dynamic_truth_table const& dont_cares, LeavesIterator begin, LeavesIterator end, Fn&& fn ) + { + if ( !kitty::is_const0( dont_cares ) ) + { + const auto cnt = kitty::count_ones( dont_cares ); + st.dont_cares += cnt; + + if ( cnt <= ps.exhaustive_dc_limit ) + { + std::vector ones; + kitty::for_each_one_bit( dont_cares, [&]( auto bit ) { + ones.push_back( bit ); + kitty::clear_bit( function, bit ); + } ); + + for ( auto i = 0u; i < ( 1u << ones.size() ); ++i ) + { + for ( auto j = 0u; j < ones.size(); ++j ) + { + if ( ( i >> j ) & 1 ) + { + kitty::set_bit( function, ones[j] ); + } + else + { + kitty::clear_bit( function, ones[j] ); + } + } + ( *this )( xag, function, begin, end, fn ); + } + } + else + { + ( *this )( xag, function, begin, end, fn ); + kitty::for_each_one_bit( dont_cares, [&]( auto bit ) { + kitty::flip_bit( function, bit ); + ( *this )( xag, function, begin, end, fn ); + kitty::flip_bit( function, bit ); + } ); + } + } + else + { + ( *this )( xag, function, begin, end, fn ); + } + } + + template + void operator()( xag_network& xag, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) + { + stopwatch t1( st.time_total ); + + const auto func_ext = kitty::extend_to<6u>( function ); + std::vector trans; + kitty::static_truth_table<6u> tt_ext; + + const auto cache_it = classify_cache->find( func_ext ); + + if ( cache_it != classify_cache->end() ) + { + st.cache_hits++; + if ( !std::get<0>( cache_it->second ) ) + { + return; /* quit */ + } + tt_ext = std::get<1>( cache_it->second ); + trans = std::get<2>( cache_it->second ); + } + else + { + st.cache_misses++; + const auto spectral = call_with_stopwatch( st.time_classify, + [&]() { return kitty::exact_spectral_canonization_limit( func_ext, 100000, + [&trans]( auto const& ops ) { + std::copy( ops.begin(), ops.end(), + std::back_inserter( trans ) ); + } ); } ); + classify_cache->insert( { func_ext, { spectral.second, spectral.first, trans } } ); + if ( !spectral.second ) + { + st.classify_aborts++; + return; /* quit */ + } + tt_ext = spectral.first; + } + + xag_network::signal circuit; + + auto search = func_mc->find( kitty::to_hex( tt_ext ) ); + if ( search != func_mc->end() ) + { + unsigned int mc{ 0u }; + std::string original_f; + + std::tie( original_f, mc, circuit ) = search->second; + + kitty::static_truth_table<6u> db_repr; + kitty::create_from_hex_string( db_repr, original_f ); + + call_with_stopwatch( st.time_classify, [&]() { return kitty::exact_spectral_canonization( + db_repr, [&trans]( auto const& ops ) { + std::copy( ops.rbegin(), ops.rend(), + std::back_inserter( trans ) ); + } ); } ); + } + else if ( kitty::is_const0( tt_ext ) ) + { + circuit = db->get_constant( false ); + } + else + { + // std::cout << "[w] unknown " << kitty::to_hex( tt_ext ) << " from " << kitty::to_hex( func_ext ) << "\n"; + st.unknown_function_aborts++; + return; /* quit */ + } + + bool out_neg{ false }; + std::vector final_xor; + std::vector pis( 6, xag.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + stopwatch t2( st.time_construct ); + for ( auto const& t : trans ) + { + switch ( t._kind ) + { + default: + assert( false ); + case kitty::detail::spectral_operation::kind::permutation: + { + const auto v1 = log2( t._var1 ); + const auto v2 = log2( t._var2 ); + std::swap( pis[v1], pis[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::input_negation: + { + const auto v1 = log2( t._var1 ); + pis[v1] = !pis[v1]; + } + break; + case kitty::detail::spectral_operation::kind::output_negation: + out_neg = !out_neg; + break; + case kitty::detail::spectral_operation::kind::spectral_translation: + { + const auto v1 = log2( t._var1 ); + const auto v2 = log2( t._var2 ); + pis[v1] = xag.create_xor( pis[v1], pis[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::disjoint_translation: + { + const auto v1 = log2( t._var1 ); + final_xor.push_back( pis[v1] ); + } + break; + } + } + + xag_network::signal output; + + if ( db->is_constant( db->get_node( circuit ) ) ) + { + output = xag.get_constant( db->is_complemented( circuit ) ); + } + else + { + cut_view topo{ *db, *db_pis, circuit }; + output = cleanup_dangling( topo, xag, pis.begin(), pis.end() ).front(); + } + + for ( auto const& g : final_xor ) + { + output = xag.create_xor( output, g ); + } + + fn( out_neg ? !output : output ); + } + +private: + void build_db( std::string const& filename ) + { + stopwatch t1( st.time_total ); + stopwatch t2( st.time_parse_db ); + + std::generate( db_pis->begin(), db_pis->end(), [&]() { return db->create_pi(); } ); + + std::ifstream file1( filename.c_str(), std::ifstream::in ); + std::string line; + unsigned pos{ 0u }; + + // std::ofstream db_file( "/tmp/db", std::ofstream::out ); + + while ( std::getline( file1, line ) ) + { + pos = static_cast( line.find( '\t' ) ); + const auto name = line.substr( 0, pos++ ); + auto original = line.substr( pos, 16u ); + pos += 17u; + const auto token_f = line.substr( pos, 16u ); + pos += 17u; + auto mc = std::stoul( line.substr( pos, 1u ) ); + pos += 2u; + line.erase( 0, pos ); + + auto circuit = line; + // auto orig_circuit = circuit; + + const std::string delimiter = " "; + std::string token = circuit.substr( 0, circuit.find( ' ' ) ); + circuit.erase( 0, circuit.find( ' ' ) + 1 ); + const auto inputs = std::stoul( token ); + + std::vector hashing_circ( db_pis->begin(), db_pis->begin() + inputs ); + + while ( circuit.size() > 4 ) + { + std::array signals; + std::vector ff( 2 ); + for ( auto j = 0u; j < 2u; j++ ) + { + token = circuit.substr( 0, circuit.find( ' ' ) ); + circuit.erase( 0, circuit.find( ' ' ) + 1 ); + signals[j] = std::stoul( token ); + if ( signals[j] == 0 ) + { + ff[j] = db->get_constant( false ); + } + else if ( signals[j] == 1 ) + { + ff[j] = db->get_constant( true ); + } + else + { + ff[j] = hashing_circ[signals[j] / 2 - 1] ^ ( signals[j] % 2 != 0 ); + } + } + circuit.erase( 0, circuit.find( ' ' ) + 1 ); + + if ( signals[0] > signals[1] ) + { + hashing_circ.push_back( db->create_xor( ff[0], ff[1] ) ); + } + else + { + hashing_circ.push_back( db->create_and( ff[0], ff[1] ) ); + } + } + + const auto output = std::stoul( circuit ); + const auto f = hashing_circ[output / 2 - 1] ^ ( output % 2 != 0 ); + db->create_po( f ); + + /* verify */ + if ( ps.verify_database ) + { + cut_view view{ *db, *db_pis, f }; + kitty::static_truth_table<6u> tt, tt_repr; + kitty::create_from_hex_string( tt, original ); + kitty::create_from_hex_string( tt_repr, token_f ); + auto result = simulate>( view )[0]; + if ( tt != result ) + { + std::cerr << "[w] invalid circuit for " << original << ", got " << kitty::to_hex( result ) << "\n"; + original = kitty::to_hex( result ); + + const auto repr = exact_spectral_canonization( tt ); + if ( repr != tt_repr ) + { + std::cerr << "[e] representatives do not match\n"; + } + } + + // db_file << name << "\t" << token_f << "\t" << original << "\t" << mc << "\t" << orig_circuit << "\n"; + } + + func_mc->insert( { token_f, { original, mc, f } } ); + } + } + +public: + xag_minmc_resynthesis_params ps; + xag_minmc_resynthesis_stats st; + +private: + xag_minmc_resynthesis_stats* pst{ nullptr }; + + std::shared_ptr db; + std::shared_ptr> db_pis; + std::shared_ptr>> func_mc; + std::shared_ptr, std::tuple, std::vector>, kitty::hash>>> classify_cache; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp new file mode 100644 index 00000000000..538af6b38a4 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp @@ -0,0 +1,172 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_minmc2.hpp + \brief XAG resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../../networks/xag.hpp" +#include "../../utils/index_list/index_list.hpp" +#include "../detail/minmc_xags.hpp" +#include "../equivalence_classes.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle::future +{ + +struct xag_minmc_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct xag_minmc_resynthesis_stats +{ + /*! \brief Database size in bytes. */ + uint64_t db_size{}; +}; + +template +class xag_minmc_resynthesis +{ +public: + xag_minmc_resynthesis() + { + build_db(); + } + + void load_from_file( std::string const& filename ) + { + if ( ps_.verbose ) + { + fmt::print( "start loading\n" ); + } + kitty::dynamic_truth_table func( 6u ); + std::ifstream in( filename, std::ifstream::in ); + std::string line; + while ( std::getline( in, line ) ) + { + const auto vline = lorina::detail::split( line, " " ); + kitty::create_from_hex_string( func, vline[0] ); + const auto sindexes = lorina::detail::split( vline[3], "," ); + std::vector index_list( sindexes.size() ); + std::transform( sindexes.begin(), sindexes.end(), index_list.begin(), [&]( std::string const& s ) { return static_cast( std::stoul( s ) ); } ); + db_[6u][*func.cbegin()] = index_list; + st_.db_size += sizeof( uint64_t ) + sizeof( sindexes ) + sizeof( uint32_t ) * sindexes.size(); + } + if ( ps_.verbose ) + { + fmt::print( "done loading, size = {:>5.2f} Kb\n", st_.db_size / 1024.0f ); + } + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + const auto num_vars = function.num_vars(); + uint64_t repr; + std::vector trans; + + if ( const auto itCache = cache_[num_vars].find( *function.cbegin() ); itCache != cache_[num_vars].end() ) + { + repr = itCache->second.first; + trans = itCache->second.second; + } + else + { + repr = *kitty::hybrid_exact_spectral_canonization( function, [&]( auto const& ops ) { trans = ops; } ).cbegin(); + cache_[num_vars][*function.cbegin()] = { repr, trans }; + } + + const auto it = db_[num_vars].find( repr ); + if ( it == db_[num_vars].end() ) + { + fmt::print( "[w] cannot find repr {:x} in database.\n", repr ); + return; + } + + const auto f = apply_spectral_transformations( ntk, trans, std::vector>( begin, end ), [&]( xag_network& ntk, std::vector> const& leaves ) { + xag_index_list il{ it->second }; + std::vector pos; + insert( ntk, std::begin( leaves ), std::begin( leaves ) + il.num_pis(), il, + [&]( xag_network::signal const& f ) { + pos.push_back( f ); + } ); + assert( pos.size() == 1u ); + return pos[0u]; + } ); + + fn( f ); + } + +private: + void build_db() + { + st_.db_size += sizeof( db_ ); + + for ( auto i = 0u; i < detail::minmc_xags.size(); ++i ) + { + for ( auto const& [_, word, repr, expr] : detail::minmc_xags[i] ) + { + (void)_; + (void)expr; + db_[i][word] = repr; + st_.db_size += sizeof( word ) + sizeof( repr ) + sizeof( uint32_t ) * repr.size(); + } + } + + if ( ps_.verbose ) + { + fmt::print( "[i] db size = {:>5.2f} Kb\n", st_.db_size / 1024.0f ); + } + } + +private: + std::vector>> db_{ 7u }; + mutable std::vector>>> cache_{ 7u }; + +private: + xag_minmc_resynthesis_params ps_; + xag_minmc_resynthesis_stats st_; +}; + +} // namespace mockturtle::future \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp new file mode 100644 index 00000000000..ed1c51949bc --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp @@ -0,0 +1,691 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_npn.hpp + \brief Replace with size-optimum XAGs and AIGs from NPN (from ABC rewrite) + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../algorithms/simulation.hpp" +#include "../../networks/xag.hpp" +#include "../../utils/index_list/index_list.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" + +namespace mockturtle +{ + +struct xag_npn_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct xag_npn_resynthesis_stats +{ + stopwatch<>::duration time_classes{ 0 }; + stopwatch<>::duration time_db{ 0 }; + + uint32_t db_size; + uint32_t covered_classes; + + void report() const + { + std::cout << fmt::format( "[i] build classes time = {:>5.2f} secs\n", to_seconds( time_classes ) ); + std::cout << fmt::format( "[i] build db time = {:>5.2f} secs\n", to_seconds( time_db ) ); + } +}; + +enum class xag_npn_db_kind : uint32_t +{ + xag_incomplete = 0, + xag_complete = 1, + aig_complete = 2, +}; + +/*! \brief Resynthesis function based on pre-computed AIGs. + * + * This resynthesis function can be passed to ``cut_rewriting``. It will + * produce a network based on pre-computed XAGs with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const aig_network aig = ...; + xag_npn_resynthesis resyn; + aig = cut_rewriting( aig, resyn ); + + .. note:: + + The implementation of this algorithm was heavily inspired by the rewrite + command in AIG. It uses the same underlying database of subcircuits. + \endverbatim + */ +template +class xag_npn_resynthesis +{ +public: + xag_npn_resynthesis( xag_npn_resynthesis_params const& ps = {}, xag_npn_resynthesis_stats* pst = nullptr ) + : ps( ps ), + pst( pst ), + _repr( 1u << 16u ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + static_assert( is_network_type_v, "DatabaseNtk is not a network type" ); + static_assert( has_get_node_v, "DatabaseNtk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "DatabaseNtk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "DatabaseNtk does not implement the is_xor method" ); + static_assert( has_size_v, "DatabaseNtk does not implement the size method" ); + static_assert( has_create_pi_v, "DatabaseNtk does not implement the create_pi method" ); + static_assert( has_create_and_v, "DatabaseNtk does not implement the create_and method" ); + static_assert( has_create_xor_v, "DatabaseNtk does not implement the create_xor method" ); + static_assert( has_foreach_fanin_v, "DatabaseNtk does not implement the foreach_fanin method" ); + static_assert( has_foreach_node_v, "DatabaseNtk does not implement the foreach_node method" ); + static_assert( has_make_signal_v, "DatabaseNtk does not implement the make_signal method" ); + + build_classes(); + build_db(); + } + + virtual ~xag_npn_resynthesis() + { + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + kitty::static_truth_table<4u> tt = kitty::extend_to<4u>( function ); + + /* get representative of function */ + const auto [repr, phase, perm] = _repr[*tt.cbegin()]; + + /* check if representative has circuits */ + const auto it = _repr_to_signal.find( repr ); + if ( it == _repr_to_signal.end() ) + { + return; + } + + std::vector> pis( 4, ntk.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::unordered_map, signal> db_to_ntk; + db_to_ntk.insert( { 0, ntk.get_constant( false ) } ); + for ( auto i = 0; i < 4; ++i ) + { + db_to_ntk.insert( { i + 1, ( phase >> perm[i] & 1 ) ? ntk.create_not( pis[perm[i]] ) : pis[perm[i]] } ); + } + + for ( auto const& cand : it->second ) + { + const auto f = copy_db_entry( ntk, _db.get_node( cand ), db_to_ntk ); + if ( !fn( _db.is_complemented( cand ) != ( phase >> 4 & 1 ) ? ntk.create_not( f ) : f ) ) + { + return; + } + } + } + +private: + signal + copy_db_entry( Ntk& ntk, node const& n, std::unordered_map, signal>& db_to_ntk ) const + { + if ( const auto it = db_to_ntk.find( n ); it != db_to_ntk.end() ) + { + return it->second; + } + + std::array, 2> fanin{}; + _db.foreach_fanin( n, [&]( auto const& f, auto i ) { + const auto ntk_f = copy_db_entry( ntk, _db.get_node( f ), db_to_ntk ); + fanin[i] = _db.is_complemented( f ) ? ntk.create_not( ntk_f ) : ntk_f; + } ); + + const auto f = _db.is_xor( n ) ? ntk.create_xor( fanin[0], fanin[1] ) : ntk.create_and( fanin[0], fanin[1] ); + db_to_ntk.insert( { n, f } ); + return f; + } + + void build_classes() + { + stopwatch t( st.time_classes ); + + kitty::static_truth_table<4u> tt; + do + { + _repr[*tt.cbegin()] = kitty::exact_npn_canonization( tt ); + kitty::next_inplace( tt ); + } while ( !kitty::is_const0( tt ) ); + } + + void build_db() + { + stopwatch t( st.time_db ); + + if constexpr ( DBKind == xag_npn_db_kind::xag_incomplete ) + { + decode( _db, xag_index_list{ std::vector{ subgraphs, subgraphs + sizeof subgraphs / sizeof subgraphs[0] } } ); + } + else if constexpr ( DBKind == xag_npn_db_kind::xag_complete ) + { + decode( _db, xag_index_list{ std::vector{ subgraphs_xag, subgraphs_xag + sizeof subgraphs_xag / sizeof subgraphs_xag[0] } } ); + } + else if constexpr ( DBKind == xag_npn_db_kind::aig_complete ) + { + decode( _db, xag_index_list{ std::vector{ subgraphs_aig, subgraphs_aig + sizeof subgraphs_aig / sizeof subgraphs_aig[0] } } ); + } + const auto sim_res = simulate_nodes>( _db ); + + _db.foreach_node( [&]( auto n ) { + if ( std::get<0>( _repr[*sim_res[n].cbegin()] ) == sim_res[n] ) + { + if ( _repr_to_signal.count( sim_res[n] ) == 0 ) + { + _repr_to_signal.insert( { sim_res[n], { _db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[sim_res[n]].push_back( _db.make_signal( n ) ); + } + } + else + { + const auto f = ~sim_res[n]; + if ( std::get<0>( _repr[*f.cbegin()] ) == f ) + { + if ( _repr_to_signal.count( f ) == 0 ) + { + _repr_to_signal.insert( { f, { !_db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[f].push_back( !_db.make_signal( n ) ); + } + } + } + } ); + + st.db_size = _db.size(); + st.covered_classes = static_cast( _repr_to_signal.size() ); + } + + xag_npn_resynthesis_params ps; + xag_npn_resynthesis_stats st; + xag_npn_resynthesis_stats* pst{ nullptr }; + + std::vector, uint32_t, std::vector>> _repr; + std::unordered_map, std::vector>, kitty::hash>> _repr_to_signal; + + DatabaseNtk _db; + + // clang-format off + /* complete XAG database */ + inline static const uint32_t subgraphs_xag[] = { 38460932, 6, 4, 10, 8, + 8, 6, 3, 5, 16, 14, 3, 10, 20, 8, 22, 6, 2, 6, 4, 27, 8, 2, 30, 28, + 7, 8, 2, 35, 4, 37, 6, 9, 40, 2, 42, 38, 4, 2, 7, 46, 17, 49, 50, 8, + 4, 8, 54, 6, 3, 56, 5, 7, 60, 8, 62, 58, 3, 8, 66, 4, 8, 68, 4, 71, + 7, 69, 74, 72, 76, 2, 2, 4, 46, 8, 6, 83, 81, 85, 86, 8, 15, 83, 90, + 80, 4, 15, 2, 95, 96, 14, 98, 54, 4, 6, 3, 102, 8, 105, 106, 2, 108, + 60, 30, 4, 2, 112, 114, 6, 114, 8, 116, 118, 120, 112, 6, 54, 54, 2, + 5, 6, 128, 126, 125, 130, 6, 81, 8, 135, 136, 2, 138, 4, 2, 8, 4, + 142, 6, 145, 146, 4, 148, 2, 6, 2, 152, 4, 6, 31, 154, 157, 7, 55, + 3, 9, 162, 160, 46, 6, 8, 167, 166, 4, 47, 171, 169, 173, 8, 4, 47, + 153, 177, 179, 180, 152, 9, 167, 2, 177, 7, 187, 188, 8, 31, 177, 7, + 193, 194, 8, 7, 145, 198, 8, 6, 67, 11, 203, 9, 10, 206, 2, 208, + 204, 16, 6, 9, 212, 7, 81, 216, 214, 6, 8, 4, 152, 222, 162, 221, + 225, 166, 8, 81, 213, 16, 231, 9, 230, 234, 232, 9, 216, 10, 152, + 14, 241, 4, 31, 7, 163, 246, 8, 245, 249, 14, 46, 14, 2, 254, 4, 7, + 257, 2, 256, 14, 261, 259, 263, 8, 17, 135, 267, 2, 221, 4, 270, + 272, 6, 274, 8, 48, 8, 66, 28, 221, 281, 7, 9, 284, 2, 286, 4, 221, + 288, 3, 6, 267, 293, 2, 7, 8, 297, 298, 2, 298, 4, 300, 302, 304, 6, + 5, 153, 9, 309, 310, 60, 142, 4, 162, 6, 314, 317, 318, 8, 6, 80, 9, + 323, 5, 27, 326, 2, 328, 6, 8, 328, 331, 333, 9, 153, 14, 154, 337, + 339, 2, 15, 4, 343, 344, 2, 346, 6, 46, 284, 9, 223, 7, 154, 354, + 352, 2, 9, 7, 359, 360, 8, 11, 363, 364, 358, 296, 4, 7, 368, 368, + 2, 9, 373, 371, 375, 6, 176, 359, 379, 380, 4, 154, 221, 16, 8, 231, + 387, 8, 103, 390, 4, 2, 14, 394, 392, 8, 217, 17, 399, 400, 216, + 346, 220, 7, 31, 15, 407, 113, 409, 162, 4, 7, 413, 414, 2, 8, 415, + 416, 419, 216, 8, 10, 2, 7, 424, 2, 11, 9, 429, 430, 426, 7, 113, + 434, 8, 4, 9, 6, 439, 154, 441, 442, 8, 80, 6, 47, 447, 8, 449, 450, + 446, 5, 9, 253, 455, 4, 163, 3, 7, 460, 458, 221, 463, 9, 155, 466, + 354, 3, 11, 470, 8, 55, 473, 5, 284, 4, 285, 478, 2, 221, 480, 477, + 483, 15, 461, 14, 4, 488, 2, 487, 491, 2, 63, 494, 60, 255, 489, + 221, 499, 358, 6, 5, 503, 504, 8, 8, 152, 3, 509, 510, 176, 309, + 512, 498, 6, 2, 10, 518, 6, 520, 8, 10, 521, 522, 525, 14, 223, 2, + 103, 9, 531, 532, 60, 8, 61, 47, 537, 11, 14, 163, 541, 4, 14, 544, + 6, 10, 461, 55, 549, 544, 360, 6, 55, 554, 4, 14, 176, 11, 221, 163, + 560, 562, 8, 14, 67, 566, 326, 26, 4, 9, 570, 572, 60, 9, 254, 14, + 257, 577, 579, 8, 47, 11, 583, 584, 2, 7, 176, 176, 2, 9, 590, 589, + 593, 102, 8, 2, 596, 598, 4, 600, 6, 9, 103, 438, 6, 4, 7, 359, 609, + 610, 606, 6, 571, 8, 615, 616, 570, 11, 30, 620, 6, 622, 2, 3, 4, 8, + 609, 627, 629, 630, 6, 5, 293, 8, 635, 2, 637, 638, 634, 9, 425, + 642, 60, 358, 4, 646, 6, 8, 646, 649, 651, 30, 176, 5, 655, 656, 30, + 7, 658, 660, 654, 27, 537, 608, 8, 359, 667, 668, 6, 3, 221, 5, 672, + 285, 675, 9, 152, 61, 679, 14, 583, 14, 30, 176, 684, 28, 2, 9, 689, + 61, 163, 8, 10, 103, 695, 27, 425, 14, 699, 394, 6, 545, 703, 5, + 246, 706, 8, 103, 709, 6, 47, 9, 713, 162, 60, 390, 6, 718, 4, 6, + 359, 5, 723, 724, 8, 7, 16, 728, 8, 674, 8, 14, 47, 734, 6, 3, 176, + 6, 738, 8, 739, 741, 743, 471, 537, 746, 60, 470, 6, 11, 751, 473, + 753, 293, 387, 30, 537, 8, 247, 4, 761, 762, 246, 3, 14, 8, 767, 5, + 14, 770, 766, 769, 773, 4, 153, 3, 777, 778, 4, 8, 780, 776, 6, 784, + 782, 21, 267, 5, 285, 2, 791, 792, 284, 221, 795, 143, 771, 798, 2, + 152, 771, 221, 803, 46, 607, 806, 8, 766, 8, 488, 811, 7, 387, 9, + 47, 816, 814, 102, 2, 596, 821, 822, 60, 387, 713, 5, 30, 8, 829, + 30, 6, 832, 4, 831, 834, 230, 8, 143, 785, 387, 447, 30, 61, 293, + 845, 846, 2, 267, 447, 7, 177, 163, 853, 854, 6, 240, 8, 60, 2, 63, + 860, 55, 620, 3, 285, 5, 867, 868, 2, 221, 871, 14, 654, 874, 8, 4, + 143, 878, 2, 878, 6, 881, 883, 884, 8, 267, 713, 10, 30, 176, 424, + 891, 893, 163, 556, 5, 766, 898, 8, 9, 240, 9, 179, 102, 30, 537, + 906, 46, 441, 910, 8, 460, 4, 7, 915, 914, 2, 9, 918, 917, 921, 8, + 471, 103, 925, 212, 4, 267, 928, 167, 387, 292, 4, 157, 935, 936, + 30, 46, 221, 394, 4, 11, 943, 16, 284, 296, 60, 15, 297, 949, 951, + 176, 6, 743, 954, 15, 163, 11, 959, 3, 60, 962, 604, 9, 17, 966, + 216, 588, 162, 63, 820, 972, 60, 394, 8, 4, 977, 978, 14, 284, 673, + 982, 674, 8, 323, 986, 154, 766, 4, 221, 991, 5, 470, 994, 206, 6, + 187, 4, 67, 1000, 998, 6, 17, 9, 1005, 9, 820, 61, 1009, 386, 6, 81, + 1013, 1005, 1014, 15, 31, 47, 1019, 1020, 6, 2, 61, 1024, 4, 1026, + 6, 9, 1029, 152, 8, 6, 1032, 152, 439, 1036, 4, 1035, 1038, 2, 102, + 9, 1043, 1044, 60, 7, 591, 1048, 8, 739, 1051, 6, 155, 3, 155, 1056, + 8, 1055, 1059, 4, 359, 1062, 2, 7, 1064, 1066, 8, 503, 647, 1070, 8, + 61, 143, 1074, 2, 518, 4, 9, 1079, 460, 8, 47, 1083, 674, 284, 3, + 177, 1088, 6, 1032, 1091, 285, 673, 4, 1095, 1096, 672, 28, 8, 1100, + 6, 689, 1102, 9, 519, 1106, 60, 9, 60, 9, 739, 1112, 188, 155, 267, + 7, 17, 1118, 4, 9, 1121, 503, 591, 1124, 8, 9, 213, 296, 8, 4, 1131, + 359, 1133, 1134, 6, 9, 81, 424, 1138, 634, 2, 221, 1143, 530, 6, 9, + 1146, 61, 1149, 6, 46, 9, 1153, 81, 1154, 187, 424, 1158, 8, 9, 61, + 1162, 6, 46, 1165, 21, 387, 2, 853, 1170, 4, 221, 1173, 2, 455, + 1176, 10, 221, 1178, 13, 18, 24, 32, 45, 52, 65, 79, 89, 93, 101, + 110, 123, 132, 141, 150, 158, 164, 174, 182, 184, 191, 197, 201, + 211, 218, 226, 228, 236, 238, 242, 250, 252, 265, 268, 276, 279, + 282, 290, 294, 307, 312, 321, 324, 334, 341, 348, 350, 356, 366, + 377, 382, 384, 388, 397, 402, 404, 410, 420, 338, 423, 432, 437, 14, + 445, 452, 457, 464, 468, 474, 485, 492, 496, 500, 507, 514, 517, + 526, 528, 534, 63, 538, 543, 547, 550, 552, 557, 558, 565, 568, 574, + 581, 587, 595, 602, 604, 612, 206, 618, 624, 633, 640, 644, 652, + 662, 664, 671, 677, 681, 682, 686, 690, 693, 696, 700, 704, 710, + 714, 716, 720, 727, 731, 9, 733, 737, 744, 748, 754, 756, 758, 764, + 774, 787, 788, 796, 801, 804, 809, 812, 818, 824, 826, 836, 839, + 840, 842, 848, 850, 856, 859, 862, 864, 872, 877, 884, 887, 888, + 895, 897, 901, 902, 904, 908, 913, 923, 926, 930, 932, 938, 940, + 944, 946, 952, 956, 960, 964, 968, 970, 974, 980, 984, 989, 992, + 996, 1003, 1006, 1011, 1016, 1023, 1030, 1040, 1046, 1052, 1060, + 1069, 1073, 1077, 1080, 1084, 1086, 1092, 1098, 1104, 1108, 284, + 1110, 1114, 8, 1116, 1122, 1127, 1128, 1136, 1140, 1144, 1151, 1156, + 1161, 1166, 1168, 1174, 1180 }; + + /* complete AIG database */ + inline static const uint32_t subgraphs_aig [] = { 52223492, 4, 7, 5, + 6, 11, 13, 8, 14, 9, 15, 17, 19, 3, 5, 6, 9, 7, 8, 25, 27, 23, 29, + 22, 28, 31, 33, 3, 4, 2, 6, 37, 39, 8, 40, 9, 41, 43, 45, 2, 5, 2, + 7, 4, 51, 49, 53, 8, 55, 9, 54, 57, 59, 2, 26, 25, 63, 2, 63, 5, + 67, 65, 68, 64, 69, 71, 73, 2, 4, 6, 23, 77, 79, 8, 80, 9, 81, 83, + 85, 5, 39, 8, 88, 2, 8, 6, 39, 93, 95, 89, 96, 91, 99, 4, 8, 2, + 103, 13, 104, 13, 27, 3, 109, 107, 111, 8, 76, 6, 22, 77, 117, 27, + 118, 115, 121, 7, 9, 22, 125, 6, 8, 23, 129, 76, 125, 130, 133, + 127, 135, 5, 8, 3, 139, 93, 141, 4, 141, 7, 145, 142, 146, 143, + 147, 149, 151, 5, 7, 8, 155, 9, 154, 157, 159, 2, 161, 4, 6, 161, + 165, 3, 167, 163, 169, 2, 9, 4, 173, 3, 8, 7, 176, 174, 179, 39, + 179, 5, 183, 181, 185, 4, 9, 155, 189, 2, 190, 165, 190, 3, 195, + 193, 197, 23, 77, 6, 77, 8, 203, 200, 205, 201, 204, 207, 209, 7, + 23, 77, 212, 9, 76, 23, 217, 6, 219, 215, 221, 23, 25, 77, 129, + 224, 226, 225, 227, 229, 231, 3, 9, 7, 103, 235, 236, 234, 237, + 239, 241, 23, 115, 7, 245, 8, 23, 77, 249, 6, 251, 247, 253, 23, + 125, 227, 257, 226, 256, 259, 261, 6, 200, 7, 201, 265, 267, 9, + 269, 49, 177, 7, 273, 25, 275, 7, 173, 4, 177, 278, 281, 25, 283, + 7, 77, 8, 287, 125, 289, 9, 165, 155, 293, 3, 295, 8, 164, 2, 294, + 299, 301, 297, 302, 4, 50, 5, 234, 307, 309, 129, 310, 11, 23, + 177, 315, 129, 317, 37, 49, 6, 320, 7, 321, 323, 325, 8, 326, 9, + 327, 329, 331, 9, 77, 7, 334, 23, 337, 129, 339, 9, 286, 26, 77, + 9, 23, 6, 346, 345, 349, 4, 234, 7, 139, 2, 354, 353, 357, 129, + 358, 77, 256, 129, 362, 2, 124, 4, 366, 3, 125, 4, 371, 367, 373, + 129, 374, 369, 377, 203, 249, 6, 76, 343, 383, 289, 384, 8, 325, + 9, 324, 389, 391, 8, 11, 51, 394, 51, 235, 4, 399, 397, 401, 367, + 371, 4, 405, 5, 404, 407, 409, 129, 411, 3, 6, 249, 415, 5, 176, + 4, 129, 2, 420, 419, 423, 6, 424, 7, 425, 427, 429, 5, 9, 2, 432, + 157, 435, 3, 7, 7, 439, 2, 441, 4, 443, 439, 444, 8, 445, 441, + 448, 447, 451, 9, 383, 24, 37, 325, 457, 9, 37, 11, 461, 50, 463, + 51, 462, 465, 467, 88, 439, 173, 439, 4, 473, 471, 475, 50, 139, + 139, 189, 3, 481, 479, 483, 129, 484, 155, 235, 293, 488, 292, + 489, 491, 493, 6, 321, 9, 51, 320, 499, 497, 501, 5, 172, 129, + 505, 4, 278, 506, 509, 7, 200, 24, 201, 513, 515, 7, 22, 347, 519, + 337, 521, 5, 399, 398, 420, 525, 527, 22, 286, 287, 346, 531, 533, + 3, 129, 4, 536, 2, 128, 4, 125, 541, 543, 537, 544, 539, 547, 8, + 77, 212, 551, 9, 201, 213, 555, 553, 557, 154, 370, 9, 371, 155, + 562, 561, 565, 24, 320, 26, 321, 569, 571, 8, 286, 9, 287, 575, + 577, 2, 542, 3, 543, 7, 583, 4, 543, 9, 587, 585, 589, 581, 591, + 25, 325, 125, 129, 9, 212, 8, 213, 77, 601, 598, 602, 599, 603, + 605, 607, 7, 93, 4, 235, 611, 612, 610, 613, 615, 617, 129, 618, + 5, 370, 7, 623, 8, 625, 373, 627, 3, 236, 129, 631, 77, 632, 125, + 201, 129, 637, 3, 154, 8, 641, 155, 165, 234, 645, 643, 647, 5, + 124, 3, 651, 542, 653, 543, 652, 129, 657, 655, 658, 4, 370, 5, + 367, 371, 664, 663, 667, 129, 669, 173, 641, 3, 124, 5, 674, 581, + 677, 129, 678, 9, 49, 155, 682, 154, 683, 685, 687, 248, 286, 22, + 287, 76, 249, 693, 695, 691, 696, 9, 22, 265, 701, 115, 702, 334, + 415, 157, 707, 6, 460, 4, 37, 7, 713, 8, 714, 711, 717, 2, 165, 9, + 721, 155, 722, 154, 723, 725, 727, 8, 154, 9, 155, 731, 733, 5, + 129, 2, 736, 173, 737, 739, 741, 165, 172, 157, 745, 13, 103, 51, + 188, 155, 751, 9, 50, 748, 755, 4, 24, 155, 759, 7, 138, 759, 763, + 2, 645, 9, 767, 731, 769, 8, 720, 6, 721, 9, 774, 773, 777, 4, + 778, 5, 779, 781, 783, 12, 173, 6, 172, 9, 789, 4, 791, 787, 793, + 2, 237, 4, 796, 129, 799, 631, 800, 125, 219, 7, 218, 77, 806, + 805, 809, 6, 235, 9, 813, 8, 812, 5, 817, 815, 819, 309, 821, 92, + 154, 3, 164, 93, 827, 155, 828, 825, 831, 188, 789, 279, 789, 5, + 837, 835, 839, 9, 644, 9, 39, 5, 844, 737, 845, 847, 849, 3, 138, + 25, 51, 2, 139, 5, 856, 855, 859, 853, 861, 77, 433, 7, 865, 9, + 865, 6, 869, 867, 871, 5, 439, 175, 875, 234, 293, 235, 295, 879, + 881, 188, 836, 839, 885, 27, 77, 25, 201, 889, 891, 888, 890, 893, + 895, 235, 237, 7, 234, 5, 900, 103, 903, 813, 904, 5, 536, 125, + 909, 6, 234, 9, 235, 4, 915, 7, 917, 913, 919, 8, 439, 237, 922, + 236, 923, 925, 927, 4, 172, 6, 930, 7, 852, 933, 935, 53, 682, + 157, 165, 6, 37, 8, 321, 943, 945, 129, 947, 5, 23, 6, 951, 249, + 953, 2, 154, 9, 957, 165, 958, 731, 961, 9, 323, 154, 234, 489, + 967, 160, 165, 5, 415, 9, 972, 7, 972, 8, 977, 975, 979, 8, 518, + 9, 519, 983, 985, 22, 26, 347, 989, 8, 320, 497, 993, 643, 827, 3, + 165, 2, 155, 999, 1001, 157, 1003, 3, 433, 154, 1007, 6, 1006, 9, + 1011, 155, 1013, 1009, 1015, 3, 11, 8, 1019, 27, 1018, 1021, 1023, + 8, 640, 173, 1027, 7, 235, 5, 1030, 4, 1031, 9, 1034, 1033, 1037, + 4, 537, 6, 1041, 9, 1043, 909, 1045, 6, 177, 5, 1049, 179, 1050, + 173, 179, 4, 1055, 1053, 1057, 9, 999, 5, 998, 7, 1062, 1061, + 1065, 129, 675, 581, 1068, 3, 13, 8, 1072, 5, 1074, 2, 12, 9, + 1073, 1079, 1080, 1077, 1083, 7, 857, 4, 857, 6, 1089, 9, 1090, + 1087, 1093, 324, 461, 325, 460, 1097, 1099, 93, 125, 415, 1102, 5, + 1105, 543, 1107, 5, 93, 7, 1110, 6, 1111, 2, 1114, 3, 1115, 1117, + 1119, 9, 1121, 1113, 1123, 8, 998, 2, 164, 9, 1128, 155, 1131, + 1126, 1132, 1127, 1133, 1135, 1137, 6, 335, 249, 701, 1141, 1142, + 77, 701, 7, 1147, 6, 1146, 249, 1151, 1149, 1152, 23, 579, 22, + 578, 1157, 1159, 77, 155, 93, 1163, 4, 415, 1031, 1167, 4, 1030, + 93, 1171, 1169, 1172, 9, 415, 155, 1176, 641, 1179, 287, 383, 249, + 1183, 125, 488, 124, 489, 1187, 1189, 5, 414, 2, 10, 1193, 1195, + 8, 1197, 9, 1196, 1199, 1201, 155, 172, 154, 176, 1205, 1207, 9, + 766, 730, 767, 1211, 1213, 875, 1007, 129, 1217, 455, 519, 23, + 165, 6, 173, 1223, 1225, 383, 641, 9, 1229, 643, 1231, 6, 201, + 249, 1235, 93, 415, 6, 415, 5, 1241, 1238, 1243, 8, 1242, 1239, + 1246, 1245, 1249, 155, 723, 9, 973, 53, 1254, 454, 519, 2, 293, 3, + 292, 157, 1263, 1261, 1264, 23, 1001, 9, 1268, 7, 1268, 8, 1273, + 1271, 1275, 5, 371, 37, 1279, 129, 1281, 165, 643, 49, 103, 7, + 1286, 6, 1287, 9, 1290, 1289, 1293, 7, 335, 265, 1297, 249, 1299, + 1050, 1054, 1057, 1303, 129, 200, 155, 1129, 93, 1309, 22, 124, 3, + 102, 49, 1315, 7, 1317, 25, 1319, 9, 645, 643, 1323, 9, 164, 154, + 173, 1327, 1329, 9, 640, 1284, 1333, 286, 347, 533, 1337, 27, 235, + 10, 1341, 11, 1340, 1343, 1345, 2, 759, 9, 1348, 3, 761, 1351, + 1353, 461, 714, 460, 715, 1357, 1359, 9, 537, 7, 1362, 909, 1365, + 23, 423, 596, 1368, 597, 1369, 1371, 1373, 663, 1279, 129, 1377, + 641, 733, 165, 1381, 3, 189, 7, 1385, 4, 1386, 9, 1385, 6, 1391, + 1389, 1393, 9, 79, 155, 1061, 1129, 1399, 226, 257, 1313, 1402, 8, + 22, 286, 1407, 221, 1409, 9, 1129, 155, 998, 1412, 1415, 4, 439, + 173, 1418, 439, 1049, 5, 1423, 1421, 1425, 160, 1129, 7, 37, 9, + 36, 1431, 1433, 49, 1435, 129, 1437, 5, 51, 1177, 1441, 1176, + 1440, 129, 1445, 1443, 1446, 9, 213, 212, 550, 1451, 1453, 157, + 1129, 3, 158, 1456, 1459, 3, 155, 2, 733, 1463, 1465, 39, 460, + 235, 875, 234, 874, 1471, 1473, 103, 1475, 125, 908, 124, 909, + 1479, 1481, 675, 911, 2, 189, 51, 737, 1487, 1489, 4, 124, 909, + 1493, 675, 1495, 165, 1463, 9, 1499, 731, 1501, 6, 461, 3, 460, 5, + 1507, 1430, 1509, 1505, 1511, 2, 188, 23, 1515, 7, 1517, 6, 1516, + 9, 1520, 1519, 1523, 292, 957, 5, 438, 581, 1529, 129, 1530, 79, + 519, 9, 1534, 7, 347, 77, 1538, 349, 1541, 80, 984, 226, 519, 77, + 415, 9, 1549, 155, 1551, 79, 334, 8, 644, 165, 1557, 2, 1559, + 1323, 1557, 3, 1562, 1561, 1565, 129, 373, 23, 1568, 6, 433, 3, + 1573, 8, 1575, 5, 433, 1574, 1579, 1577, 1581, 9, 13, 2, 1585, 8, + 12, 5, 1589, 3, 1591, 1587, 1593, 8, 48, 1430, 1597, 1505, 1599, + 20, 35, 47, 60, 74, 87, 101, 113, 122, 137, 152, 170, 186, 198, + 210, 223, 233, 243, 254, 263, 270, 277, 285, 290, 304, 312, 318, + 333, 340, 342, 351, 360, 364, 379, 380, 386, 392, 402, 412, 416, + 430, 436, 452, 454, 459, 468, 477, 390, 486, 494, 502, 510, 517, + 522, 529, 535, 549, 558, 567, 573, 579, 592, 595, 596, 608, 620, + 628, 634, 638, 648, 660, 670, 673, 680, 689, 699, 704, 708, 719, + 729, 735, 742, 746, 748, 753, 756, 761, 765, 771, 784, 794, 802, + 811, 822, 833, 292, 841, 842, 850, 863, 872, 876, 883, 887, 897, + 899, 906, 911, 921, 928, 937, 938, 489, 940, 948, 954, 963, 964, + 968, 970, 980, 987, 9, 991, 994, 996, 1004, 1016, 1024, 1029, + 1039, 1047, 1059, 1067, 1070, 1085, 1095, 1101, 1108, 1125, 1139, + 1144, 1154, 1161, 1164, 1174, 1181, 1184, 1190, 1203, 1209, 1215, + 1218, 1221, 1226, 1232, 1236, 1251, 1253, 521, 1256, 1258, 1266, + 1276, 1282, 1284, 1295, 1300, 1305, 1306, 1310, 1312, 1321, 1324, + 1331, 1334, 1339, 1346, 1355, 1361, 1367, 1375, 1378, 1382, 1394, + 1396, 1400, 1404, 1411, 1416, 1426, 1428, 1438, 1448, 1455, 1460, + 1466, 1468, 1476, 1483, 1484, 1490, 1496, 1503, 124, 158, 1512, 8, + 1525, 1526, 1532, 1536, 1543, 1544, 1546, 1553, 1554, 1566, 1570, + 1582, 1594, 1600 }; + + /* incomplete XAG database */ + inline static const uint32_t subgraphs[] = {1780 << 16 | 0 << 8 | 4, + 2, 4, 2, 5, 3, 4, 3, 5, 4, 2, 2, 6, 2, 7, 3, 6, 3, 7, 6, 2, 4, 6, + 4, 7, 5, 6, 5, 7, 6, 4, 2, 8, 2, 9, 3, 8, 3, 9, 8, 2, 4, 8, + 4, 9, 5, 8, 5, 9, 8, 4, 6, 8, 6, 9, 7, 8, 7, 9, 8, 6, 5, 11, + 6, 10, 6, 11, 7, 10, 7, 11, 10, 6, 8, 10, 8, 11, 9, 10, 9, 11, 10, 8, + 6, 12, 6, 13, 7, 12, 7, 13, 12, 6, 9, 12, 9, 13, 12, 8, 5, 15, 6, 14, + 6, 15, 7, 14, 7, 15, 14, 6, 8, 14, 8, 15, 9, 14, 9, 15, 14, 8, 6, 16, + 6, 17, 7, 16, 7, 17, 16, 6, 8, 16, 8, 17, 9, 16, 9, 17, 16, 8, 6, 18, + 6, 19, 7, 18, 7, 19, 18, 6, 8, 19, 9, 18, 9, 19, 18, 8, 4, 20, 4, 21, + 5, 20, 7, 21, 8, 20, 9, 21, 20, 8, 11, 21, 20, 10, 15, 21, 20, 14, 17, 21, + 19, 21, 4, 22, 4, 23, 5, 22, 9, 22, 9, 23, 22, 8, 22, 12, 15, 23, 17, 23, + 18, 23, 4, 24, 7, 25, 9, 25, 24, 8, 11, 25, 13, 25, 15, 25, 24, 14, 19, 25, + 4, 26, 4, 27, 5, 26, 5, 27, 26, 4, 8, 27, 9, 26, 9, 27, 26, 8, 11, 27, + 13, 27, 17, 27, 26, 16, 19, 27, 4, 28, 28, 4, 9, 28, 9, 29, 28, 8, 11, 28, + 11, 29, 13, 29, 17, 29, 18, 29, 19, 28, 19, 29, 2, 30, 2, 31, 3, 30, 5, 31, + 7, 31, 8, 30, 8, 31, 9, 30, 9, 31, 30, 8, 13, 31, 17, 31, 19, 31, 23, 31, + 27, 31, 29, 31, 2, 32, 2, 33, 5, 33, 32, 6, 8, 33, 32, 8, 13, 33, 17, 33, + 21, 33, 25, 33, 27, 33, 28, 33, 32, 28, 2, 34, 3, 35, 34, 4, 7, 35, 34, 8, + 11, 35, 15, 35, 19, 35, 34, 18, 23, 35, 27, 35, 33, 35, 2, 36, 2, 37, 3, 36, + 3, 37, 36, 2, 8, 36, 8, 37, 9, 36, 9, 37, 36, 8, 11, 37, 15, 37, 17, 37, + 18, 37, 19, 37, 21, 37, 25, 37, 27, 37, 29, 37, 2, 38, 3, 38, 38, 2, 8, 38, + 8, 39, 9, 38, 9, 39, 38, 8, 11, 38, 11, 39, 15, 38, 15, 39, 17, 39, 18, 38, + 19, 38, 19, 39, 21, 39, 23, 38, 25, 39, 27, 38, 27, 39, 28, 38, 29, 38, 29, 39, + 4, 40, 4, 41, 6, 40, 9, 41, 13, 41, 15, 41, 19, 41, 23, 41, 25, 41, 29, 41, + 31, 41, 33, 41, 35, 41, 36, 41, 37, 41, 40, 36, 39, 41, 4, 42, 4, 43, 5, 42, + 6, 43, 7, 42, 17, 43, 27, 43, 30, 43, 31, 42, 31, 43, 32, 43, 33, 42, 42, 32, + 36, 43, 37, 42, 37, 43, 42, 36, 39, 42, 39, 43, 42, 38, 7, 45, 9, 45, 11, 45, + 21, 45, 31, 45, 44, 32, 36, 44, 36, 45, 39, 45, 44, 38, 4, 46, 4, 47, 5, 46, + 6, 47, 7, 46, 46, 6, 13, 47, 19, 47, 23, 47, 31, 46, 46, 30, 32, 47, 33, 47, + 34, 47, 35, 47, 36, 46, 36, 47, 37, 46, 37, 47, 46, 36, 38, 47, 39, 47, 4, 49, + 48, 4, 6, 49, 48, 6, 15, 48, 19, 48, 19, 49, 25, 48, 28, 49, 29, 48, 29, 49, + 31, 49, 33, 48, 35, 48, 36, 49, 39, 48, 48, 38, 2, 50, 2, 51, 6, 50, 7, 51, + 9, 51, 13, 51, 19, 51, 21, 51, 23, 51, 25, 51, 26, 51, 50, 26, 31, 51, 35, 51, + 39, 51, 47, 51, 48, 51, 2, 53, 3, 52, 6, 52, 6, 53, 17, 53, 22, 52, 23, 52, + 23, 53, 26, 53, 27, 53, 37, 53, 45, 53, 3, 55, 7, 55, 9, 55, 11, 55, 21, 55, + 22, 55, 23, 55, 26, 54, 26, 55, 31, 55, 43, 55, 53, 55, 2, 56, 3, 56, 6, 57, + 7, 56, 56, 6, 11, 57, 15, 57, 19, 57, 21, 56, 56, 20, 23, 57, 24, 57, 25, 56, + 25, 57, 26, 56, 27, 56, 27, 57, 56, 26, 33, 57, 41, 57, 2, 59, 3, 59, 58, 2, + 6, 59, 7, 58, 7, 59, 58, 6, 13, 59, 17, 59, 19, 59, 58, 20, 25, 59, 26, 59, + 27, 59, 58, 28, 35, 58, 58, 34, 38, 58, 38, 59, 39, 58, 43, 59, 47, 59, 2, 60, + 4, 60, 4, 61, 5, 61, 60, 4, 9, 61, 10, 61, 11, 61, 13, 61, 15, 61, 16, 61, + 17, 61, 18, 61, 19, 61, 23, 61, 27, 61, 33, 61, 39, 61, 43, 61, 47, 61, 48, 61, + 60, 52, 57, 61, 58, 61, 2, 63, 4, 62, 4, 63, 12, 63, 13, 62, 17, 63, 19, 63, + 27, 63, 37, 63, 45, 63, 55, 63, 3, 65, 5, 65, 9, 65, 11, 65, 16, 64, 16, 65, + 18, 65, 21, 65, 31, 65, 43, 65, 53, 65, 57, 65, 63, 65, 2, 66, 2, 67, 3, 66, + 3, 67, 66, 2, 4, 67, 5, 66, 66, 4, 10, 67, 11, 66, 66, 10, 13, 67, 14, 67, + 15, 67, 16, 66, 17, 66, 17, 67, 66, 16, 18, 66, 19, 66, 19, 67, 66, 18, 25, 67, + 35, 67, 41, 67, 51, 67, 57, 67, 3, 69, 68, 2, 4, 68, 4, 69, 5, 68, 68, 4, + 11, 69, 16, 69, 17, 68, 17, 69, 68, 16, 18, 68, 18, 69, 68, 18, 23, 69, 27, 69, + 68, 32, 37, 69, 39, 68, 43, 69, 47, 69, 57, 69, 58, 68, 70, 68, 9, 73, 37, 73, + 41, 73, 45, 73, 51, 73, 55, 73, 61, 73, 65, 73, 74, 2, 74, 4, 74, 16, 74, 18, + 33, 75, 41, 75, 74, 46, 48, 75, 51, 75, 58, 75, 67, 75, 8, 77, 9, 77, 76, 8, + 17, 77, 35, 77, 51, 77, 61, 77, 68, 77, 69, 77, 76, 68, 78, 2, 8, 79, 9, 78, + 9, 79, 78, 8, 17, 79, 78, 16, 31, 79, 63, 79, 9, 80, 17, 80, 48, 81, 51, 81, + 6, 83, 7, 83, 17, 83, 68, 83, 75, 83, 7, 85, 21, 85, 6, 87, 17, 87, 27, 87, + 37, 87, 61, 87, 65, 87, 67, 87, 69, 87, 6, 89, 7, 88, 88, 6, 23, 89, 88, 22, + 25, 88, 33, 89, 61, 89, 90, 74, 9, 93, 15, 93, 9, 94, 9, 95, 15, 95, 94, 14, + 23, 95, 9, 97, 25, 97, 31, 97, 51, 97, 9, 99, 98, 14, 21, 99, 35, 99, 45, 98, + 47, 99, 98, 46, 69, 99, 7, 103, 33, 103, 35, 103, 37, 103, 39, 103, 51, 103, 61, 103, + 6, 105, 21, 104, 27, 104, 27, 105, 104, 26, 31, 104, 37, 105, 104, 36, 55, 105, 65, 105, + 69, 105, 108, 68, 112, 4, 13, 113, 33, 113, 58, 112, 61, 115, 97, 115, 103, 115, 116, 4, + 8, 117, 9, 117, 57, 117, 116, 56, 63, 117, 13, 118, 13, 119, 55, 119, 61, 121, 98, 121, + 122, 2, 23, 125, 61, 125, 6, 127, 126, 6, 21, 126, 27, 127, 37, 126, 37, 127, 126, 36, + 65, 127, 77, 127, 126, 78, 128, 20, 77, 131, 9, 133, 11, 133, 132, 10, 79, 133, 88, 133, + 8, 135, 9, 134, 9, 135, 134, 8, 11, 135, 43, 135, 134, 42, 53, 135, 61, 135, 63, 135, + 73, 135, 87, 135, 134, 86, 134, 88, 136, 4, 136, 6, 9, 136, 9, 137, 136, 8, 47, 137, + 53, 136, 55, 136, 57, 137, 61, 137, 63, 137, 69, 137, 136, 68, 75, 137, 89, 137, 105, 137, + 127, 137, 9, 139, 138, 8, 11, 138, 11, 139, 88, 139, 21, 141, 31, 141, 43, 141, 63, 141, + 73, 141, 133, 141, 7, 143, 25, 143, 27, 143, 35, 143, 39, 143, 47, 143, 67, 143, 75, 143, + 78, 143, 79, 143, 95, 143, 97, 143, 101, 143, 131, 143, 6, 145, 7, 144, 7, 145, 144, 6, + 73, 145, 144, 78, 143, 145, 146, 4, 7, 146, 146, 6, 27, 146, 27, 147, 69, 147, 135, 147, + 7, 148, 148, 6, 25, 149, 61, 149, 81, 149, 101, 149, 131, 149, 150, 90, 9, 153, 143, 153, + 9, 154, 154, 62, 61, 157, 68, 157, 156, 68, 9, 159, 158, 8, 61, 158, 68, 158, 83, 158, + 143, 159, 149, 159, 68, 161, 151, 161, 158, 161, 7, 162, 21, 164, 28, 164, 31, 164, 38, 164, + 164, 134, 166, 6, 68, 167, 166, 72, 158, 167, 9, 169, 168, 166, 170, 48, 174, 58, 158, 177, + 15, 178, 19, 178, 180, 14, 9, 186, 188, 8, 9, 192, 192, 68, 68, 195, 194, 68, 104, 197, + 202, 78, 9, 211, 210, 8, 216, 58, 41, 226, 48, 226, 143, 229, 149, 229, 41, 230, 51, 230, + 61, 231, 178, 235, 9, 236, 236, 42, 236, 86, 236, 164, 238, 58, 178, 241, 19, 243, 159, 243, + 186, 243, 192, 243, 211, 243, 213, 243, 229, 243, 5, 244, 19, 249, 149, 249, 159, 249, 186, 249, + 192, 249, 211, 249, 143, 257, 149, 257, 243, 257, 249, 257, 68, 261, 260, 180, 9, 263, 262, 8, + 61, 262, 68, 262, 83, 262, 143, 263, 149, 263, 177, 262, 243, 263, 249, 263, 19, 264, 264, 36, + 38, 264, 268, 4, 143, 271, 149, 271, 272, 58, 9, 280, 9, 283, 41, 282, 48, 282, 51, 282, + 58, 282, 61, 282, 68, 282, 282, 166, 9, 285, 286, 58, 290, 68, 292, 58, 158, 295, 262, 295, + 296, 38, 19, 300, 300, 36, 97, 300, 300, 134, 199, 300, 223, 300, 300, 236, 252, 300, 302, 36, + 302, 222, 246, 304, 9, 308, 308, 68, 243, 308, 249, 308, 310, 58, 104, 312, 314, 58, 68, 317, + 316, 68, 126, 319, 320, 68, 322, 8, 326, 6, 326, 210, 330, 204, 246, 331, 330, 248, 332, 48, + 126, 335, 334, 128, 143, 339, 149, 339, 41, 341, 48, 341, 346, 8, 348, 58, 350, 4, 350, 252, + 178, 353, 352, 180, 41, 354, 41, 356, 51, 356, 61, 357, 41, 359, 48, 359, 104, 361, 360, 106, + 362, 106, 364, 8, 300, 367, 300, 369, 9, 370, 370, 42, 370, 86, 370, 300, 41, 373, 48, 373, + 372, 48, 41, 374, 48, 374, 300, 375, 376, 104, 376, 178, 376, 264, 376, 300, 19, 379, 21, 379, + 31, 379, 48, 379, 101, 379, 103, 379, 159, 379, 182, 379, 185, 379, 192, 379, 207, 379, 263, 379, + 267, 379, 271, 379, 277, 379, 301, 379, 308, 379, 339, 379, 369, 379, 374, 379, 3, 380, 379, 381, + 186, 382, 211, 382, 19, 385, 31, 385, 48, 385, 93, 385, 101, 385, 103, 385, 153, 385, 159, 385, + 173, 385, 185, 385, 192, 385, 207, 385, 384, 210, 384, 254, 263, 385, 267, 385, 271, 385, 279, 385, + 308, 385, 339, 385, 343, 385, 374, 385, 41, 387, 178, 388, 388, 220, 390, 248, 243, 393, 126, 396, + 398, 128, 400, 8, 400, 148, 379, 403, 385, 403, 404, 302, 379, 405, 385, 405, 143, 407, 149, 407, + 243, 407, 249, 407, 9, 409, 408, 8, 61, 408, 68, 408, 83, 408, 143, 409, 149, 409, 177, 408, + 243, 409, 249, 409, 295, 408, 379, 409, 385, 409, 19, 414, 28, 414, 414, 134, 414, 236, 414, 370, + 413, 415, 418, 2, 143, 418, 243, 418, 422, 48, 51, 425, 41, 426, 48, 426, 9, 429, 379, 429, + 411, 429, 418, 429, 385, 431, 9, 432, 9, 435, 41, 434, 48, 434, 51, 434, 58, 434, 61, 434, + 68, 434, 436, 48, 41, 440, 48, 440, 51, 443, 9, 445, 411, 445, 418, 445, 9, 446, 385, 449, + 9, 451, 41, 450, 48, 450, 51, 450, 58, 450, 61, 450, 68, 450, 68, 453, 158, 453, 262, 453, + 408, 453, 454, 270, 454, 338, 454, 344, 158, 457, 262, 457, 408, 457, 458, 134, 458, 154, 458, 236, + 458, 370, 334, 460, 373, 460, 397, 460, 35, 462, 39, 462, 239, 462, 313, 462, 360, 462, 25, 464, + 29, 464, 35, 464, 39, 464, 464, 78, 354, 466, 387, 466, 19, 468, 39, 468, 307, 468, 328, 468, + 385, 468, 19, 470, 39, 470, 379, 472, 385, 472, 75, 474, 224, 474, 251, 474, 15, 476, 19, 476, + 171, 476, 191, 476, 208, 476, 478, 218, 478, 300, 15, 484, 19, 484, 25, 484, 29, 484, 486, 134, + 486, 236, 486, 370, 224, 489, 239, 489, 274, 489, 313, 489, 360, 489, 379, 491, 385, 491, 307, 493, + 328, 493, 35, 497, 39, 497, 39, 499, 385, 499, 379, 503, 385, 503, 61, 511, 512, 298, 512, 414, + 514, 416, 13, 517, 287, 517, 367, 517, 518, 298, 379, 521, 51, 525, 61, 525, 385, 525, 528, 134, + 528, 154, 528, 236, 528, 370, 517, 535, 517, 541, 379, 543, 501, 545, 7, 550, 51, 553, 462, 553, + 464, 553, 497, 553, 507, 553, 549, 553, 5, 554, 51, 557, 385, 557, 462, 557, 464, 557, 497, 557, + 564, 478, 385, 567, 462, 573, 497, 573, 578, 300, 580, 300, 472, 584, 51, 587, 61, 587, 143, 588, + 243, 588, 68, 591, 545, 591, 592, 6, 68, 593, 545, 595, 596, 4, 39, 598, 600, 154, 151, 603, + 39, 604, 537, 611, 379, 613, 75, 614, 171, 616, 379, 619, 15, 620, 25, 620, 51, 620, 61, 620, + 61, 623, 259, 623, 395, 623, 561, 623, 68, 625, 158, 625, 262, 625, 408, 625, 626, 420, 626, 438, + 158, 629, 262, 629, 408, 629, 630, 46, 630, 56, 630, 162, 632, 134, 632, 154, 632, 236, 632, 370, + 636, 78, 553, 636, 557, 636, 574, 640, 535, 642, 243, 648, 67, 650, 77, 650, 195, 650, 317, 650, + 533, 650, 562, 650, 650, 608, 652, 42, 48, 652, 103, 652, 201, 652, 491, 652, 495, 652, 313, 654, + 360, 654, 39, 656, 334, 659, 373, 659, 397, 659, 662, 512, 33, 665, 191, 665, 208, 665, 569, 665, + 614, 665, 670, 36, 670, 218, 15, 677, 171, 677, 570, 677, 67, 679, 77, 679, 195, 679, 317, 679, + 533, 679, 562, 679, 669, 679, 334, 681, 397, 681, 243, 683, 484, 683, 665, 685, 686, 134, 686, 154, + 686, 236, 686, 370, 476, 691, 677, 691, 694, 522, 61, 701, 468, 701, 499, 701, 700, 606, 313, 703, + 360, 703, 704, 6, 379, 707, 385, 707, 652, 707, 7, 708, 51, 711, 143, 711, 464, 711, 636, 711, + 3, 712, 51, 715, 143, 715, 149, 715, 464, 715, 636, 715, 623, 721, 722, 644, 379, 725, 517, 726, + 143, 729, 379, 731, 474, 732, 736, 630, 584, 739, 583, 740, 638, 740, 61, 742, 468, 742, 499, 742, + 334, 744, 397, 744, 249, 749, 750, 6, 68, 751, 754, 46, 753, 755, 468, 757, 499, 757, 557, 757, + 758, 2, 39, 763, 151, 765, 61, 766, 61, 769, 243, 771, 39, 773, 61, 775, 259, 775, 395, 775, + 561, 775, 721, 775, 776, 508, 776, 564, 647, 781, 782, 532, 784, 468, 39, 789, 557, 789, 158, 791, + 262, 791, 408, 791, 158, 793, 262, 793, 408, 793, 796, 66, 796, 178, 796, 264, 798, 504, 800, 134, + 800, 154, 800, 236, 800, 370, 802, 68, 804, 66, 135, 804, 237, 804, 371, 804, 804, 416, 577, 804, + 623, 804, 735, 804, 775, 804, 570, 806, 691, 806, 808, 98, 535, 808, 808, 684, 726, 808, 810, 66, + 814, 66, 689, 818, 718, 818, 820, 568, 822, 42, 57, 822, 822, 502, 531, 822, 558, 822, 822, 674, + 737, 822, 824, 42, 48, 824, 103, 824, 201, 824, 491, 824, 495, 824, 707, 824, 826, 32, 307, 828, + 328, 828, 39, 830, 832, 570, 33, 834, 191, 834, 208, 834, 836, 508, 354, 839, 387, 839, 840, 512, + 35, 843, 224, 843, 251, 843, 616, 843, 642, 845, 846, 126, 75, 849, 574, 849, 732, 849, 151, 851, + 689, 853, 718, 853, 57, 855, 531, 855, 558, 855, 737, 855, 354, 857, 387, 857, 35, 859, 224, 859, + 251, 859, 143, 861, 484, 861, 843, 863, 864, 134, 864, 154, 864, 236, 864, 370, 474, 867, 640, 867, + 849, 867, 143, 871, 483, 873, 647, 873, 851, 873, 634, 875, 667, 875, 51, 877, 462, 877, 497, 877, + 307, 879, 328, 879, 33, 881, 191, 881, 208, 881, 882, 570, 884, 4, 652, 887, 824, 887, 808, 889, + 816, 889, 5, 890, 892, 480, 808, 895, 816, 895, 61, 897, 243, 897, 816, 897, 3, 898, 61, 901, + 243, 901, 468, 901, 499, 901, 816, 901, 904, 810, 904, 814, 61, 907, 517, 908, 642, 908, 243, 911, + 476, 912, 677, 912, 804, 915, 650, 917, 679, 917, 916, 796, 916, 810, 61, 919, 249, 919, 557, 919, + 584, 919, 740, 919, 61, 920, 583, 920, 739, 920, 804, 920, 804, 921, 924, 814, 61, 927, 61, 928, + 634, 930, 667, 930, 51, 932, 462, 932, 497, 932, 354, 934, 387, 934, 35, 936, 224, 936, 251, 936, + 938, 582, 149, 941, 919, 941, 942, 4, 947, 949, 950, 2, 763, 953, 769, 953, 789, 953, 919, 953, + 143, 955, 39, 959, 161, 965, 157, 969, 195, 969, 763, 969, 769, 969, 789, 969, 957, 969, 39, 971, + 591, 971, 157, 975, 157, 979, 763, 979, 769, 979, 789, 979, 957, 979, 39, 981, 591, 981, 157, 983, + 988, 78, 135, 988, 237, 988, 371, 988, 407, 988, 988, 538, 988, 696, 988, 868, 41, 991, 48, 991, + 379, 992, 379, 995, 243, 996, 243, 999, 143, 1000, 143, 1003, 1004, 488, 1006, 658, 83, 1009, 143, 1011, + 243, 1011, 379, 1011, 41, 1012, 48, 1012, 33, 1014, 685, 1014, 385, 1017, 789, 1017, 33, 1018, 527, 1020, + 1022, 46, 143, 1024, 1026, 6, 1028, 962, 1030, 132, 9, 1033, 1032, 8, 243, 1033, 249, 1033, 379, 1033, + 385, 1033, 51, 1034, 35, 1036, 645, 1038, 957, 1043, 143, 1046, 379, 1046, 67, 1049, 283, 1049, 435, 1049, + 451, 1049, 1050, 810, 133, 1052, 1052, 810, 73, 1055, 1054, 132, 763, 1055, 769, 1055, 789, 1055, 919, 1055, + 9, 1057, 1056, 8, 9, 1058, 143, 1059, 51, 1061, 645, 1063, 1064, 154, 161, 1067, 1068, 796, 1070, 178, + 1072, 18, 1072, 716, 1074, 88, 1074, 126, 1076, 74, 1080, 16, 61, 1083, 1084, 812, 137, 1087, 553, 1089, + 557, 1089, 711, 1089, 715, 1089, 33, 1091, 685, 1091, 1090, 854, 23, 1093, 527, 1093, 1092, 852, 1094, 156, + 137, 1097, 1098, 582, 957, 1101, 143, 1103, 761, 1103, 787, 1103, 1104, 810, 83, 1107, 149, 1107, 919, 1107, + 379, 1108, 1110, 794, 379, 1113, 243, 1114, 1114, 672, 137, 1116, 1120, 36, 243, 1122, 1124, 126, 1126, 124, + 1126, 546, 1126, 660, 1128, 58, 9, 1131, 1132, 58, 31, 1134, 143, 1136, 379, 1136, 9, 1138, 493, 1140, + 1142, 796, 61, 1145, 143, 1147, 379, 1147, 9, 1149, 61, 1151, 489, 1153, 489, 1154, 897, 1154, 945, 1157, + 1158, 794, 665, 1160, 843, 1162, 1164, 8, 51, 1166, 61, 1166, 39, 1168, 39, 1170, 507, 1170, 462, 1173, + 497, 1173, 1174, 796, 31, 1176, 197, 1179, 197, 1180, 897, 1180, 27, 1182, 381, 1185, 39, 1189, 39, 1191, + 507, 1191, 945, 1193, 41, 1197, 48, 1197, 1198, 48, 1200, 48, 761, 1203, 787, 1203, 747, 1204, 1206, 8, + 379, 1208, 51, 1210, 1212, 336, 1214, 154, 659, 1217, 659, 1218, 889, 1218, 747, 1221, 9, 1223, 61, 1225, + 61, 1227, 1228, 98, 61, 1231, 39, 1232, 1234, 324, 693, 1236, 143, 1239, 157, 1239, 77, 1241, 83, 1241, + 195, 1241, 317, 1241, 453, 1241, 625, 1241, 37, 1242, 51, 1245, 121, 1245, 21, 1246, 319, 1249, 319, 1250, + 889, 1250, 693, 1253, 1254, 6, 9, 1261, 1260, 8, 11, 1262, 79, 1262, 1262, 134, 9, 1264, 161, 1267, + 9, 1268, 1270, 134, 31, 1273, 47, 1273, 73, 1273, 111, 1273, 133, 1273, 145, 1273, 169, 1273, 211, 1273, + 215, 1273, 285, 1273, 289, 1273, 565, 1273, 915, 1273, 804, 1275, 73, 1276, 169, 1276, 285, 1276, 31, 1279, + 41, 1279, 51, 1279, 61, 1279, 133, 1279, 143, 1279, 211, 1279, 61, 1280, 301, 1283, 179, 1287, 11, 1288, + 903, 1288, 89, 1291, 9, 1292, 67, 1295, 133, 1298, 9, 1301, 1302, 8, 650, 1305, 679, 1305, 1304, 796, + 1306, 814, 1308, 6, 261, 1309, 591, 1309, 665, 1310, 493, 1313, 665, 1315, 493, 1316, 89, 1318, 11, 1321, + 903, 1321, 11, 1323, 9, 1327, 61, 1328, 261, 1331, 405, 1333, 11, 1334, 83, 1339, 9, 1340, 143, 1341, + 665, 1345, 493, 1347, 39, 1349, 75, 1351, 61, 1353, 1354, 802, 1356, 56, 1356, 144, 97, 1358, 199, 1358, + 301, 1358, 367, 1358, 381, 1358, 713, 1358, 899, 1358, 1360, 16, 179, 1362, 47, 1364, 39, 1366, 75, 1368, + 67, 1370, 1372, 144, 1374, 16, 25, 1378, 245, 1382, 555, 1382, 891, 1382, 143, 1385, 497, 1385, 804, 1387, + 1388, 816, 143, 1391, 497, 1391, 379, 1392, 143, 1394, 1396, 78, 39, 1399, 650, 1401, 679, 1401, 1400, 796, + 39, 1403, 1404, 796, 233, 1407, 699, 1407, 11, 1409, 245, 1411, 555, 1411, 891, 1411, 61, 1413}; + // clang-format off +}; // namespace mockturtle + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp new file mode 100644 index 00000000000..c14b7807689 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp @@ -0,0 +1,347 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xmg3_npn.hpp + \brief Replace with size-optimum xmg3s and AIGs from NPN (from ABC rewrite) + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../algorithms/simulation.hpp" +#include "../../io/write_bench.hpp" +#include "../../networks/xmg.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +struct xmg3_npn_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct xmg3_npn_resynthesis_stats +{ + stopwatch<>::duration time_classes{ 0 }; + stopwatch<>::duration time_db{ 0 }; + + uint32_t db_size; + uint32_t covered_classes; + + void report() const + { + std::cout << fmt::format( "[i] build classes time = {:>5.2f} secs\n", to_seconds( time_classes ) ); + std::cout << fmt::format( "[i] build db time = {:>5.2f} secs\n", to_seconds( time_db ) ); + } +}; + +/*! \brief Resynthesis function based on pre-computed AIGs. + * + * This resynthesis function can be passed to ``cut_rewriting``. It will + * produce a network based on pre-computed xmg3s with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const aig_network aig = ...; + xmg3_npn_resynthesis resyn; + aig = cut_rewriting( aig, resyn ); + + .. note:: + + The implementation of this algorithm was heavily inspired by the rewrite + command in AIG. It uses the same underlying database of subcircuits. + \endverbatim + */ +template +class xmg3_npn_resynthesis +{ +public: + xmg3_npn_resynthesis( xmg3_npn_resynthesis_params const& ps = {}, xmg3_npn_resynthesis_stats* pst = nullptr ) + : ps( ps ), + pst( pst ), + _classes( 1 << 16 ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + static_assert( is_network_type_v, "DatabaseNtk is not a network type" ); + static_assert( has_get_node_v, "DatabaseNtk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "DatabaseNtk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "DatabaseNtk does not implement the is_xor method" ); + static_assert( has_size_v, "DatabaseNtk does not implement the size method" ); + static_assert( has_create_pi_v, "DatabaseNtk does not implement the create_pi method" ); + static_assert( has_create_and_v, "DatabaseNtk does not implement the create_and method" ); + static_assert( has_create_xor_v, "DatabaseNtk does not implement the create_xor method" ); + static_assert( has_foreach_fanin_v, "DatabaseNtk does not implement the foreach_fanin method" ); + static_assert( has_foreach_node_v, "DatabaseNtk does not implement the foreach_node method" ); + static_assert( has_make_signal_v, "DatabaseNtk does not implement the make_signal method" ); + + _repr.reserve( 222u ); + build_classes(); + build_db(); + } + + virtual ~xmg3_npn_resynthesis() + { + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + kitty::static_truth_table<4u> tt = kitty::extend_to<4u>( function ); + + /* get representative of function */ + const auto repr = _repr[_classes[*tt.cbegin()]]; + + /* check if representative has circuits */ + const auto it = _repr_to_signal.find( repr ); + if ( it == _repr_to_signal.end() ) + { + return; + } + + const auto config = kitty::exact_npn_canonization( tt ); + + assert( repr == std::get<0>( config ) ); + + std::vector> pis( 4, ntk.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::vector> pis_perm; + auto perm = std::get<2>( config ); + for ( auto i = 0; i < 4; ++i ) + { + pis_perm.push_back( pis[perm[i]] ); + } + + const auto& phase = std::get<1>( config ); + for ( auto i = 0; i < 4; ++i ) + { + if ( ( phase >> perm[i] ) & 1 ) + { + pis_perm[i] = ntk.create_not( pis_perm[i] ); + } + } + + for ( auto const& cand : it->second ) + { + std::unordered_map, signal> db_to_ntk; + + db_to_ntk.insert( { 0, ntk.get_constant( false ) } ); + for ( auto i = 0; i < 4; ++i ) + { + db_to_ntk.insert( { i + 1, pis_perm[i] } ); + } + auto f = copy_db_entry( ntk, _db.get_node( cand ), db_to_ntk ); + if ( _db.is_complemented( cand ) != ( ( phase >> 4 ) & 1 ) ) + { + f = ntk.create_not( f ); + } + if ( !fn( f ) ) + { + return; + } + } + } + +private: + signal + copy_db_entry( Ntk& ntk, node const& n, std::unordered_map, signal>& db_to_ntk ) const + { + if ( const auto it = db_to_ntk.find( n ); it != db_to_ntk.end() ) + { + return it->second; + } + + std::vector> fanin; + // std::array, 2> fanin; + _db.foreach_fanin( n, [&]( auto const& f ) { + auto ntk_f = copy_db_entry( ntk, _db.get_node( f ), db_to_ntk ); + if ( _db.is_complemented( f ) ) + { + ntk_f = ntk.create_not( ntk_f ); + } + fanin.push_back( ntk_f ); + } ); + + const auto f = _db.is_xor3( n ) ? ntk.create_xor3( fanin[0], fanin[1], fanin[2] ) : ntk.create_maj( fanin[0], fanin[1], fanin[2] ); + db_to_ntk.insert( { n, f } ); + return f; + } + + void build_classes() + { + stopwatch t( st.time_classes ); + + kitty::dynamic_truth_table map( 16u ); + std::transform( map.cbegin(), map.cend(), map.begin(), []( auto word ) { return ~word; } ); + + int64_t index = 0; + kitty::static_truth_table<4u> tt; + while ( index != -1 ) + { + kitty::create_from_words( tt, &index, &index + 1 ); + const auto res = kitty::exact_npn_canonization( tt, [&]( const auto& tt ) { + _classes[*tt.cbegin()] = _repr.size(); + kitty::clear_bit( map, *tt.cbegin() ); + } ); + _repr.push_back( std::get<0>( res ) ); + + /* find next non-classified truth table */ + index = find_first_one_bit( map ); + } + } + + void build_db() + { + stopwatch t( st.time_db ); + + _db.get_constant( false ); + /* four primary inputs */ + _db.create_pi(); + _db.create_pi(); + _db.create_pi(); + _db.create_pi(); + + auto* p = subgraphs; + while ( true ) + { + auto entry0 = *p++; + auto entry1 = *p++; + auto entry2 = *p++; + + if ( entry0 == 0 && entry1 == 0 && entry2 == 0 ) + break; + + auto is_xor = entry0 & 1; + entry0 >>= 1; + + const auto child0 = _db.make_signal( entry0 >> 1 ) ^ ( entry0 & 1 ); + const auto child1 = _db.make_signal( entry1 >> 1 ) ^ ( entry1 & 1 ); + const auto child2 = _db.make_signal( entry2 >> 1 ) ^ ( entry2 & 1 ); + + if ( is_xor ) + { + _db.create_xor3( child0, child1, child2 ); + } + else + { + _db.create_maj( child0, child1, child2 ); + } + } + + const auto sim_res = simulate_nodes>( _db ); + + _db.foreach_node( [&]( auto n ) { + if ( _repr[_classes[*sim_res[n].cbegin()]] == sim_res[n] ) + { + if ( _repr_to_signal.count( sim_res[n] ) == 0 ) + { + _repr_to_signal.insert( { sim_res[n], { _db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[sim_res[n]].push_back( _db.make_signal( n ) ); + } + } + else + { + const auto f = ~sim_res[n]; + if ( _repr[_classes[*f.cbegin()]] == f ) + { + if ( _repr_to_signal.count( f ) == 0 ) + { + _repr_to_signal.insert( { f, { !_db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[f].push_back( !_db.make_signal( n ) ); + } + } + } + } ); + + st.db_size = _db.size(); + st.covered_classes = static_cast( _repr_to_signal.size() ); + } + + xmg3_npn_resynthesis_params ps; + xmg3_npn_resynthesis_stats st; + xmg3_npn_resynthesis_stats* pst{ nullptr }; + + std::vector> _repr; + std::vector _classes; + std::unordered_map, std::vector>, kitty::hash>> _repr_to_signal; + + DatabaseNtk _db; + + // clang-format off + inline static const uint16_t subgraphs[] + { + 0x2,0x2,0x4,0xc,0x8,0xb,0x2,0xa,0xc,0x8,0x7,0x8,0x2,0x6,0x10,0x2,0x6,0x8,0x29,0x4,0x2,0x0,0x15,0x16,0x0,0x3,0x4,0x8,0x14,0x1b,0x8,0x7,0x1a,0x3d,0x6,0x2,0x0,0x9,0x20,0x4,0x4,0x6,0x2,0x8,0x24,0x49,0x6,0x0,0x0,0x9,0x28,0xa,0x6,0xa,0x0,0x2,0x2d,0x10,0xa,0x2f,0x6,0x4,0x8,0x4,0x6,0x8,0x2,0x32,0x34,0x15,0x6,0x0,0x0,0x9,0x38,0x4,0x4,0x9,0x0,0x7,0x3c,0x10,0x3c,0x3f,0x11,0x6,0x4,0x0,0x9,0x42,0x0,0x6,0x9,0x8d,0x4,0x0,0x10,0xa,0x49,0x0,0x4,0x7,0x8,0x8,0x4d,0xd,0x4,0x2,0x2,0x8,0x50,0x0,0x2,0x15,0x10,0x16,0x55,0x11,0x4,0x2,0x0,0x6,0x59,0x2,0x8,0x5a,0x4,0x4,0x7,0xbd,0x8,0x6,0x0,0x9,0x60,0x4,0x4,0x8,0x0,0x6,0x65,0xc,0x8,0x67,0x4,0x6,0x51,0xd5,0x50,0x8,0x0,0x6b,0x6c,0x2,0x4,0x6,0x8,0x8,0x70,0x4,0x14,0x72,0x6,0x4,0x6,0x4,0x8,0x76,0xed,0x8,0x4,0x0,0x79,0x7a,0x69,0x4,0x2,0x2,0x34,0x7e,0x11,0x6,0x2,0x8,0x6,0x8,0x0,0x82,0x85,0x0,0x2,0x35,0x8,0x34,0x89,0x0,0x5,0x82,0xc,0x8,0x8d,0x8,0x8c,0x8f,0xc,0x8,0xa,0x85,0x6,0x2,0xc,0x8,0x95,0x0,0x42,0x97,0x0,0x2,0x5,0x135,0x6,0x0,0x2,0x84,0x9c,0x0,0x2,0x9,0xc,0x64,0xa1,0x4,0x7,0x42,0x6,0x44,0xa4,0x10,0xa,0x43,0x85,0x4,0x2,0x0,0x7,0xaa,0x12,0x42,0xac,0x0,0x2,0x8,0x2,0x84,0xb0,0x0,0x3,0x6,0x169,0x8,0x2,0x0,0x51,0xb6,0x4,0x6,0x25,0x0,0x9,0x24,0x8,0xba,0xbd,0x109,0x8,0x2,0x0,0x8,0x84,0x0,0xc0,0xc3,0x10,0x50,0x70,0x0,0x7,0x42,0x12,0x58,0xc8,0xc,0x8,0x59,0x0,0x3,0x58,0x10,0xcc,0xcf,0x4,0x5,0x8,0x0,0x4,0xd3,0x4,0x7,0xd4,0x1ad,0x8,0x6,0xc,0x8,0x64,0x8,0x7,0x32,0x1b9,0x4,0x0,0x0,0x4,0x8,0x0,0x2,0xe1,0xa,0x6,0xe2,0x1c9,0x4,0x0,0x0,0x8,0xd2,0x4,0x6,0xe8,0x1d5,0xd2,0x0,0x0,0x2,0x59,0x8,0x6,0xef,0x1e1,0xee,0x0,0x0,0x2,0x4,0xc,0x58,0xf5,0x1ed,0x6,0x0,0x0,0x8,0x64,0x0,0x6,0xfa,0x1f9,0x64,0x6,0x2,0x2,0x6,0xa,0xa0,0x100,0x205,0x4,0x0,0x65,0x4,0x0,0x2,0x8,0x32,0xc,0x107,0x108,0xc9,0x8,0x0,0xc,0x8,0x10d,0x11,0x4,0x0,0x221,0x6,0x2,0x4,0x8,0x113,0x10,0x111,0x114,0x0,0x112,0x117,0xb1,0x6,0x0,0x10,0x100,0x11b,0x8,0x6,0x9b,0x23d,0x32,0x0,0x0,0x9,0x100,0x9,0x2,0x0,0x200,0x123,0x124,0x0,0x5,0x6,0x251,0x8,0x2,0x8,0x8,0x128,0x0,0x12a,0x12d,0x10,0x70,0x124,0x1a5,0x6,0x4,0x1a5,0x2,0x0,0x12,0x132,0x134,0x161,0x4,0x2,0xc,0x8,0x138,0x2,0xb0,0x13a,0x4,0x8,0x70,0x27d,0x8,0x0,0xe1,0x2,0x0,0x2,0x2,0x70,0x10,0x142,0x144,0x0,0x6,0x8,0x291,0x8,0x2,0x8,0x8,0x148,0x0,0x14a,0x14d,0x10,0x70,0x142,0x6,0x6,0x8,0x0,0x8,0x153,0x8,0x6,0x155,0x2ad,0x152,0x0,0x0,0x7,0xd2,0x2b5,0x152,0x0,0x11,0x2,0x0,0x12,0xc8,0x15e,0x0,0x6,0x1b,0x2c5,0x1a,0x2,0xc,0x8,0x164,0xc,0x9,0x32,0x2d1,0x4c,0x32,0x4,0x4,0x4d,0x99,0x6,0x2,0x10,0x16c,0x16e,0xc,0x9,0x128,0x4,0x4,0x173,0x2e9,0x128,0x8,0xa,0x6,0x15e,0xc,0x8,0x179,0x0,0x4,0x9,0x8,0x6,0x15e,0x2fd,0x8,0x0,0x2bc,0x17c,0x180,0xc9,0x50,0x0,0x10,0x50,0x184,0x0,0x3,0x84,0x311,0x8,0x2,0x6,0x6,0xa,0x10,0x38,0x18c,0x15,0x8,0x0,0x0,0x7,0x190,0x14,0x190,0x192,0xc,0x8,0x191,0x0,0x7,0xa,0x331,0x8,0x6,0x8,0x6,0x9,0x0,0x8,0x19d,0x33d,0x6,0x4,0x8,0x6,0x43,0x0,0x2,0x43,0x2,0x1a2,0x1a4,0x2,0x2,0x8,0x351,0x128,0x4,0x0,0x4,0x1a8,0xc,0x1a8,0x1ac,0x0,0x111,0x1a8,0x2,0x6,0x1b0,0x365,0x1a8,0x0,0x8,0x8,0x83,0x36d,0x9a,0x0,0x0,0x3,0x8,0x375,0x6,0x2,0x0,0x5,0x1bc,0x12,0x1bc,0x1be,0x289,0x6,0x2,0x10,0x70,0x1c2,0x8,0x8,0x1a9,0x2,0x6,0x1c6,0x391,0x1a8,0x0,0x0,0x2,0x6,0x10,0x70,0x1cc,0x4,0x6,0x10,0x0,0x9,0x1d0,0x3a5,0x10,0x4,0x141,0x6,0x4,0x2,0xe0,0x1d6,0xa,0x6,0x8,0x3b5,0xa0,0x6,0x8,0x9,0xa0,0x8,0x6,0x1df,0x3c1,0x1de,0xa0,0x399,0x8,0x4,0xe,0x46,0x1e4,0x2,0x6,0xa0,0x10,0x1d6,0x1e8,0x4,0x4,0x85,0x0,0x8,0x1ed,0x3dd,0x1ec,0x84,0x351,0x4,0x0,0xc,0x8,0x1f3,0x10,0x4c,0x65,0x3ed,0x64,0x6,0x86,0xe0,0x1a8,0xa,0x6,0xa0,0x3f9,0x8,0x4,0x21,0x4,0x0,0x4,0x6,0x11,0x20,0x201,0x202,0xd,0x4,0x0,0x2,0xe0,0x206,0x4,0x6,0x9,0x0,0x9,0x20a,0x419,0x84,0x0,0x4,0x4,0x111,0xc,0x8,0x210,0x425,0x210,0x110,0x1c0,0x100,0x206,0xc,0x8,0x111,0x8,0x6,0xa1,0x435,0x8,0x0,0xe,0xa0,0x1da,0x43d,0x8,0x0,0x4,0x7,0x128,0x2,0x8,0x222,0x449,0x128,0x4,0x10,0x24,0x206,0xc,0x64,0x9b,0x455,0x8,0x0,0x6,0x8,0x10,0x2,0xa,0x10,0x461,0x22e,0x4,0x0,0x2,0x207,0x469,0x4,0x2,0x10,0x206,0x236,0x251,0x8,0x4,0x0,0x3,0x23a,0x252,0x23a,0x23c,0x291,0x8,0x4,0x14,0x148,0x241,0x2,0x8,0xd2,0x489,0x70,0x0,0x99,0x8,0x6,0x4,0x4,0x82,0x495,0x2,0x0,0x4,0x4,0x83,0x0,0x5,0x8,0x104,0x24f,0x250,0x4a5,0x4,0x0,0x11,0x6,0x0,0x4ad,0x4,0x2,0xc,0x8,0x258,0x134,0x256,0x25b,0x8,0x7,0x46,0x4bd,0x152,0x0,0x0,0x7,0x8,0x12,0x124,0x262,0x4,0x8,0x47,0xc,0x125,0x266,0x2bd,0x6,0x4,0xc,0x47,0x26a,0x8,0x6,0x15f,0x2,0x6,0x26e,0x10,0x26a,0x270,0xc,0x8,0x125,0x2,0x4,0x8,0x248,0x256,0x277,0x2a5,0x4,0x0,0x8,0x6,0x153,0x10,0x27a,0x27c,0x4a,0x46,0x124,0x0,0x8,0x82,0x4,0x5,0x282,0x509,0x82,0x0,0x4,0x7,0x8,0xd,0x2,0x0,0x6,0x288,0x28a,0x8,0x289,0x28c,0x51d,0x28a,0x8,0x0,0x9,0x84,0x4,0x6,0x293,0x529,0x84,0x0,0x8,0x6,0x110,0x10,0x112,0x298,0x351,0x6,0x0,0x12,0x124,0x29c,0x0,0x59,0x256,0x48,0x47,0x58,0x4,0x6,0x262,0x549,0x262,0x4,0x10,0x263,0x2a6,0x8d,0x4,0x2,0xe,0x46,0x2aa,0x8,0x8,0x1cd,0x55d,0x4,0x2,0xc,0x2ae,0x2b0,0xc,0x8,0x58,0xc,0x8,0x124,0x0,0x276,0x2b6,0x571,0x6,0x0,0x4,0xe0,0x207,0x579,0x8,0x4,0xa0,0x256,0x277,0x0,0x4,0x6,0x10,0x152,0x2c2,0x589,0x2c2,0x4,0x10,0x76,0x124,0x591,0x6,0x0,0xa,0x6,0xe0,0x1c1,0x2,0x0,0x2,0x2cc,0x2ce,0x5a1,0x8,0x6,0xa0,0x9a,0x149,0x8,0x6,0x125,0x249,0x6,0x0,0x10,0x2d6,0x2d9,0x4,0x6,0xe0,0x5b9,0x4,0x2,0x0,0x2dc,0x2de,0x5c1,0x2de,0x8,0x351,0x6,0x2,0x4,0x4,0x2e4,0x5cd,0x8,0x2,0x0,0x6,0x125,0x248,0x256,0x2ea,0x2,0x2,0x256,0x5dd,0x4,0x2,0x10,0x257,0x2ee,0x4ae,0x2f0,0x2f2,0x12,0x256,0x278,0x12,0x124,0x2d8,0x0,0x6,0x124,0x5f5,0x124,0x8,0x6a,0x124,0x256,0x4,0x4,0x148,0x0,0x149,0x300,0x605,0x8,0x6,0x68,0x7e,0x257,0x0,0x2,0x1db,0x611,0x8,0x6,0x4,0x4,0x257,0x12,0x256,0x30c,0x61d,0x256,0x0,0x61a,0x30e,0x310,0x515,0x8,0x2,0x0,0x5,0x314,0x516,0x314,0x316,0x64,0x100,0x257,0x6,0x6,0x32,0x0,0x8,0x31d,0x63d,0x32,0x6,0x4,0x6,0x33,0x645,0xb0,0x6,0x611,0x276,0x6,0xa,0x6,0x1a,0x0,0x8,0x1b,0x655,0x328,0x4,0xc,0x8,0x3c,0x65d,0x8,0x0,0xe,0x8,0x3c,0x0,0x6,0x333,0x669,0x3c,0x0,0x0,0x4,0x3c,0xe,0x8,0x338,0x675,0x3c,0x0,0x79,0x6,0x0,0x10,0x19c,0x33e,0xc,0xa,0x58,0x685,0x8,0x0,0x4,0x6,0x9b,0x8,0x9,0x9a,0x691,0x346,0x0,0x8,0x6,0x1b,0xc,0x256,0x34d,0x8,0x8,0x257,0x4,0x257,0x350,0x140,0xf5,0x256,0x0,0x6,0xf5,0x6ad,0xf4,0x8,0x4,0x5,0x6,0x8,0x43,0x35a,0x6b9,0x2,0x0,0x0,0x2,0x7,0x4,0x9,0x360,0x4,0x5,0x362,0x6c9,0x42,0x0,0x0,0x8,0x19c,0x6d1,0x4,0x2,0x0,0x369,0x36a,0x6d9,0x19c,0x6,0x85,0x2,0x0,0xc,0x8,0x370,0x84,0x370,0x372,0xc,0x9,0xf4,0x6ed,0x28a,0x4,0x0,0x9,0xa,0xc,0xf4,0x37b,0x6f9,0xa,0x0,0x65,0x6,0x4,0x201,0x8,0x4,0x2,0x380,0x382,0x6,0x8,0x2c2,0x70d,0x6,0x4,0x1a5,0x6,0x2,0x1a5,0x4,0x0,0x0,0x38b,0x38c,0x0,0x3,0x32,0x64,0x380,0x391,0x0,0x6,0x3d,0x729,0x2,0x0,0x72d,0x8,0x4,0xc,0x8,0x71,0x4,0x4,0x39a,0x739,0x70,0x8,0x2,0x4,0x32,0x65,0x6,0x0,0x4,0x3a1,0x3a2,0x749,0x6,0x4,0x0,0x8,0x3c,0x0,0x6,0x3a9,0x755,0x4,0x2,0x8,0xd2,0x38a,0x639,0x262,0x4,0x6,0x8,0x262,0x8,0x9,0x3b2,0x769,0x262,0x2,0x4,0x8,0x262,0xa,0x6,0x3b8,0x775,0x262,0x2,0x0,0x2,0x58,0xc,0x8,0x3be,0x781,0x3be,0x58,0x515,0x4,0x0,0x12,0x28a,0x3c4,0xe,0x3c4,0x3c6,0x0,0x4,0x1bb,0x0,0x6,0x3cb,0x799,0x58,0x0,0x8,0x8,0x71,0xc,0x15f,0x3d0,0x7a5,0x70,0x0,0x2,0x6,0xf4,0xa,0x8,0xf4,0x7b1,0x3d6,0x2,0x585,0x8,0x2,0x0,0x2,0x3dd,0x7bd,0x6,0x4,0xa,0x6,0xb4,0x7c5,0x58,0x0,0x0,0x4,0x207,0x6,0x8,0x206,0x7d1,0x3e6,0x2,0xe,0x110,0x15e,0x4,0x7,0x15e,0x7dd,0x6,0x4,0x6,0x3ee,0x3f0,0x8,0x8,0x15,0x4,0x6,0x3f4,0x7e9,0x14,0x0,0x7f1,0x3f6,0x4,0xa,0x6,0x1ba,0x7f9,0xe0,0x2,0x1c1,0x6,0x4,0x4,0x4,0x400,0x805,0x8,0x0,0x49,0x8,0x0,0x49,0x8,0x6,0xc,0x14,0xf5,0x815,0xf4,0x2,0x819,0x8,0x4,0x8,0x6,0xb5,0x4,0x8,0xb4,0x825,0x410,0x6,0x0,0x4,0x1cd,0x82d,0x8,0x2,0x4,0x8,0x42,0x835,0x6,0x0,0x0,0x2,0x206,0x83d,0x8,0x4,0x15,0x8,0x6,0x515,0x8,0x4,0x0,0x0,0x0 + + }; + // clang-format on +}; // namespace mockturtle + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg_npn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg_npn.hpp new file mode 100644 index 00000000000..28a5284bf9a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/xmg_npn.hpp @@ -0,0 +1,292 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xmg_npn.hpp + \brief Replace with size-optimum XMGs from NPN + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee + \author Zhufei Chu +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../../algorithms/cleanup.hpp" +#include "../../io/write_bench.hpp" +#include "../../networks/xmg.hpp" +#include "../../traits.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on pre-computed size-optimum XMGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. It will produce an XMG based on + * pre-computed size-optimum XMGs with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + xmg_npn_resynthesis resyn; + const auto xmg = node_resynthesis( klut, resyn ); + \endverbatim + */ +class xmg_npn_resynthesis +{ +public: + /*! \brief Default constructor. + * + */ + xmg_npn_resynthesis() + { + build_db(); + } + + template + void operator()( xmg_network& xmg, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 4 ); + const auto fe = kitty::extend_to( function, 4 ); + const auto config = kitty::exact_npn_canonization( fe ); + + auto func_str = "0x" + kitty::to_hex( std::get<0>( config ) ); + const auto it = class2signal.find( func_str ); + assert( it != class2signal.end() ); + + // const auto it = class2signal.find( static_cast( std::get<0>( config )._bits[0] ) ); + + std::vector pis( 4, xmg.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::vector pis_perm( 4 ); + auto perm = std::get<2>( config ); + for ( auto i = 0; i < 4; ++i ) + { + pis_perm[i] = pis[perm[i]]; + } + + const auto& phase = std::get<1>( config ); + for ( auto i = 0; i < 4; ++i ) + { + if ( ( phase >> perm[i] ) & 1 ) + { + pis_perm[i] = !pis_perm[i]; + } + } + + for ( auto const& po : it->second ) + { + topo_view topo{ db, po }; + auto f = cleanup_dangling( topo, xmg, pis_perm.begin(), pis_perm.end() ).front(); + + if ( !fn( ( ( phase >> 4 ) & 1 ) ? !f : f ) ) + { + return; /* quit */ + } + } + } + +private: + std::unordered_map opt_xmgs; + + inline std::vector split( const std::string& str, const std::string& sep ) + { + std::vector result; + + size_t last = 0; + size_t next = 0; + while ( ( next = str.find( sep, last ) ) != std::string::npos ) + { + result.push_back( str.substr( last, next - last ) ); + last = next + 1; + } + result.push_back( str.substr( last ) ); + + return result; + } + + void load_optimal_xmgs( const unsigned& strategy ) + { + std::vector result; + + switch ( strategy ) + { + case 1: + result = split( npn4_s, "\n" ); + break; + + case 2: + result = split( npn4_sd, "\n" ); + break; + + case 3: + result = split( npn4_ds, "\n" ); + break; + + default: + break; + } + + for ( auto record : result ) + { + auto p = split( record, " " ); + assert( p.size() == 2u ); + opt_xmgs.insert( std::make_pair( p[0], p[1] ) ); + } + } + + std::vector create_xmg_from_str( const std::string& str, const std::vector& signals ) + { + auto sig = signals; + std::vector result; + + std::stack polar; + std::stack inputs; + + for ( auto i = 0ul; i < str.size(); i++ ) + { + // operators polarity + if ( str[i] == '[' || str[i] == '<' ) + { + polar.push( i > 0 && str[i - 1] == '!' ? 1 : 0 ); + } + + // input signals + if ( str[i] >= 'a' && str[i] <= 'd' ) + { + inputs.push( sig[str[i] - 'a' + 1] ); + + polar.push( i > 0 && str[i - 1] == '!' ? 1 : 0 ); + } + else if ( str[i] == '0' ) + { + inputs.push( sig[0] ); + + polar.push( i > 0 && str[i - 1] == '!' ? 1 : 0 ); + } + + // create signals + if ( str[i] == '>' ) + { + assert( inputs.size() >= 3u ); + auto x1 = inputs.top(); + inputs.pop(); + auto x2 = inputs.top(); + inputs.pop(); + auto x3 = inputs.top(); + inputs.pop(); + + assert( polar.size() >= 4u ); + auto p1 = polar.top(); + polar.pop(); + auto p2 = polar.top(); + polar.pop(); + auto p3 = polar.top(); + polar.pop(); + + auto p4 = polar.top(); + polar.pop(); + + inputs.push( db.create_maj( x1 ^ p1, x2 ^ p2, x3 ^ p3 ) ^ p4 ); + polar.push( 0 ); + } + + if ( str[i] == ']' ) + { + assert( inputs.size() >= 2u ); + auto x1 = inputs.top(); + inputs.pop(); + auto x2 = inputs.top(); + inputs.pop(); + + assert( polar.size() >= 3u ); + auto p1 = polar.top(); + polar.pop(); + auto p2 = polar.top(); + polar.pop(); + + auto p3 = polar.top(); + polar.pop(); + + inputs.push( db.create_xor( x1 ^ p1, x2 ^ p2 ) ^ p3 ); + polar.push( 0 ); + } + } + + assert( !polar.empty() ); + auto po = polar.top(); + polar.pop(); + db.create_po( inputs.top() ^ po ); + result.push_back( inputs.top() ^ po ); + return result; + } + + void build_db() + { + std::vector signals; + signals.push_back( db.get_constant( false ) ); + + for ( auto i = 0u; i < 4; ++i ) + { + signals.push_back( db.create_pi() ); + } + + load_optimal_xmgs( 1 ); // size optimization + + for ( const auto& e : opt_xmgs ) + { + class2signal.insert( std::make_pair( e.first, create_xmg_from_str( e.second, signals ) ) ); + } + } + + xmg_network db; + std::unordered_map> class2signal; + + std::string npn4_s = "0x3cc3 [d[!bc]]\n0x1bd8 [!!]\n0x19e3 [[!>]>]\n0x19e1 [>>]\n0x17e8 [d]\n0x179a <[!a][ad]!>\n0x178e [!d]\n0x16e9 [![!d]!>]\n0x16bc [[!c<0ad>]>]\n0x16ad >!>>\n0x16ac [!>>]\n0x16a9 !![ac]>>![ac]>>\n0x169e <[bc][a[bc]]>\n0x169b [>]\n0x169a [a]>\n0x1699 <>>!>>>\n0x1687 [!a!>![c]>]\n0x167e []>\n0x166e [>!>]\n0x03cf \n0x166b ]>\n0x01be >>]>>\n0x07f2 >\n0x07e9 [>>]\n0x6996 [b[a[cd]]]\n0x01af >!>\n0x033c [c]![!bd]>\n0x07e3 ]]>>\n0x1681 <!>![[!ab]]>\n0x01ae [!d<0!>]\n0x07e2 [!>>]\n0x01ad [>]\n0x07e1 [!!<0c!>>]\n0x001f <0!c!<0!a!b>>>\n0x01ac [>]\n0x07e0 [d>]\n0x07bc [>>]\n0x03dc ]!>>\n0x06f6 \n0x06bd [!>>]\n0x077e >[a]>\n0x06b9 ><0!cd>>\n0x06b7 ![b[!ac]]>\n0x06b6 !<0c!<0!d>>>\n0x077a [!<0!a>]\n0x06b5 [!>>]\n0x0ff0 [cd]\n0x0779 [>>]\n0x06b4 [c>]\n0x0778 >[c>]>\n0x007f !>>\n0x06b3 <[cd][!bd]>>\n0x007e <0!d![a]>\n0x06b2 [d]>\n0x0776 <[ab]![cd]>\n0x06b1 [!d!![ab]>]\n0x06b0 [d>>]\n0x037c ]!>>\n0x01ef ]>\n0x0696 >!>\n0x035e [!<0!a!d>!<0!c!>>]\n0x0678 [c]!>\n0x0676 ![ab]>\n0x0669 [!d!>!>]\n0x01bc [<0!b!c>>]\n0x0663 [!<0b>]\n0x07f0 <0[cd][cd]>>\n0x01bf >\n0x0666 <0[ab]!<0cd>>\n0x0661 [>>]\n0x1689 [!<0d![ab]>>]\n0x0660 <0![!cd][ab]>\n0x0182 [!>>]\n0x07b6 [c!>]\n0x03d7 >\n0x06f1 >![d>]>>\n0x03dd !![!d]>\n0x0180 [!!>]\n0x07b4 [!<0c!<0d>>]\n0x03db [a]>\n0x07b0 [cd]>\n0x03d4 [d>]\n0x03c7 <[!bc]<0!a!c>!<0bd>>\n0x03c6 [b>]>\n0x03c5 [!!>]\n0x03c3 ![bc]>\n0x03c1 ]!![b]>>\n0x03c0 [d]\n0x1798 [><0!d>>]\n0x036f ]>\n0x03fc [d!<0!b!c>]\n0x1698 <0!d>>>\n0x177e [!<0!a!c>]\n0x066f \n0x035b !<0b!c><0a!c>>\n0x1697 [>[!bc]]\n0x066b !]>>\n0x07f8 [d>]\n0x035a [c]>\n0x1696 <0>![a[!bc]]>\n0x003f <0!d!>\n0x0673 ]!>>\n0x0359 [!<0b!c>]\n0x0672 <<0!b!d>[ab][cd]>\n0x0358 [!>!>![c<0a!d>]>]\n0x003d <0>>>\n0x0357 !>\n0x003c <0!d[bc]>\n0x0356 [<0!a!d><0!b!c>]\n0x07e6 [>]\n0x033f \n0x033d [<0!d>>>]\n0x0690 [c]\n0x01e9 [!>!<0d>>>]\n0x1686 [!a>]\n0x07f1 >\n0x01bd ]<0!c>>\n0x1683 [[!bc]>]\n0x001e <0!d[c]>\n0x01ab [ad]>\n0x01aa >>\n0x01a9 ]<0!ad>>\n0x001b <0!!>\n0x01a8 [<0!b!c>>]\n0x000f <0!c!d>\n0x0199 [!ab]<0!d[!ab]>>\n0x0198 [b!]>]\n0x0197 [!a<0bc>]>\n0x06f9 [d]\n0x0196 >!>\n0x03d8 [d!>]\n0x06f2 <[cd]<0a![cd]>>\n0x03de ]>\n0x018f ]>\n0x0691 <<0c>>>\n0x01ea [!d!>]\n0x07b1 <[cd]<0!a!c>!>\n0x0189 <<0b!d>[a]>\n0x036b ]>>\n0x03d5 ]>\n0x0186 [>]\n0x0119 <0[!ab]!>\n0x0368 [![c<0a!d>]!]>>]\n0x0183 <[!bc]<0a!d><0!a!b>>\n0x07b5 ![ac][cd]>\n0x0181 [>>>]\n0x036e ]!>\n0x011f >\n0x016f >!>>\n0x016e >\n0x013f !>\n0x019f <0!<0bd>>\n0x013e [[!bd]!>]\n0x019e [d>>]\n0x013d <0!>>\n0x016b >\n0x01fe [d!<0!a!>]\n0x166a [!<0b!c>>>]\n0x0019 [<0b>>]\n0x006b <0!a<0bc>>>\n0x069f !>\n0x013c <0!!>>>\n0x037e [>]\n0x012f <0!a!c>>\n0x0693 [c!![!b>]>]\n0x012d <0!>\n0x01ee [!d<0!a!b>]>\n0x012c [!<0!b!c>>]\n0x017f >\n0x1796 ![!d]>\n0x036d <!>\n0x011e >>!!<0!d!<0!c<0!a!b>>>>>\n0x0679 !>>\n0x035f >\n0x067b >![b]!>\n0x0118 <0[!d[a]]![a]>>\n0x067a [>>]\n0x0117 <0!!>>\n0x1ee1 [!d![!c]]\n0x016a >!!>>>\n0x0169 ><0!d>>\n0x037d ]>\n0x0697 <<0ac>!!!>>\n0x006f ![!ab]>\n0x17ac [>]\n0x0069 <0!d![a[bc]]>\n0x0667 <0!<0ab>!>>\n0x0168 >>>\n0x067e ]>\n0x011b !>\n0x036a []\n0x018b <0a!d>>\n0x0001 <0<0!b!d>>\n0x1669 <!<[bd]!>>>\n0x0018 <0!d[a]>\n0x168b [![d]>>]\n0x0662 [>>]\n0x00ff !d\n0x168e [>>]\n0x0007 <0<0!c!d>!>\n0x19e6 [[ad]>]\n0x0116 <0!![!c>]>\n0x036c [[bd]<0c!>]\n0x01e8 <<0b!d>>\n0x06f0 <<0!ab>![!cd]>\n0x03d6 [[c]!>>]\n0x1be4 [!d!]\n0x0187 <0!c>>\n0x0003 <0!d<0!b!c>>\n0x017e [[!ad]>>]\n0x01eb ]>>\n0x0006 <0!d>\n0x011a [a[cd]>]\n0x0369 <0[!c<0!d![!ab]>]!<0bd>>\n0x03d9 [!b>]\n0x0000 0\n0x019b ![ab]>\n0x1668 [>]\n0x18e7 [d[!b]]\n0x0017 <0!d!>\n0x019a ]<0!c>>\n0x0016 <0!>!>>"; + std::string npn4_sd = "0x3cc3 [b[!cd]]\n0x1bd8 [!]\n0x19e3 [>]\n0x19e1 [>>]\n0x17e8 [d]\n0x179a [>[!bd]>]\n0x178e \n0x16e9 [d<!<0a!b>>]\n0x16bc [<0!b!c>[ad]>]\n0x16ad [![ac]><0d>]\n0x16ac [!>>]\n0x16a9 <0>[!<0!b!c>[!ad]]>\n0x169e >\n0x169b <>!>!>\n0x169a [a>]\n0x1699 >!>>\n0x1687 [[ac]!<0bc>>]\n0x167e <>[a]!>>\n0x166e [>>]\n0x03cf \n0x166b ]>\n0x01be [ad]!>>\n0x07f2 <[cd]<0a!b>!<0ad>>\n0x07e9 [>>]\n0x6996 [[bc][ad]]\n0x01af <[!ac]<0a!d>!>\n0x033c <<0c!d>!>\n0x07e3 [c!<0b!c>>]\n0x1681 <0[![ab]]!>>\n0x01ae ![!ad]<0b!d>>\n0x07e2 [!>>]\n0x01ad <0b!d>[!ac]>\n0x07e1 [c!>]\n0x001f <0!c!d>>\n0x01ac [d!<0!b!c>>]\n0x07e0 [d>]\n0x07bc [>!<0!d>]\n0x03dc ]!>>\n0x06f6 \n0x06bd <>!>>\n0x077e <<0!ab>!>\n0x06b9 ![!cd]>>\n0x06b7 >\n0x06b6 [!c!<!<0c!d>[ab]>]\n0x077a [!<0a!>![cd]]\n0x06b5 ![!c]>\n0x0ff0 [cd]\n0x0779 <>!>\n0x06b4 [!c![ab]>]\n0x0778 [[cd]<0<0ab>>]\n0x007f <0!b!d>>\n0x06b3 [![!bd]!>]\n0x007e <0!d![a]>\n0x06b2 [!<0!c!>>]\n0x0776 <[ab][cd]!>\n0x06b1 [d[!ab]>]\n0x06b0 [>!<0cd>>]\n0x037c [[!bd]!>]\n0x01ef ]>\n0x0696 <<0c!d>!>\n0x035e [!<0!c!>>]\n0x0678 <<0a!b>[c]>\n0x0676 [ab]<0c!d>>\n0x0669 [<0!c!d><[cd]<0!c!d>[ab]>]\n0x01bc [>!<0!b!c>]\n0x0663 <0[b]!<0cd>>\n0x07f0 <[cd]!!<0ab>>\n0x01bf <0!a!b>>\n0x0666 <0[ab]!<0cd>>\n0x0661 <!>[]>\n0x1689 [[d]<0!c>]\n0x0660 [!]\n0x0182 [>>]\n0x07b6 [>[!c]]\n0x03d7 >\n0x06f1 [><0!ad>>]\n0x03dd <[bd]<0!a!d>!<0cd>>\n0x0180 [!>]\n0x07b4 [>>]\n0x03db <[!bc]!>\n0x07b0 <<0ac>[cd]!>\n0x03d4 [!d>]\n0x03c7 <0!a!c>[!bc]>\n0x03c6 <0!<0cd>[!b]>\n0x03c5 <[bd]<0!a!c>![bc]>\n0x03c3 [!bc]>\n0x03c1 [b]>>\n0x03c0 [d]\n0x1798 [>>]\n0x036f ]>\n0x03fc [d!<0!b!c>]\n0x1698 [b<[ac][!ad]>]\n0x177e [<0!b!d>!]\n0x066f \n0x035b <<0!b!c>!<0ac>>\n0x1697 >\n0x066b [>!<0!cd>>]\n0x07f8 [!d!>]\n0x035a [!><0!a!d>]\n0x1696 >>>\n0x003f <0!d!>\n0x0673 ]>>\n0x0359 []\n0x0672 <[cd]<0!b!d>[ab]>\n0x0358 <<0a!c>[!d<0!b!c>]>\n0x003d [bc]<0!a!d>>\n0x0357 <0!a!d>>\n0x003c <0!d![!bc]>\n0x0356 [!<0!a!d>!<0!b!c>]\n0x07e6 [>]\n0x033f \n0x033d <0!b>>\n0x0690 [!d]\n0x01e9 ![b]>\n0x1686 [[bc]>]\n0x07f1 [cd]>\n0x01bd ]<0!b>>\n0x1683 [[bc]!>]\n0x001e <0!d![!c]>\n0x01ab >\n0x01aa <[ad]<0!b!c><0a!d>>\n0x01a9 [a>]\n0x001b <0!>\n0x01a8 [a>]\n0x000f <0!c!d>\n0x0199 <[!ab]<0!a!c>>\n0x0198 [!>]\n0x0197 ]>>\n0x06f9 [d]\n0x0196 <<0!d!>[ad]!>\n0x03d8 [>>]\n0x06f2 >\n0x03de ][bd]>\n0x018f ]>\n0x0691 <0>>>\n0x01ea [d>]\n0x07b1 <<0!a!c>[cd]>\n0x0189 <[!ab]!<0a!d>>\n0x036b !>\n0x03d5 [!<0a!d>>]\n0x0186 [>!]\n0x0119 <0[!ab]!>\n0x0368 [>]\n0x0183 <[!bc]<0a!d><0!a!b>>\n0x07b5 [cd]![ac]>\n0x0181 <[b]<0!d!>>\n0x036e [!b<0!a!d>]>\n0x011f >\n0x016f <<0a!d>!>\n0x016e [a[bd]]>\n0x013f <0!<0ad>!>\n0x019f <0!a!b>>\n0x013e [[!cd]>]\n0x019e <0!b>>\n0x013d <!!>\n0x016b ![!b[!ac]]>\n0x01fe [d>]\n0x166a ]!>>\n0x0019 <0!a!b><0b!c>>\n0x006b >!>\n0x069f !>\n0x013c <<0!ad>>!>>\n0x037e [!>]\n0x012f !>\n0x0693 [![bd]>]\n0x012d <<0ac>!>\n0x01ee [!!>]\n0x012c [<0!a!b>>]\n0x017f >\n0x1796 !>>\n0x036d <!>\n0x011e >!!<0c!d>>>\n0x0679 <0!b!c>>[c]>\n0x035f !>\n0x067b [![ad]<0b!c>]>\n0x0118 <<0!a!b>>>\n0x067a [>>]\n0x0117 ><0!c!>>\n0x1ee1 [[cd]<0!a!b>]\n0x016a !>!>>\n0x0169 <0!d>>\n0x037d [!d<0!b!c>]>\n0x0697 <>!>\n0x006f [ab]>\n0x17ac [>]\n0x0069 <0!d[a[!bc]]>\n0x0667 !>\n0x0168 [>>]\n0x067e ][ab]>\n0x011b !>\n0x036a [!<0!a!d>]\n0x018b <0!b!c><0ab>>\n0x0001 <0<0!a!b><0!c!d>>\n0x1669 <>!>\n0x0018 <0!d[a]>\n0x168b [[!a]>>]\n0x0662 [ab][d]>\n0x00ff !d\n0x168e [!>>]\n0x0007 <0!!<0ab>>\n0x19e6 [[ad]<0b!<0ac>>]\n0x0116 [<0[!ac]!>]\n0x036c [[bd]!>]\n0x01e8 <0!ad>>\n0x06f0 <[cd]!>\n0x03d6 [!<0bc>>>]\n0x1be4 [d!]\n0x0187 !>\n0x0003 <0!d<0!b!c>>\n0x017e [[!bd]<[!bd]>]\n0x01eb ]!>>\n0x0006 <0[ab]<0!c!d>>\n0x011a <0[d[ac]]!>\n0x0369 >>>\n0x03d9 [d<>]\n0x0000 0\n0x019b ![ab]>\n0x1668 [>]\n0x18e7 [[!cd]]\n0x0017 <0!d!>\n0x019a ]!>>\n0x0016 <0>!>"; + std::string npn4_ds = "0x3cc3 [b[!cd]]\n0x1bd8 [!]\n0x19e3 [>]\n0x19e1 [bc]]![bc]>>\n0x17e8 [d]\n0x179a [>[!bd]>]\n0x178e \n0x16e9 [d<!<0a!b>>]\n0x16bc [<0!b!c>[ad]>]\n0x16ad [![ac]><0d>]\n0x16ac [<[bc]<0ab>>>]\n0x16a9 <0>[!<0!b!c>[!ad]]>\n0x169e >\n0x169b <>!>!>\n0x169a [a>]\n0x1699 >!>>\n0x1687 [[ac]!<0bc>>]\n0x167e <>[a]!>>\n0x166e [>>]\n0x03cf \n0x166b ]>\n0x01be [ad]!>>\n0x07f2 <[cd]<0a!b>!<0ad>>\n0x07e9 [>>]\n0x6996 [[bc][ad]]\n0x01af <[!ac]<0a!d>!>\n0x033c <<0c!d>!>\n0x07e3 [c!<0b!c>>]\n0x1681 <0[![ab]]!>>\n0x01ae ![!ad]<0b!d>>\n0x07e2 [>]\n0x01ad <0b!d>[!ac]>\n0x07e1 [c!>]\n0x001f <0!c!d>>\n0x01ac [d!<0!b!c>>]\n0x07e0 [d>]\n0x07bc [>!<0!d>]\n0x03dc ]!>>\n0x06f6 \n0x06bd <>!>>\n0x077e <<0!ab>!>\n0x06b9 ![!cd]>>\n0x06b7 >\n0x06b6 [!c!<!<0c!d>[ab]>]\n0x077a [!<0a!>![cd]]\n0x06b5 ![!c]>\n0x0ff0 [cd]\n0x0779 <>!>\n0x06b4 [!c![ab]>]\n0x0778 [[cd]<0<0ab>>]\n0x007f <0!b!d>>\n0x06b3 [![!bd]!>]\n0x007e <0a!c>>\n0x06b2 [!<0!c!>>]\n0x0776 <[ab][cd]!>\n0x06b1 [d[!ab]>]\n0x06b0 [>!<0cd>>]\n0x037c [[!bd]!>]\n0x01ef <<0b!d>!<0ad>>\n0x0696 <<0c!d>!>\n0x035e <<0b!d>!>\n0x0678 <<0a!b>[c]>\n0x0676 [ab]<0c!d>>\n0x0669 [<0!c!d><[cd]<0!c!d>[ab]>]\n0x01bc [>!<0!b!c>]\n0x0663 <0[b]!<0cd>>\n0x07f0 <[cd]!!<0ab>>\n0x01bf <0!a!b>>\n0x0666 <0[ab]!<0cd>>\n0x0661 <!>[]>\n0x1689 [[d]<0!c>]\n0x0660 [!]\n0x0182 <0>!>\n0x07b6 [>[!c]]\n0x03d7 >\n0x06f1 [><0!ad>>]\n0x03dd <[bd]<0!a!d>!<0cd>>\n0x0180 [!>]\n0x07b4 [>>]\n0x03db <[!bc]!>\n0x07b0 <<0ac>[cd]!>\n0x03d4 <[cd]<0!a!d>[bd]>\n0x03c7 <0!a!c>[!bc]>\n0x03c6 <0!<0cd>[!b]>\n0x03c5 <[bd]<0!a!c>![bc]>\n0x03c3 [!bc]>\n0x03c1 [b]>>\n0x03c0 [d]\n0x1798 [>>]\n0x036f ]>\n0x03fc [d!<0!b!c>]\n0x1698 [b<[ac][!ad]>]\n0x177e [<0!b!d>!]\n0x066f \n0x035b <<0!b!c>!<0ac>>\n0x1697 >\n0x066b [>!<0!cd>>]\n0x07f8 [!d!>]\n0x035a [!><0!a!d>]\n0x1696 >>>\n0x003f <0!d!>\n0x0673 ]>>\n0x0359 []\n0x0672 <[cd]<0!b!d>[ab]>\n0x0358 <<0a!c>[!d<0!b!c>]>\n0x003d [bc]<0!a!d>>\n0x0357 <0!a!d>>\n0x003c <0!d![!bc]>\n0x0356 [!<0!a!d>!<0!b!c>]\n0x07e6 [>]\n0x033f \n0x033d <0!b>>\n0x0690 <<0c!d>>\n0x01e9 ![b]>\n0x1686 [[bc]>]\n0x07f1 [cd]>\n0x01bd ]<0!b>>\n0x1683 [[bc]!>]\n0x001e <0!d![!c]>\n0x01ab >\n0x01aa <[ad]<0!b!c><0a!d>>\n0x01a9 [a>]\n0x001b <0!>\n0x01a8 [a>]\n0x000f <0!c!d>\n0x0199 <[!ab]<0!a!c>>\n0x0198 [!>]\n0x0197 ]>>\n0x06f9 <[cd]>\n0x0196 <<0!d!>[ad]!>\n0x03d8 [>>]\n0x06f2 >\n0x03de ][bd]>\n0x018f ]>\n0x0691 <0>>>\n0x01ea [d>]\n0x07b1 <<0!a!c>[cd]>\n0x0189 <[!ab]!<0a!d>>\n0x036b !>\n0x03d5 [!<0a!d>>]\n0x0186 [>!]\n0x0119 <0[!ab]!>\n0x0368 [>]\n0x0183 <[!bc]<0a!d><0!a!b>>\n0x07b5 [cd]![ac]>\n0x0181 <[b]<0!d!>>\n0x036e [!b<0!a!d>]>\n0x011f >\n0x016f <<0a!d>!>\n0x016e [a[bd]]>\n0x013f <0!<0ad>!>\n0x019f <0!a!b>>\n0x013e [!>![!d]]\n0x019e <0!b>>\n0x013d <!!>\n0x016b ![!b[!ac]]>\n0x01fe [d>]\n0x166a ]!>>\n0x0019 <0!a!b><0b!c>>\n0x006b >!>\n0x069f !>\n0x013c <<0!ad>>!>>\n0x037e [!>]\n0x012f !>\n0x0693 [![bd]>]\n0x012d <<0ac>!>\n0x01ee [!!>]\n0x012c [<0!a!b>>]\n0x017f >\n0x1796 !>>\n0x036d <!>\n0x011e >!!<0c!d>>>\n0x0679 <0!b!c>>[c]>\n0x035f !>\n0x067b [![ad]<0b!c>]>\n0x0118 <<0!a!b>>>\n0x067a <[c]<0a!b>!>\n0x0117 ><0!c!>>\n0x1ee1 [[cd]<0!a!b>]\n0x016a !>!>>\n0x0169 <0!d>>\n0x037d [!d<0!b!c>]>\n0x0697 <>!>\n0x006f [ab]>\n0x17ac [>]\n0x0069 <0!d[a[!bc]]>\n0x0667 !>\n0x0168 [>>]\n0x067e ][ab]>\n0x011b !>\n0x036a [!<0!a!d>]\n0x018b <0!b!c><0ab>>\n0x0001 <0<0!a!b><0!c!d>>\n0x1669 <>!>\n0x0018 <0ab>!>\n0x168b <<0!b!c>!<0!b!c>>!!>>\n0x0662 [ab][d]>\n0x00ff !d\n0x168e [!>>]\n0x0007 <0!!<0ab>>\n0x19e6 [[ad]<0b!<0ac>>]\n0x0116 [<0[!ac]!>]\n0x036c [>!>]\n0x01e8 <0!ad>>\n0x06f0 <[cd]!>\n0x03d6 [!<0bc>>>]\n0x1be4 [d!]\n0x0187 !>\n0x0003 <0!d<0!b!c>>\n0x017e [[!bd]<[!bd]>]\n0x01eb ]!>>\n0x0006 <0[ab]<0!c!d>>\n0x011a <0[d[ac]]!>\n0x0369 >>>\n0x03d9 [d<>]\n0x0000 0\n0x019b ![ab]>\n0x1668 [>]\n0x18e7 [[!cd]]\n0x0017 <0!d!>\n0x019a ]!>>\n0x0016 <0>!>"; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/pattern_generation.hpp b/third-party/mockturtle/include/mockturtle/algorithms/pattern_generation.hpp new file mode 100644 index 00000000000..ef5657a5611 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/pattern_generation.hpp @@ -0,0 +1,603 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file pattern_generation.hpp + \brief Expressive Simulation Pattern Generation + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "circuit_validator.hpp" +#include "dont_cares.hpp" +#include "simulation.hpp" +#include +#include +#include +#include + +namespace mockturtle +{ + +struct pattern_generation_params +{ + /*! \brief Number of patterns each node should have for both values. + * + * When this parameter is set to greater than 1, and if the network has more + * than 2048 PIs, the `BUFFER_SIZE` in `lib/bill/sat/interface/abc_bsat2.hpp` + * has to be increased to at least `ntk.num_pis()`. + */ + uint32_t num_stuck_at{ 1 }; + + /*! \brief Whether to consider observability, and how many levels. 0 = no. -1 = Consider TFO until PO. */ + int32_t odc_levels{ 0 }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. Note that it will take more time to do extra ODC computation if this is turned on. */ + bool verbose{ false }; + + /*! \brief Random seed. */ + std::default_random_engine::result_type random_seed{ 1 }; + + /*! \brief Conflict limit of the SAT solver. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Maximum number of clauses of the SAT solver. (incremental CNF construction) */ + uint32_t max_clauses{ 1000 }; +}; + +struct pattern_generation_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Time for ODC computation */ + stopwatch<>::duration time_odc{ 0 }; + + /*! \brief Number of constant nodes. */ + uint32_t num_constant{ 0 }; + + /*! \brief Number of generated patterns. */ + uint32_t num_generated_patterns{ 0 }; + + /*! \brief Number of stuck-at patterns that is re-generated because the original one was unobservable. */ + uint32_t unobservable_type1{ 0 }; + + /*! \brief Number of additional patterns generated because the node was unobservable with one value. */ + uint32_t unobservable_type2{ 0 }; + + /*! \brief Number of unobservable nodes (node for which an observable pattern can not be found). */ + uint32_t unobservable_node{ 0 }; +}; + +namespace detail +{ + +template +class patgen_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using TT = incomplete_node_map; + + explicit patgen_impl( Ntk& ntk, Simulator& sim, pattern_generation_params const& ps, validator_params& vps, pattern_generation_stats& st ) + : ntk( ntk ), ps( ps ), st( st ), vps( vps ), validator( ntk, vps ), + tts( ntk ), sim( sim ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + if constexpr ( has_EXCDC_interface_v ) + { + sim.remove_CDC_patterns( ntk ); + } + + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + + if ( ps.num_stuck_at > 0 ) + { + stuck_at_check(); + if constexpr ( std::is_same_v ) + { + sim.pack_bits(); + call_with_stopwatch( st.time_sim, [&]() { + tts.reset(); + simulate_nodes( ntk, tts, sim, true ); + } ); + } + if constexpr ( substitute_const ) + { + for ( auto n : const_nodes ) + { + if ( !ntk.is_dead( ntk.get_node( n ) ) ) + { + ntk.substitute_node( ntk.get_node( n ), ntk.get_constant( ntk.is_complemented( n ) ) ); + } + } + } + } + + if constexpr ( use_odc ) + { + observability_check(); + if constexpr ( std::is_same_v ) + { + sim.pack_bits(); + call_with_stopwatch( st.time_sim, [&]() { + tts.reset(); + simulate_nodes( ntk, tts, sim, true ); + } ); + } + } + + if constexpr ( std::is_same_v ) + { + sim.randomize_dont_care_bits( ps.random_seed ); + if constexpr ( has_EXCDC_interface_v ) + { + sim.remove_CDC_patterns( ntk ); + } + } + } + +private: + void stuck_at_check() + { + progress_bar pbar{ ntk.size(), "patgen-sa |{0}| node = {1:>4} #pat = {2:>4}", ps.progress }; + + kitty::partial_truth_table zero = sim.compute_constant( false ); + + ntk.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i, sim.num_bits() ); + + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + assert( zero.num_bits() == sim.num_bits() ); + + if ( ( tts[n] == zero ) || ( tts[n] == ~zero ) ) + { + bool value = ( tts[n] == zero ); /* wanted value of n */ + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( 0 ); + return validator.validate( n, !value ); + } ); + if ( !res ) + { + return true; /* timeout, next node */ + } + else if ( !( *res ) ) /* SAT, pattern found */ + { + if constexpr ( use_odc ) + { + /* check if the found pattern is observable */ + bool observable = call_with_stopwatch( st.time_odc, [&]() { + return pattern_is_observable( ntk, n, validator.cex, ps.odc_levels ); + } ); + if ( !observable ) + { + if ( ps.verbose ) + { + std::cout << "\t[i] generated pattern is not observable (type 1). node: " << n << ", with value " << value << "\n"; + } + + const auto res2 = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.validate( n, !value ); + } ); + if ( res2 ) + { + if ( !( *res2 ) ) + { + ++st.unobservable_type1; + if ( ps.verbose ) + { + assert( pattern_is_observable( ntk, n, validator.cex, ps.odc_levels ) ); + std::cout << "\t\t[i] unobservable pattern resolved.\n"; + } + } + else + { + ++st.unobservable_node; + if ( ps.verbose ) + { + std::cout << "\t\t[i] unobservable node " << n << "\n"; + } + } + } + } + } + + new_pattern( validator.cex, n ); + + if ( ps.num_stuck_at > 1 ) + { + auto generated = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.generate_pattern( n, value, { validator.cex }, ps.num_stuck_at - 1 ); + } ); + for ( auto& pattern : generated ) + { + new_pattern( pattern, n ); + } + } + + zero = sim.compute_constant( false ); + } + else /* UNSAT, constant node */ + { + ++st.num_constant; + const_nodes.emplace_back( value ? ntk.make_signal( n ) : !ntk.make_signal( n ) ); + return true; /* next gate */ + } + } + else if ( ps.num_stuck_at > 1 ) + { + auto const& tt = tts[n]; + if ( kitty::count_ones( tt ) < ps.num_stuck_at ) + { + generate_more_patterns( n, tt, true, zero ); + } + else if ( kitty::count_zeros( tt ) < ps.num_stuck_at ) + { + generate_more_patterns( n, tt, false, zero ); + } + } + return true; /* next gate */ + } ); + } + + void observability_check() + { + progress_bar pbar{ ntk.size(), "patgen-obs |{0}| node = {1:>4} #pat = {2:>4}", ps.progress }; + + kitty::partial_truth_table zero = sim.compute_constant( false ); + + ntk.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i, sim.num_bits() ); + + for ( auto& f : const_nodes ) + { + if ( ntk.get_node( f ) == n ) + { + return true; /* skip constant nodes */ + } + } + + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + assert( zero.num_bits() == sim.num_bits() ); + + /* compute ODC */ + auto odc = call_with_stopwatch( st.time_odc, [&]() { + return observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); + } ); + + /* check if under non-ODCs n is always the same value */ + if ( ( tts[n] & ~odc ) == zero ) + { + if ( ps.verbose ) + { + std::cout << "\t[i] under all observable patterns, node " << n << " is always 0 (type 2).\n"; + } + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.validate( n, false ); + } ); + if ( res ) + { + if ( !( *res ) ) + { + new_pattern( validator.cex, n ); + ++st.unobservable_type2; + + if ( ps.verbose ) + { + auto odc2 = call_with_stopwatch( st.time_odc, [&]() { return observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); } ); + assert( ( tts[n] & ~odc2 ) != sim.compute_constant( false ) ); + std::cout << "\t\t[i] added generated pattern to resolve unobservability.\n"; + } + + zero = sim.compute_constant( false ); + } + else + { + ++st.unobservable_node; + if ( ps.verbose ) + { + std::cout << "\t\t[i] unobservable node " << n << "\n"; + } + } + } + } + else if ( ( tts[n] | odc ) == ~zero ) + { + if ( ps.verbose ) + { + std::cout << "\t[i] under all observable patterns, node " << n << " is always 1 (type 2).\n"; + } + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.validate( n, true ); + } ); + if ( res ) + { + if ( !( *res ) ) + { + new_pattern( validator.cex, n ); + ++st.unobservable_type2; + + if ( ps.verbose ) + { + auto odc2 = call_with_stopwatch( st.time_odc, [&]() { return observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); } ); + assert( ( tts[n] | odc2 ) != sim.compute_constant( true ) ); + std::cout << "\t\t[i] added generated pattern to resolve unobservability.\n"; + } + + zero = sim.compute_constant( false ); + } + else + { + ++st.unobservable_node; + if ( ps.verbose ) + { + std::cout << "\t\t[i] unobservable node " << n << "\n"; + } + } + } + } + + return true; /* next gate */ + } ); + } + +private: + void new_pattern( std::vector const& pattern, node const& n ) + { + if constexpr ( std::is_same_v ) + { + sim.add_pattern( pattern, compute_support( n ) ); + } + else + { + (void)n; + sim.add_pattern( pattern ); + } + + if constexpr ( has_EXCDC_interface_v ) + { + assert( !ntk.pattern_is_EXCDC( pattern ) ); + } + ++st.num_generated_patterns; + + /* re-simulate */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + } + + void generate_more_patterns( node const& n, kitty::partial_truth_table const& tt, bool value, kitty::partial_truth_table& zero ) + { + /* collect the `value` patterns */ + std::vector> patterns; + for ( auto i = 0u; i < tt.num_bits(); ++i ) + { + if ( kitty::get_bit( tt, i ) == value ) + { + patterns.emplace_back(); + ntk.foreach_pi( [&]( auto const& pi ) { + patterns.back().emplace_back( kitty::get_bit( tts[pi], i ) ); + } ); + } + } + + auto generated = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.generate_pattern( n, value, patterns, ps.num_stuck_at - patterns.size() ); + } ); + for ( auto& pattern : generated ) + { + new_pattern( pattern, n ); + } + zero = sim.compute_constant( false ); + } + + std::vector compute_support( node const& n ) + { + ntk.incr_trav_id(); + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + std::vector leaves; + mark_fanout_leaves_rec( n, 1, leaves ); + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + { + leaves.emplace_back( ntk.get_node( f ) ); + } + } ); + + ntk.incr_trav_id(); + for ( auto& l : leaves ) + { + mark_support_rec( l ); + } + } + } + mark_support_rec( n ); + + std::vector care( ntk.num_pis(), false ); + ntk.foreach_pi( [&]( auto const& f, uint32_t i ) { + if ( ntk.visited( f ) == ntk.trav_id() ) + { + care[i] = true; + } + } ); + return care; + } + + void mark_support_rec( node const& n ) + { + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + { + return true; + } + mark_support_rec( ntk.get_node( f ) ); + return true; + } ); + } + + void mark_fanout_leaves_rec( node const& n, int32_t level, std::vector& leaves ) + { + ntk.foreach_fanout( n, [&]( auto const& fo ) { + if ( ntk.visited( fo ) == ntk.trav_id() ) + { + return true; + } + ntk.set_visited( fo, ntk.trav_id() ); + + if ( level == ps.odc_levels ) + { + leaves.emplace_back( fo ); + return true; + } + + mark_fanout_leaves_rec( fo, level + 1, leaves ); + return true; + } ); + } + +private: + Ntk& ntk; + + pattern_generation_params const& ps; + pattern_generation_stats& st; + + validator_params& vps; + circuit_validator validator; + + TT tts; + std::vector const_nodes; + + Simulator& sim; +}; + +} /* namespace detail */ + +/*! \brief Expressive simulation pattern generation. + * + * This function implements two simulation pattern generation methods: + * stuck-at value checking and observability checking. Please refer to + * [1] for details of the algorithm and its purpose. + * + * [1] Simulation-Guided Boolean Resubstitution. IWLS 2020 (arXiv:2007.02579). + * + * \param sim Reference of a `partial_simulator` or `bit_packed_simulator` + * object where the generated patterns will be stored. + * It can be empty (`Simulator( ntk.num_pis(), 0 )`) + * or already containing some patterns generated from previous runs + * (`Simulator( filename )`) or randomly generated + * (`Simulator( ntk.num_pis(), num_random_patterns )`). The generated + * patterns can then be written out with `write_patterns` + * or directly be used by passing the simulator to another algorithm. + */ +template +void pattern_generation( Ntk& ntk, Simulator& sim, pattern_generation_params const& ps = {}, pattern_generation_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( std::is_same_v || std::is_same_v, "Simulator should be either partial_simulator or bit_packed_simulator" ); + + pattern_generation_stats st; + validator_params vps; + vps.conflict_limit = ps.conflict_limit; + vps.max_clauses = ps.max_clauses; + vps.random_seed = ps.random_seed; + + if ( ps.odc_levels != 0 ) + { + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + detail::patgen_impl p( fanout_view, sim, ps, vps, st ); + p.run(); + } + else + { + detail::patgen_impl p( ntk, sim, ps, vps, st ); + p.run(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/reconv_cut.hpp b/third-party/mockturtle/include/mockturtle/algorithms/reconv_cut.hpp new file mode 100644 index 00000000000..3c7e5fe9b7a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/reconv_cut.hpp @@ -0,0 +1,461 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file reconv_cut.hpp + \brief Implements reconvergence-driven cuts (based on ABC's + implementation in `abcReconv.c` by Alan Mishchenko). + + \author Heinz Riener +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for reconvergence-driven cut computation + * + * The data structure `reconvergence_driven_cut_parameters` holds configurable parameters + * with default arguments for `reconvergence_driven_cut_impl*`. + */ +struct reconvergence_driven_cut_parameters +{ + /* Maximum number of leaves */ + uint64_t max_leaves{ 8u }; + + /* Skip nodes with many fanouts */ + uint64_t max_fanouts{ 100000u }; + + /* Initially reserve memory for a fixed number of nodes */ + uint64_t reserve_memory_for_nodes{ 300u }; +}; + +/*! \brief Statistics for reconvergence-driven cut computation + * + * The data structure `reconvergence_driven_cut_statistics` holds data + * collected when running a reconvergence-driven cut computation + * algorithm. + */ +struct reconvergence_driven_cut_statistics +{ + /* Total number of calls */ + uint64_t num_calls{ 0 }; + + /* Total number of leaves */ + uint64_t num_leaves{ 0 }; + + /* Total number of nodes */ + uint64_t num_nodes{ 0 }; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class reconvergence_driven_cut_impl +{ +public: + using parameters_type = reconvergence_driven_cut_parameters; + using statistics_type = reconvergence_driven_cut_statistics; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit reconvergence_driven_cut_impl( Ntk const& ntk, reconvergence_driven_cut_parameters const& ps, reconvergence_driven_cut_statistics& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + leaves.reserve( ps.max_leaves ); + if constexpr ( compute_nodes ) + { + nodes.reserve( ps.reserve_memory_for_nodes ); + } + } + + std::pair, std::vector> run( std::vector const& pivots ) + { + assert( pivots.size() > 0u ); + + /* prepare for traversal and clean internal state */ + ntk.incr_trav_id(); + nodes.clear(); + leaves.clear(); + + /* collect and mark all pivots */ + for ( const auto& pivot : pivots ) + { + if constexpr ( compute_nodes ) + { + nodes.emplace_back( pivot ); + } + ntk.set_visited( pivot, ntk.trav_id() ); + } + + leaves = pivots; + + if ( leaves.size() > ps.max_leaves ) + { + /* special case: cut already overflows at the current node because the cut size limit is very low */ + leaves.clear(); + nodes.clear(); + return { leaves, nodes }; + } + + /* compute the cut */ + while ( construct_cut() ) + ; + assert( leaves.size() <= ps.max_leaves ); + + /* update statistics */ + ++st.num_calls; + st.num_leaves += leaves.size(); + st.num_nodes += nodes.size(); + + return { leaves, nodes }; + } + +private: + bool construct_cut() + { + uint64_t best_cost{ std::numeric_limits::max() }; + std::optional best_fanin; + uint64_t best_position; + + /* evaluate fanins of the cut */ + uint64_t position{ 0 }; + for ( const auto& l : leaves ) + { + uint64_t const current_cost{ cost( l ) }; + if constexpr ( sort_equal_cost_by_level ) + { + if ( best_cost > current_cost || + ( best_cost == current_cost && best_fanin && ntk.level( l ) > ntk.level( *best_fanin ) ) ) + { + best_cost = current_cost; + best_fanin = std::make_optional( l ); + best_position = position; + } + } + else + { + if ( best_cost > current_cost ) + { + best_cost = current_cost; + best_fanin = std::make_optional( l ); + best_position = position; + } + } + + if ( best_cost == 0u ) + { + break; + } + + ++position; + } + + if ( !best_fanin ) + { + return false; + } + + if ( leaves.size() - 1 + best_cost > ps.max_leaves ) + { + return false; + } + + /* remove the best node from the array */ + leaves.erase( std::begin( leaves ) + best_position ); + + /* add the fanins of best to leaves and nodes */ + ntk.foreach_fanin( *best_fanin, [&]( signal const& fi ) { + node const& n = ntk.get_node( fi ); + if ( n != 0 && ( ntk.visited( n ) != ntk.trav_id() ) ) + { + ntk.set_visited( n, ntk.trav_id() ); + if constexpr ( compute_nodes ) + { + nodes.emplace_back( n ); + } + leaves.emplace_back( n ); + } + } ); + + assert( leaves.size() <= ps.max_leaves ); + return true; + } + + uint64_t cost( node const& n ) const + { + /* make sure the node is in the construction zone */ + assert( ntk.visited( n ) == ntk.trav_id() ); + + /* cannot expand over a constant or CI node */ + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + return std::numeric_limits::max(); + } + + /* count the number of leaves that we haven't visited */ + uint64_t cost{ 0 }; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + cost += ntk.visited( ntk.get_node( fi ) ) != ntk.trav_id(); + } ); + + /* always accept if the number of leaves does not increase */ + if ( cost < ntk.fanin_size( n ) ) + { + return cost; + } + + /* skip nodes with many fanouts */ + if ( ntk.fanout_size( n ) > ps.max_fanouts ) + { + return std::numeric_limits::max(); + } + + /* return the number of nodes that will be on the leaves if this node is removed */ + return cost; + } + +private: + Ntk const& ntk; + reconvergence_driven_cut_parameters ps; + reconvergence_driven_cut_statistics& st; + + std::vector leaves; + std::vector nodes; +}; /* reconvergence_drive_cut_impl */ + +template +class reconvergence_driven_cut_impl2 +{ +public: + using parameters_type = reconvergence_driven_cut_parameters; + using statistics_type = reconvergence_driven_cut_statistics; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit reconvergence_driven_cut_impl2( Ntk const& ntk, reconvergence_driven_cut_parameters const& ps, reconvergence_driven_cut_statistics& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + } + + std::pair, std::vector> run( std::vector const& pivots ) + { + assert( pivots.size() > 0u ); + + /* prepare for traversal and clean internal state */ + ntk.incr_trav_id(); + nodes.clear(); + leaves.clear(); + assert( nodes.empty() ); + + for ( const auto& pivot : pivots ) + { + ntk.set_visited( pivot, ntk.trav_id() ); + } + + while ( construct_cut() ) + ; + assert( leaves.size() <= ps.max_leaves ); + + /* update statistics */ + ++st.num_calls; + st.num_leaves += leaves.size(); + st.num_nodes += nodes.size(); + + return { leaves, nodes }; + } + + bool construct_cut() + { + assert( leaves.size() <= ps.max_leaves && "cut-size overflow" ); + std::stable_sort( std::begin( leaves ), std::end( leaves ), + [this]( node const& a, node const& b ) { + return cost( a ) < cost( b ); + } ); + + /* find the first non-pi node to extend the cut (because the vector is sorted, this non-pi is cost-minimal) */ + auto const it = std::find_if( std::begin( leaves ), std::end( leaves ), + [&]( node const& n ) { + return !ntk.is_ci( n ); + } ); + if ( std::end( leaves ) == it ) + { + /* if all nodes are pis, then the cut cannot be extended */ + return false; + } + + /* the cost is identical to the number of nodes added to `leaves` if *it is used to expand leaves */ + int64_t const c = cost( *it ); + if ( leaves.size() + c > ps.max_leaves ) + { + /* if the expansion exceeds the cut_size, then the cut cannot be extended */ + return false; + } + + /* otherwise expand the cut with the children of *it and mark *it visited */ + node const n = *it; + leaves.erase( it ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + node const& child = ntk.get_node( fi ); + if ( !ntk.is_constant( child ) && std::find( std::begin( leaves ), std::end( leaves ), child ) == std::end( leaves ) && ntk.visited( child ) != ntk.trav_id() ) + { + leaves.emplace_back( child ); + ntk.set_visited( child, ntk.trav_id() ); + } + } ); + + assert( leaves.size() <= ps.max_leaves ); + return true; + } + + /* counts the number of non-constant leaves */ + int64_t cost( node const& n ) const + { + int32_t current_cost = -1; + ntk.foreach_fanin( n, [&]( signal const& s ) { + auto const& child = ntk.get_node( s ); + if ( !ntk.is_constant( child ) ) + { + ++current_cost; + } + } ); + return current_cost; + } + +private: + Ntk const& ntk; + reconvergence_driven_cut_parameters ps; + reconvergence_driven_cut_statistics& st; + + std::vector leaves; + std::vector nodes; +}; /* reconvergence_drive_cut_impl2 */ + +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, std::vector> const& pivots, reconvergence_driven_cut_parameters const& ps, reconvergence_driven_cut_statistics& st ) +{ + return Impl( ntk, ps, st ).run( pivots ); +} + +} // namespace detail +/*! \endcond */ + +/*! \brief Reconvergence-driven cut towards inputs. + * + * This class implements a generation algorithm for + * reconvergence-driven cuts. The cut grows towards the primary + * inputs starting from a set of pivot nodes. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `get_node` + * - `visited` + * - `has_visited` + * - `foreach_fanin` + * + */ +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, std::vector> const& pivots, reconvergence_driven_cut_parameters const& ps = {}, reconvergence_driven_cut_statistics* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + if constexpr ( sort_equal_cost_by_level ) + { + static_assert( has_level_v, "Ntk does not implement the level method" ); + } + + using Impl = detail::reconvergence_driven_cut_impl; + + reconvergence_driven_cut_statistics st; + auto const result = detail::reconvergence_driven_cut( ntk, pivots, ps, st ); + if ( pst ) + { + *pst = st; + } + return result; +} + +/*! \brief Reconvergence-driven cut towards inputs. + * + * This class implements a generation algorithm for + * reconvergence-driven cuts. The cut grows towards the primary + * inputs starting from a single pivot node. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `get_node` + * - `visited` + * - `has_visited` + * - `foreach_fanin` + * + */ +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, node const& pivot, reconvergence_driven_cut_parameters const& ps = {}, reconvergence_driven_cut_statistics* pst = nullptr ) +{ + return reconvergence_driven_cut( ntk, std::vector>{ pivot }, ps, pst ); +} + +/*! \brief Reconvergence-driven cut towards inputs. + * + * This class implements a generation algorithm for + * reconvergence-driven cuts. The cut grows towards the primary + * inputs starting from a single pivot signal. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `get_node` + * - `visited` + * - `has_visited` + * - `foreach_fanin` + * + */ +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, signal const& pivot, reconvergence_driven_cut_parameters const& ps = {}, reconvergence_driven_cut_statistics* pst = nullptr ) +{ + return reconvergence_driven_cut( ntk, std::vector>{ ntk.get_node( pivot ) }, ps, pst ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/refactoring.hpp b/third-party/mockturtle/include/mockturtle/algorithms/refactoring.hpp new file mode 100644 index 00000000000..eafc5e02dcf --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/refactoring.hpp @@ -0,0 +1,416 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file refactoring.hpp + \brief Refactoring + + \author Alessandro Tempia Calvino + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ +#pragma once + +#include "../networks/mig.hpp" +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cut_view.hpp" +#include "../views/mffc_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "../views/color_view.hpp" +#include "cleanup.hpp" +#include "detail/mffc_utils.hpp" +#include "dont_cares.hpp" +#include "simulation.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for refactoring. + * + * The data structure `refactoring_params` holds configurable parameters with + * default arguments for `refactoring`. + */ +struct refactoring_params +{ + /*! \brief Maximum number of PIs of the MFFC or window. */ + uint32_t max_pis{ 6 }; + + /*! \brief Allow zero-gain substitutions */ + bool allow_zero_gain{ false }; + + /*! \brief Extract a reconvergence-driven cut for large MFFcs */ + bool use_reconvergence_cut{ true }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for refactoring. + * + * The data structure `refactoring_stats` provides data collected by running + * `refactoring`. + */ +struct refactoring_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for computing MFFCs. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for rewriting. */ + stopwatch<>::duration time_refactoring{ 0 }; + + /*! \brief Accumulated runtime for simulating MFFCs. */ + stopwatch<>::duration time_simulation{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] MFFC time = {:>5.2f} secs\n", to_seconds( time_mffc ) ); + std::cout << fmt::format( "[i] refactoring time = {:>5.2f} secs\n", to_seconds( time_refactoring ) ); + std::cout << fmt::format( "[i] simulation time = {:>5.2f} secs\n", to_seconds( time_simulation ) ); + } +}; + +namespace detail +{ + +template +struct has_refactoring_with_dont_cares : std::false_type +{ +}; + +template +struct has_refactoring_with_dont_cares()( std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_refactoring_with_dont_cares_v = has_refactoring_with_dont_cares::value; + +template +class refactoring_impl +{ +public: + refactoring_impl( Ntk& ntk, RefactoringFn&& refactoring_fn, refactoring_params const& ps, refactoring_stats& st, NodeCostFn const& cost_fn ) + : ntk( ntk ), refactoring_fn( refactoring_fn ), ps( ps ), st( st ), cost_fn( cost_fn ) {} + + void run() + { + progress_bar pbar{ ntk.size(), "refactoring |{0}| node = {1:>4} cand = {2:>4} est. reduction = {3:>5}", ps.progress }; + + stopwatch t( st.time_total ); + + ntk.clear_visited(); + + reconvergence_driven_cut_parameters rps; + rps.max_leaves = ps.max_pis; + reconvergence_driven_cut_statistics rst; + detail::reconvergence_driven_cut_impl reconv_cuts( ntk, rps, rst ); + + color_view color_ntk{ ntk }; + + const auto size = ntk.num_gates(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( i >= size ) + { + return false; + } + if ( ntk.fanout_size( n ) == 0u ) + { + return true; + } + + const auto mffc = make_with_stopwatch>( st.time_mffc, ntk, n ); + + pbar( i, i, _candidates, _estimated_gain ); + + if ( mffc.num_pos() == 0 || ( !ps.use_reconvergence_cut && mffc.num_pis() > ps.max_pis ) || mffc.size() < 4 ) + { + return true; + } + + kitty::dynamic_truth_table tt; + std::vector> leaves( ps.max_pis ); + uint32_t num_leaves = 0; + + if ( mffc.num_pis() <= ps.max_pis ) + { + /* use MFFC */ + mffc.foreach_pi( [&]( auto const& m, auto j ) { + leaves[j] = ntk.make_signal( m ); + } ); + + num_leaves = mffc.num_pis(); + + default_simulator sim( mffc.num_pis() ); + tt = call_with_stopwatch( st.time_simulation, + [&]() { return simulate( mffc, sim )[0]; } ); + } + else + { + /* compute a reconvergent-driven cut */ + std::vector> roots = { n }; + auto const extended_leaves = reconv_cuts.run( roots ).first; + + num_leaves = extended_leaves.size(); + assert( num_leaves <= ps.max_pis ); + + for ( auto j = 0u; j < num_leaves; ++j ) + { + leaves[j] = ntk.make_signal( extended_leaves[j] ); + } + + cut_view cut( ntk, extended_leaves, ntk.make_signal( n ) ); + default_simulator sim( num_leaves ); + tt = call_with_stopwatch( st.time_simulation, + [&]() { return simulate( cut, sim )[0]; } ); + } + + signal new_f; + bool resynthesized{ false }; + + ntk.incr_trav_id(); + int32_t gain = recursive_deref_mark( n ); + + { + if ( ps.use_dont_cares ) + { + if constexpr ( has_refactoring_with_dont_cares_v ) + { + std::vector> pivots; + for ( auto const& c : leaves ) + { + pivots.push_back( ntk.get_node( c ) ); + } + stopwatch t( st.time_refactoring ); + + refactoring_fn( ntk, tt, satisfiability_dont_cares( ntk, pivots, 16u ), leaves.begin(), leaves.begin() + num_leaves, [&]( auto const& f ) { new_f = f; resynthesized = true; return false; } ); + } + else + { + stopwatch t( st.time_refactoring ); + refactoring_fn( ntk, tt, leaves.begin(), leaves.begin() + num_leaves, [&]( auto const& f ) { new_f = f; resynthesized = true; return false; } ); + } + } + else + { + stopwatch t( st.time_refactoring ); + refactoring_fn( ntk, tt, leaves.begin(), leaves.begin() + num_leaves, [&]( auto const& f ) { new_f = f; resynthesized = true; return false; } ); + } + } + + if ( !resynthesized || n == ntk.get_node( new_f ) ) + { + recursive_ref( n ); + return true; + } + + /* ref only if it is a new node */ + if ( ntk.fanout_size( ntk.get_node( new_f ) ) == 0 ) + { + recursive_deref_check_mark( ntk.get_node( new_f ) ); + gain -= recursive_ref( ntk.get_node( new_f ) ); + } + + recursive_ref( n ); + + if ( gain > 0 || ( ps.allow_zero_gain && gain == 0 ) ) + { + ++_candidates; + _estimated_gain += gain; + ntk.substitute_node( n, new_f ); + } + else + { + /* remove */ + if ( ntk.fanout_size( ntk.get_node( new_f ) ) == 0 ) + ntk.take_out_node( ntk.get_node( new_f ) ); + } + return true; + } ); + } + +private: + uint32_t recursive_deref_mark( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + ntk.set_visited( n, ntk.trav_id() ); + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref_mark( ntk.get_node( s ) ); + } + } ); + return value; + } + + uint32_t recursive_deref_check_mark( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + if ( ntk.visited( n ) == ntk.trav_id() ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref_check_mark( ntk.get_node( s ) ); + } + } ); + return value; + } + + uint32_t recursive_ref( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.incr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_ref( ntk.get_node( s ) ); + } + } ); + return value; + } + +private: + Ntk& ntk; + RefactoringFn&& refactoring_fn; + refactoring_params const& ps; + refactoring_stats& st; + NodeCostFn cost_fn; + + uint32_t _candidates{ 0 }; + uint32_t _estimated_gain{ 0 }; +}; + +} /* namespace detail */ + +/*! \brief Boolean refactoring. + * + * This algorithm performs refactoring by collapsing maximal fanout-free cones + * (MFFCs) into truth tables and recreating a new network structure from it. + * If the MFFC is too large a reconvergence-driven cut is extracted. + * The algorithm performs changes directly in the input network and keeps the + * substituted structures dangling in the network. They can be cleaned up using + * the `cleanup_dangling` algorithm. + * + * The refactoring function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. There are + * some refactoring algorithms in the folder + * `mockturtle/algorithms/node_resyntesis`, since the resynthesis functions + * have the same signature. + * + * **Required network functions:** + * - `get_node` + * - `size` + * - `make_signal` + * - `foreach_gate` + * - `substitute_node` + * - `clear_visited` + * - `clear_values` + * - `fanout_size` + * - `set_value` + * - `foreach_node` + * + * \param ntk Input network (will be changed in-place) + * \param refactoring_fn Refactoring function + * \param ps Refactoring params + * \param pst Refactoring statistics + * \param cost_fn Node cost function (a functor with signature `uint32_t(Ntk const&, node const&)`) + */ +template> +void refactoring( Ntk& ntk, RefactoringFn&& refactoring_fn, refactoring_params const& ps = {}, refactoring_stats* pst = nullptr, NodeCostFn const& cost_fn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_clear_visited_v, "Ntk does not implement the clear_visited method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + fanout_view f_ntk{ ntk }; + + refactoring_stats st; + detail::refactoring_impl, RefactoringFn, NodeCostFn> p( f_ntk, refactoring_fn, ps, st, cost_fn ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/resubstitution.hpp b/third-party/mockturtle/include/mockturtle/algorithms/resubstitution.hpp new file mode 100644 index 00000000000..d8ed920a821 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/resubstitution.hpp @@ -0,0 +1,859 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file resubstitution.hpp + \brief Generic resubstitution framework + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" + +#include "detail/resub_utils.hpp" +#include "dont_cares.hpp" +#include "reconv_cut.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for resubstitution. + * + * The data structure `resubstitution_params` holds configurable parameters with + * default arguments for `resubstitution`. + */ +struct resubstitution_params +{ + /*! \brief Maximum number of PIs of reconvergence-driven cuts. */ + uint32_t max_pis{ 8 }; + + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 150 }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ 2 }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /****** window-based resub engine ******/ + + /*! \brief Use don't cares for optimization. Only used by window-based resub engine. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. Only used by window-based resub engine. */ + uint32_t window_size{ 12u }; + + /*! \brief Whether to prevent from increasing depth. Currently only used by window-based resub engine. */ + bool preserve_depth{ false }; + + /****** simulation-based resub engine ******/ + + /*! \brief Whether to use pre-generated patterns stored in a file. + * If not, by default, 1024 random pattern + 1x stuck-at patterns will be generated. Only used by simulation-based resub engine. + */ + std::optional pattern_filename{}; + + /*! \brief Whether to save the appended patterns (with CEXs) into file. Only used by simulation-based resub engine. */ + std::optional save_patterns{}; + + /*! \brief Maximum number of clauses of the SAT solver. Only used by simulation-based resub engine. */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Conflict limit for the SAT solver. Only used by simulation-based resub engine. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Random seed for the SAT solver (influences the randomness of counter-examples). Only used by simulation-based resub engine. */ + uint32_t random_seed{ 1 }; + + /*! \brief Whether to utilize ODC, and how many levels. 0 = no. -1 = Consider TFO until PO. Only used by simulation-based resub engine. */ + int32_t odc_levels{ 0 }; + + /*! \brief Maximum number of trials to call the resub functor. Only used by simulation-based resub engine. */ + uint32_t max_trials{ 100 }; + + /* k-resub engine specific */ + /*! \brief Maximum number of divisors to consider in k-resub engine. Only used by `abc_resub_functor` with simulation-based resub engine. */ + uint32_t max_divisors_k{ 50 }; +}; + +/*! \brief Statistics for resubstitution. + * + * The data structure `resubstitution_stats` provides data collected by running + * `resubstitution`. + */ +struct resubstitution_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime of the divisor collector. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Accumulated runtime of the resub engine. */ + stopwatch<>::duration time_resub{ 0 }; + + /*! \brief Accumulated runtime of the callback function. */ + stopwatch<>::duration time_callback{ 0 }; + + /*! \brief Total number of divisors. */ + uint64_t num_total_divisors{ 0 }; + + /*! \brief Total number of gain. */ + uint64_t estimated_gain{ 0 }; + + /*! \brief Initial network size (before resubstitution). */ + uint64_t initial_size{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] \n" ); + fmt::print( "[i] ======== Stats ========\n" ); + fmt::print( "[i] #divisors = {:8d}\n", num_total_divisors ); + fmt::print( "[i] est. gain = {:8d} ({:>5.2f}%)\n", estimated_gain, ( 100.0 * estimated_gain ) / initial_size ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] DivCollector: {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( "[i] ResubEngine : {:>5.2f} secs\n", to_seconds( time_resub ) ); + fmt::print( "[i] callback : {:>5.2f} secs\n", to_seconds( time_callback ) ); + fmt::print( "[i] =========================\n\n" ); + // clang-format on + } +}; + +namespace detail +{ + +template +bool substitute_fn( Ntk& ntk, typename Ntk::node const& n, typename Ntk::signal const& g ) +{ + ntk.substitute_node( n, g ); + return true; +} + +template +bool report_fn( Ntk& ntk, typename Ntk::node const& n, typename Ntk::signal const& g ) +{ + fmt::print( "[i] Substitute node {} with signal {}{}\n", n, ntk.is_complemented( g ) ? "!" : "", ntk.get_node( g ) ); + return false; +} + +struct default_collector_stats +{ + /*! \brief Total number of leaves. */ + uint64_t num_total_leaves{ 0 }; + + /*! \brief Accumulated runtime for cut computation. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor computation. */ + stopwatch<>::duration time_divs{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] \n" ); + fmt::print( "[i] #leaves = {:6d}\n", num_total_leaves ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] reconv. cut : {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( "[i] MFFC : {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( "[i] divs collect: {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( "[i] =========================\n\n" ); + // clang-format on + } +}; + +/*! \brief Prepare the three public data members `leaves`, `divs` and `mffc` + * to be ready for usage. + * + * `leaves`: sufficient support for all divisors + * `divs`: divisor nodes that can be used for resubstitution + * `mffc`: MFFC nodes which are needed to do simulation from + * `leaves`, through `divs` and `mffc` until the root node, + * but should be excluded from resubstitution. + * The last element of `mffc` is always the root node. + * + * `divs` and `mffc` are in topological order. + * + * \param MffcMgr Manager class to compute the potential gain if a + * resubstitution exists (number of MFFC nodes when the cost function is circuit size). + * \param MffcRes Typename of the return value of `MffcMgr`. + * \param cut_comp Manager class to compute reconvergence-driven cuts. + */ +template, typename MffcRes = uint32_t, typename cut_comp = detail::reconvergence_driven_cut_impl> +class default_divisor_collector +{ +public: + using stats = default_collector_stats; + using mffc_result_t = MffcRes; + using node = typename Ntk::node; + + using cut_comp_parameters_type = typename cut_comp::parameters_type; + using cut_comp_statistics_type = typename cut_comp::statistics_type; + +public: + explicit default_divisor_collector( Ntk const& ntk, resubstitution_params const& ps, stats& st ) + : ntk( ntk ), ps( ps ), st( st ), cuts( ntk, cut_comp_parameters_type{ ps.max_pis }, cuts_st ) + { + } + + bool run( node const& n, mffc_result_t& potential_gain ) + { + /* skip nodes with many fanouts */ + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return false; + } + + /* compute a reconvergence-driven cut */ + leaves = call_with_stopwatch( st.time_cuts, [&]() { + return cuts.run( { n } ).first; + } ); + st.num_total_leaves += leaves.size(); + + /* collect the MFFC */ + MffcMgr mffc_mgr( ntk ); + potential_gain = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.run( n, leaves, mffc ); + } ); + + /* collect the divisor nodes in the cut */ + bool div_comp_success = call_with_stopwatch( st.time_divs, [&]() { + return collect_divisors( n ); + } ); + + if ( !div_comp_success ) + { + return false; + } + + return true; + } + +private: + void collect_divisors_rec( node const& n ) + { + /* skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( const auto& f ) { + collect_divisors_rec( ntk.get_node( f ) ); + } ); + + /* collect the internal nodes */ + if ( ntk.value( n ) == 0 && n != 0 ) /* ntk.fanout_size( n ) */ + { + divs.emplace_back( n ); + } + } + + bool collect_divisors( node const& root ) + { + auto max_depth = std::numeric_limits::max(); + if ( ps.preserve_depth ) + { + max_depth = ntk.level( root ); + } + /* add the leaves of the cuts to the divisors */ + divs.clear(); + + ntk.incr_trav_id(); + for ( const auto& l : leaves ) + { + divs.emplace_back( l ); + ntk.set_visited( l, ntk.trav_id() ); + } + + /* mark nodes in the MFFC */ + for ( const auto& t : mffc ) + { + ntk.set_value( t, 1 ); + } + + /* collect the cone (without MFFC) */ + collect_divisors_rec( root ); + + /* unmark the current MFFC */ + for ( const auto& t : mffc ) + { + ntk.set_value( t, 0 ); + } + + /* check if the number of divisors is not exceeded */ + if ( divs.size() + mffc.size() - leaves.size() > ps.max_divisors - ps.max_pis ) + { + return false; + } + uint32_t limit = ps.max_divisors - ps.max_pis - mffc.size() + leaves.size(); + + /* explore the fanouts, which are not in the MFFC */ + bool quit = false; + for ( auto i = 0u; i < divs.size(); ++i ) + { + auto const d = divs.at( i ); + + if ( ntk.fanout_size( d ) > ps.skip_fanout_limit_for_divisors ) + { + continue; + } + if ( divs.size() >= limit ) + { + break; + } + + /* if the fanout has all fanins in the set, add it */ + ntk.foreach_fanout( d, [&]( node const& p ) { + if ( ntk.visited( p ) == ntk.trav_id() || ntk.level( p ) > max_depth ) + { + return true; /* next fanout */ + } + + bool all_fanins_visited = true; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.visited( ntk.get_node( g ) ) != ntk.trav_id() ) + { + all_fanins_visited = false; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( !all_fanins_visited ) + return true; /* next fanout */ + + bool has_root_as_child = false; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.get_node( g ) == root ) + { + has_root_as_child = true; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( has_root_as_child ) + { + return true; /* next fanout */ + } + + divs.emplace_back( p ); + ntk.set_visited( p, ntk.trav_id() ); + + /* quit computing divisors if there are too many of them */ + if ( divs.size() >= limit ) + { + quit = true; + return false; /* terminate fanout-loop */ + } + + return true; /* next fanout */ + } ); + + if ( quit ) + { + break; + } + } + + /* note: different from the previous version, now we do not add MFFC nodes into divs */ + assert( root == mffc.at( mffc.size() - 1u ) ); + /* note: this assertion makes sure window_simulator does not go out of bounds */ + assert( divs.size() + mffc.size() - leaves.size() <= ps.max_divisors - ps.max_pis ); + + return true; + } + +private: + Ntk const& ntk; + resubstitution_params ps; + stats& st; + + cut_comp cuts; + cut_comp_statistics_type cuts_st; + +public: + std::vector leaves; + std::vector divs; + std::vector mffc; +}; + +template +struct window_resub_stats +{ + /*! \brief Number of successful resubstitutions. */ + uint32_t num_resub{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for don't-care computation. */ + stopwatch<>::duration time_dont_care{ 0 }; + + /*! \brief Time of the resub functor. */ + stopwatch<>::duration time_compute_function{ 0 }; + + ResubFnSt functor_st; + + void report() const + { + // clang-format off + fmt::print( "[i] \n" ); + fmt::print( "[i] #resub = {:6d}\n", num_resub ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] simulation: {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( "[i] don't care: {:>5.2f} secs\n", to_seconds( time_dont_care ) ); + fmt::print( "[i] functor : {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + fmt::print( "[i] ======== Details ========\n" ); + functor_st.report(); + fmt::print( "[i] =========================\n\n" ); + // clang-format on + } +}; + +/*! \brief Window-based resubstitution engine. + * + * This engine computes the complete truth tables of nodes within a window + * with the leaves as inputs. It does not verify the resubstitution candidates + * given by the resubstitution functor. This engine requires the divisor + * collector to prepare three data members: `leaves`, `divs` and `mffc`. + * + * Required interfaces of the resubstitution functor: + * - Constructor: `resub_fn( Ntk const& ntk, Simulator const& sim,` + * `std::vector const& divs, uint32_t num_divs, ResubFnSt& st )` + * - A public `operator()`: `std::optional operator()` + * `( node const& root, TTdc care, uint32_t required, uint32_t max_inserts,` + * `MffcRes potential_gain, uint32_t& last_gain ) const` + * + * Compatible resubstitution functors implemented: + * - `default_resub_functor` + * - `aig_resub_functor` + * - `mig_resub_functor` + * - `xmg_resub_functor` + * - `xag_resub_functor` + * - `mig_resyn_functor` + * + * \param TTsim Truth table type for simulation. + * \param TTdc Truth table type for don't-care computation. + * \param ResubFn Resubstitution functor to compute the resubstitution. + * \param MffcRes Typename of `potential_gain` needed by the resubstitution functor. + */ +template, TTdc>, typename MffcRes = uint32_t> +class window_based_resub_engine +{ +public: + static constexpr bool require_leaves_and_mffc = true; + using stats = window_resub_stats; + using mffc_result_t = MffcRes; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit window_based_resub_engine( Ntk& ntk, resubstitution_params const& ps, stats& st ) + : ntk( ntk ), ps( ps ), st( st ), sim( ntk, ps.max_divisors, ps.max_pis ) + { + } + + void init() {} + void update() {} + + std::optional run( node const& n, std::vector const& leaves, std::vector const& divs, std::vector const& mffc, mffc_result_t potential_gain, uint32_t& last_gain ) + { + /* simulate the collected divisors */ + call_with_stopwatch( st.time_sim, [&]() { + simulate( leaves, divs, mffc ); + } ); + + auto care = kitty::create( ps.max_pis ); + call_with_stopwatch( st.time_dont_care, [&]() { + if ( ps.use_dont_cares ) + { + care = ~satisfiability_dont_cares( ntk, leaves, ps.window_size ); + } + else + { + care = ~care; + } + } ); + + ResubFn resub_fn( ntk, sim, divs, divs.size(), st.functor_st ); + auto res = call_with_stopwatch( st.time_compute_function, [&]() { + auto max_depth = std::numeric_limits::max(); + if ( ps.preserve_depth ) + { + max_depth = ntk.level( n ); + } + return resub_fn( n, care, max_depth, ps.max_inserts, potential_gain, last_gain ); + } ); + if ( res ) + { + ++st.num_resub; + } + return res; + } + +private: + void simulate( std::vector const& leaves, std::vector const& divs, std::vector const& mffc ) + { + sim.resize(); + for ( auto i = 0u; i < divs.size() + mffc.size(); ++i ) + { + const auto d = i < divs.size() ? divs.at( i ) : mffc.at( i - divs.size() ); + + /* skip constant 0 */ + if ( d == 0 ) + continue; + + /* assign leaves to variables */ + if ( i < leaves.size() ) + { + sim.assign( d, i + 1 ); + continue; + } + + /* compute truth tables of inner nodes */ + sim.assign( d, i - uint32_t( leaves.size() ) + ps.max_pis + 1 ); + std::vector tts; + ntk.foreach_fanin( d, [&]( const auto& s ) { + tts.emplace_back( sim.get_tt( ntk.make_signal( ntk.get_node( s ) ) ) ); /* ignore sign */ + } ); + + auto const tt = ntk.compute( d, tts.begin(), tts.end() ); + sim.set_tt( i - uint32_t( leaves.size() ) + ps.max_pis + 1, tt ); + } + + /* normalize truth tables */ + sim.normalize( divs ); + sim.normalize( mffc ); + } + +private: + Ntk& ntk; + resubstitution_params const& ps; + stats& st; + + window_simulator sim; +}; /* window_based_resub_engine */ + +/*! \brief The top-level resubstitution framework. + * + * \param ResubEngine The engine that computes the resubtitution for a given root + * node and divisors. One can choose from `window_based_resub_engine` which + * does complete simulation within small windows, or `simulation_based_resub_engine` + * which does partial simulation on the whole circuit. + * + * \param DivCollector Collects divisors near a given root node, and compute + * the potential gain (MFFC size or its variants). + * Currently only `default_divisor_collector` is implemented, but + * a frontier-based approach may be integrated in the future. + * When using `window_based_resub_engine`, the `DivCollector` should prepare + * three public data members: `leaves`, `divs`, and `mffc` (see documentation + * of `default_divisor_collector` for details). When using `simulation_based_resub_engine`, + * only `divs` is needed. + */ +template, class DivCollector = default_divisor_collector> +class resubstitution_impl +{ +public: + using engine_st_t = typename ResubEngine::stats; + using collector_st_t = typename DivCollector::stats; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using resub_callback_t = std::function; + using mffc_result_t = typename ResubEngine::mffc_result_t; + + /*! \brief Constructor of the top-level resubstitution framework. + * + * \param ntk The network to be optimized. + * \param ps Resubstitution parameters. + * \param st Top-level resubstitution statistics. + * \param engine_st Statistics of the resubstitution engine. + * \param collector_st Statistics of the divisor collector. + * \param callback Callback function when a resubstitution is found. + */ + explicit resubstitution_impl( Ntk& ntk, resubstitution_params const& ps, resubstitution_stats& st, engine_st_t& engine_st, collector_st_t& collector_st ) + : ntk( ntk ), ps( ps ), st( st ), engine_st( engine_st ), collector_st( collector_st ) + { + static_assert( std::is_same_v, "MFFC result type of the engine and the collector are different" ); + + st.initial_size = ntk.num_gates(); + + register_events(); + } + + ~resubstitution_impl() + { + ntk.events().release_add_event( add_event ); + ntk.events().release_modified_event( modified_event ); + ntk.events().release_delete_event( delete_event ); + } + + void run( resub_callback_t const& callback = substitute_fn ) + { + stopwatch t( st.time_total ); + + /* start the managers */ + DivCollector collector( ntk, ps, collector_st ); + ResubEngine resub_engine( ntk, ps, engine_st ); + call_with_stopwatch( st.time_resub, [&]() { + resub_engine.init(); + } ); + + progress_bar pbar{ ntk.size(), "resub |{0}| node = {1:>4} cand = {2:>4} est. gain = {3:>5}", ps.progress }; + + auto const size = ntk.num_gates(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( i >= size ) + { + return false; /* terminate */ + } + + pbar( i, i, candidates, st.estimated_gain ); + + /* compute cut, collect divisors, compute MFFC */ + mffc_result_t potential_gain; + const auto collector_success = call_with_stopwatch( st.time_divs, [&]() { + return collector.run( n, potential_gain ); + } ); + if ( !collector_success ) + { + return true; /* next */ + } + + /* update statistics */ + last_gain = 0; + st.num_total_divisors += collector.divs.size(); + + /* try to find a resubstitution with the divisors */ + auto g = call_with_stopwatch( st.time_resub, [&]() { + if constexpr ( ResubEngine::require_leaves_and_mffc ) /* window-based */ + { + return resub_engine.run( n, collector.leaves, collector.divs, collector.mffc, potential_gain, last_gain ); + } + else /* simulation-based */ + { + return resub_engine.run( n, collector.divs, potential_gain, last_gain ); + } + } ); + if ( !g ) + { + return true; /* next */ + } + + /* update progress bar */ + candidates++; + st.estimated_gain += last_gain; + + /* update network */ + bool updated = call_with_stopwatch( st.time_callback, [&]() { + return callback( ntk, n, *g ); + } ); + if ( updated ) + { + resub_engine.update(); + } + + return true; /* next */ + } ); + } + +private: + void register_events() + { + auto const update_level_of_new_node = [&]( const auto& n ) { + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_deleted_node = [&]( const auto& n ) { + ntk.set_level( n, -1 ); + }; + + add_event = ntk.events().register_add_event( update_level_of_new_node ); + modified_event = ntk.events().register_modified_event( update_level_of_existing_node ); + delete_event = ntk.events().register_delete_event( update_level_of_deleted_node ); + } + + /* maybe should move to depth_view */ + void update_node_level( node const& n, bool top_most = true ) + { + uint32_t curr_level = ntk.level( n ); + + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + + /* update only one more level */ + if ( top_most ) + { + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level( p, false ); + } ); + } + } + } + +private: + Ntk& ntk; + + resubstitution_params const& ps; + resubstitution_stats& st; + engine_st_t& engine_st; + collector_st_t& collector_st; + + /* temporary statistics for progress bar */ + uint32_t candidates{ 0 }; + uint32_t last_gain{ 0 }; + + /* events */ + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +} /* namespace detail */ + +/*! \brief Window-based Boolean resubstitution with default resub functor (only div0). */ +template +void default_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + if ( ps.max_pis == 8 ) + { + using truthtable_t = kitty::static_truth_table<8>; + using truthtable_dc_t = kitty::dynamic_truth_table; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + using resub_impl_t = detail::resubstitution_impl; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/aig_enumerative.hpp b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/aig_enumerative.hpp new file mode 100644 index 00000000000..44d95056f28 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/aig_enumerative.hpp @@ -0,0 +1,472 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aig_enumerative.hpp + \brief AIG enumerative resynthesis + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee + + Based on previous implementation of AIG resubstitution by + Eleonora Testa, Heinz Riener, and Mathias Soeken +*/ + +#pragma once + +#include "../../utils/index_list/index_list.hpp" +#include "../../utils/null_utils.hpp" +#include +#include +#include + +namespace mockturtle +{ + +struct aig_enumerative_resyn_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for 12-resub. */ + stopwatch<>::duration time_resub12{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Accumulated runtime for three-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted single AND-resubsitutions */ + uint64_t num_div1_and_accepts{ 0 }; + + /*! \brief Number of accepted single OR-resubsitutions */ + uint64_t num_div1_or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions using triples of unate divisors */ + uint64_t num_div12_accepts{ 0 }; + + /*! \brief Number of accepted single 2AND-resubsitutions */ + uint64_t num_div12_2and_accepts{ 0 }; + + /*! \brief Number of accepted single 2OR-resubsitutions */ + uint64_t num_div12_2or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + /*! \brief Number of accepted double AND-OR-resubsitutions */ + uint64_t num_div2_and_or_accepts{ 0 }; + + /*! \brief Number of accepted double OR-AND-resubsitutions */ + uint64_t num_div2_or_and_accepts{ 0 }; + + /*! \brief Number of accepted three resubsitutions */ + uint64_t num_div3_accepts{ 0 }; + + /*! \brief Number of accepted AND-2OR-resubsitutions */ + uint64_t num_div3_and_2or_accepts{ 0 }; + + /*! \brief Number of accepted OR-2AND-resubsitutions */ + uint64_t num_div3_or_2and_accepts{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] aig_enumerative_resyn_stats\n" ); + fmt::print( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + fmt::print( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + fmt::print( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + fmt::print( "[i] 1-resub {:6d} ({:>5.2f} secs)\n", + num_div1_accepts, to_seconds( time_resub1 ) ); + fmt::print( "[i] 2-resub {:6d} = {:6d} 2AND + {:6d} 2OR ({:>5.2f} secs)\n", + num_div12_accepts, num_div12_2and_accepts, num_div12_2or_accepts, to_seconds( time_resub12 ) ); + fmt::print( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + fmt::print( "[i] 2-resub {:6d} = {:6d} AND-OR + {:6d} OR-AND ({:>5.2f} secs)\n", + num_div2_accepts, num_div2_and_or_accepts, num_div2_or_and_accepts, to_seconds( time_resub2 ) ); + fmt::print( "[i] 3-resub {:6d} = {:6d} AND-2OR + {:6d} OR-2AND ({:>5.2f} secs)\n", + num_div3_accepts, num_div3_and_2or_accepts, num_div3_or_2and_accepts, to_seconds( time_resub3 ) ); + fmt::print( "[i] total {:6d}\n", + (num_const_accepts + num_div0_accepts + num_div1_accepts + num_div12_accepts + num_div2_accepts + num_div3_accepts) ); + // clang-format on + } +}; /* aig_enumerative_resyn_stats */ + +template +struct aig_enumerative_resyn +{ +public: + using stats = aig_enumerative_resyn_stats; + using index_list_t = xag_index_list; + using truth_table_t = TT; + +public: + explicit aig_enumerative_resyn( stats& st ) noexcept + : st( st ) + {} + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + (void)care; + assert( is_const0( ~care ) && "enumerative resynthesis does not support don't cares" ); + + index_list_t il( std::distance( begin, end ) ); + const TT ntarget = ~target; + uint32_t i, j, k, l; + iterator_type it = begin; + + /* C-resub */ + if ( kitty::is_const0( target ) ) + { + il.add_output( 0 ); + return il; + } + if constexpr ( !normalized ) + { + if ( kitty::is_const0( ntarget ) ) + { + il.add_output( 1 ); + return il; + } + } + + /* 0-resub */ + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + if ( target == tts[*it] ) + { + il.add_output( make_lit( i ) ); + return il; + } + if constexpr ( !normalized ) + { + if ( ntarget == tts[*it] ) + { + assert( !normalized ); + il.add_output( make_lit( i, true ) ); + return il; + } + } + } + + if ( max_size == 0 ) + { + return std::nullopt; + } + + /* collect unate literals */ + std::vector pos_unate, neg_unate, binate; + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + if ( kitty::implies( tts[*it], target ) ) + { + pos_unate.emplace_back( make_lit( i ) ); + } + else if ( kitty::implies( target, tts[*it] ) ) + { + neg_unate.emplace_back( make_lit( i ) ); + } + else if ( kitty::implies( target, ~tts[*it] ) ) + { + neg_unate.emplace_back( make_lit( i, true ) ); + } + else + { + if constexpr ( !normalized ) + { + if ( kitty::implies( ~tts[*it], target ) ) + { + pos_unate.emplace_back( make_lit( i, true ) ); + } + else + { + binate.emplace_back( make_lit( i ) ); + } + } + else + { + binate.emplace_back( make_lit( i ) ); + } + } + } + + /* 1-resub */ + for ( i = 0u; i < pos_unate.size(); ++i ) + { + for ( j = i + 1; j < pos_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( pos_unate[i], tts, begin ) | get_tt_from_lit( pos_unate[j], tts, begin ) ) ) + { + il.add_output( il.add_and( pos_unate[i] ^ 0x1, pos_unate[j] ^ 0x1 ) ^ 0x1 ); // OR + return il; + } + } + } + for ( i = 0u; i < neg_unate.size(); ++i ) + { + for ( j = i + 1; j < neg_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( neg_unate[i], tts, begin ) & get_tt_from_lit( neg_unate[j], tts, begin ) ) ) + { + il.add_output( il.add_and( neg_unate[i], neg_unate[j] ) ); // AND + return il; + } + } + } + + if ( max_size == 1 ) + { + return std::nullopt; + } + + /* 2-resub */ + for ( i = 0u; i < pos_unate.size(); ++i ) + { + for ( j = i + 1; j < pos_unate.size(); ++j ) + { + for ( k = j + 1; k < pos_unate.size(); ++k ) + { + if ( target == ( get_tt_from_lit( pos_unate[i], tts, begin ) | get_tt_from_lit( pos_unate[j], tts, begin ) | get_tt_from_lit( pos_unate[k], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( pos_unate[i] ^ 0x1, pos_unate[j] ^ 0x1 ), pos_unate[k] ^ 0x1 ) ^ 0x1 ); // OR-OR + return il; + } + } + } + } + + for ( i = 0u; i < neg_unate.size(); ++i ) + { + for ( j = i + 1; j < neg_unate.size(); ++j ) + { + for ( k = j + 1; k < neg_unate.size(); ++k ) + { + if ( target == ( get_tt_from_lit( neg_unate[i], tts, begin ) & get_tt_from_lit( neg_unate[j], tts, begin ) & get_tt_from_lit( neg_unate[k], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( neg_unate[i], neg_unate[j] ), neg_unate[k] ) ); // AND-AND + return il; + } + } + } + } + + /* collect binate divisors */ + std::vector> neg_binates, pos_binates; + + for ( i = 0u; i < binate.size(); ++i ) + { + if ( neg_binates.size() >= 500 && pos_binates.size() >= 500 ) + { + break; + } + for ( j = i + 1; j < binate.size(); ++j ) + { + auto const& tt_s0 = get_tt_from_lit( binate[i], tts, begin ); + auto const& tt_s1 = get_tt_from_lit( binate[j], tts, begin ); + if ( pos_binates.size() < 500 ) + { + if ( kitty::implies( tt_s0 & tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i], binate[j] ) ); + } + if ( kitty::implies( ~tt_s0 & tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ) ); + } + + if ( kitty::implies( tt_s0 & ~tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i], binate[j] ^ 0x1 ) ); + } + + if ( kitty::implies( ~tt_s0 & ~tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ^ 0x1 ) ); + } + } + if ( neg_binates.size() < 500 ) + { + if ( kitty::implies( target, tt_s0 | tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i], binate[j] ) ); + } + if ( kitty::implies( target, ~tt_s0 | tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ) ); + } + + if ( kitty::implies( target, tt_s0 | ~tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i], binate[j] ^ 0x1 ) ); + } + + if ( kitty::implies( target, ~tt_s0 | ~tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ^ 0x1 ) ); + } + } + } + } + for ( i = 0u; i < pos_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( pos_binates[i].first, tts, begin ) & get_tt_from_lit( pos_binates[i].second, tts, begin ); + for ( j = 0u; j < pos_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( pos_unate[j], tts, begin ) | tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( pos_binates[i].first, pos_binates[i].second ) ^ 0x1, pos_unate[j] ^ 0x1 ) ^ 0x1 ); // AND-OR + return il; + } + } + } + for ( i = 0u; i < neg_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( neg_binates[i].first, tts, begin ) | get_tt_from_lit( neg_binates[i].second, tts, begin ); + for ( j = 0u; j < neg_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( neg_unate[j], tts, begin ) & tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( neg_binates[i].first ^ 0x1, neg_binates[i].second ^ 0x1 ) ^ 0x1, neg_unate[j] ) ); // OR-AND + return il; + } + } + } + + if ( max_size == 2 ) + { + return std::nullopt; + } + + /* 3-resub */ + for ( i = 0u; i < neg_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( neg_binates[i].first, tts, begin ) | get_tt_from_lit( neg_binates[i].second, tts, begin ); + for ( j = i + 1; j < neg_binates.size(); ++j ) + { + if ( target == ( ( get_tt_from_lit( neg_binates[j].first, tts, begin ) | get_tt_from_lit( neg_binates[j].second, tts, begin ) ) & tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( neg_binates[i].first ^ 0x1, neg_binates[i].second ^ 0x1 ) ^ 0x1, il.add_and( neg_binates[j].first ^ 0x1, neg_binates[j].second ^ 0x1 ) ^ 0x1 ) ); // AND-2OR + return il; + } + } + } + for ( i = 0u; i < pos_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( pos_binates[i].first, tts, begin ) & get_tt_from_lit( pos_binates[i].second, tts, begin ); + for ( j = i + 1; j < pos_binates.size(); ++j ) + { + if ( target == ( ( get_tt_from_lit( pos_binates[j].first, tts, begin ) & get_tt_from_lit( pos_binates[j].second, tts, begin ) ) | tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( pos_binates[i].first, pos_binates[i].second ) ^ 0x1, il.add_and( pos_binates[j].first, pos_binates[j].second ) ^ 0x1 ) ^ 0x1 ); // OR-2AND + return il; + } + } + } + + for ( i = 0u; i < pos_unate.size(); ++i ) + { + for ( j = i + 1; j < pos_unate.size(); ++j ) + { + for ( k = j + 1; k < pos_unate.size(); ++k ) + { + for ( l = k + 1; l < pos_unate.size(); ++l ) + { + if ( target == ( get_tt_from_lit( pos_unate[i], tts, begin ) | get_tt_from_lit( pos_unate[j], tts, begin ) | get_tt_from_lit( pos_unate[k], tts, begin ) | get_tt_from_lit( pos_unate[l], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( pos_unate[i] ^ 0x1, pos_unate[j] ^ 0x1 ), il.add_and( pos_unate[k] ^ 0x1, pos_unate[l] ^ 0x1 ) ) ^ 0x1 ); // OR-2OR + return il; + } + } + } + } + } + + for ( i = 0u; i < neg_unate.size(); ++i ) + { + for ( j = i + 1; j < neg_unate.size(); ++j ) + { + for ( k = j + 1; k < neg_unate.size(); ++k ) + { + for ( l = k + 1; l < neg_unate.size(); ++l ) + { + if ( target == ( get_tt_from_lit( neg_unate[i], tts, begin ) & get_tt_from_lit( neg_unate[j], tts, begin ) & get_tt_from_lit( neg_unate[k], tts, begin ) & get_tt_from_lit( neg_unate[l], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( neg_unate[i], neg_unate[j] ), il.add_and( neg_unate[k], neg_unate[l] ) ) ); // AND-2AND + return il; + } + } + } + } + } + + if ( max_size == 3 ) + { + return std::nullopt; + } + + return std::nullopt; + } + +private: + uint32_t make_lit( uint32_t const& var, bool const& inv = false ) + { + return ( var + 1 ) * 2 + (uint32_t)inv; + } + + template + TT get_tt_from_lit( uint32_t const& lit, truth_table_storage_type const& tts, iterator_type const& begin ) + { + return ( lit % 2 ) ? ~tts[*( begin + ( lit / 2 ) - 1 )] : tts[*( begin + ( lit / 2 ) - 1 )]; + } + +private: + stats& st; +}; /* aig_enumerative_resyn */ + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/dump_resyn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/dump_resyn.hpp new file mode 100644 index 00000000000..228f0f3278f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/dump_resyn.hpp @@ -0,0 +1,120 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dump_resyn.hpp + \brief Dumps out resynthesis problems. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/null_utils.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +template +class resyn_dumper +{ +public: + using stats = null_stats; + using index_list_t = IndexList; + using truth_table_t = TT; + + explicit resyn_dumper( stats& st ) + { + (void)st; + } + + ~resyn_dumper() + { + std::cout << "avg. size = " << float( total_size ) / float( num_calls ) << "\n"; + } + + void reset_filename( std::string const& new_prefix ) + { + filename_prefix = new_prefix; + id_counter = 0; + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max(), uint32_t max_level = std::numeric_limits::max() ) + { + (void)max_level; + + std::string const filename = fmt::format( "{}{:0>{}}.resyn", filename_prefix, id_counter++, id_width ); + std::ofstream os( filename.c_str(), std::ofstream::out ); + + /* header */ + const uint32_t I = 0; + const uint32_t N = std::distance( begin, end ); + const uint32_t T = 1; + const uint32_t L = target.num_bits(); + os << fmt::format( "resyn {} {} {} {}\n", I, N, T, L ); + + /* simulation signatures */ + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + kitty::print_binary( tt, os ); + os << "\n"; + ++begin; + } + + /* target offset */ + kitty::print_binary( ~target & care, os ); + os << "\n"; + /* target onset */ + kitty::print_binary( target & care, os ); + os << "\n"; + + /* comment */ + os << fmt::format( "c\nmax size = {}\n", max_size ); + + total_size += max_size; + num_calls++; + os.close(); + return std::nullopt; + } + +private: + std::string filename_prefix = "resyn"; + uint32_t id_counter{0}; + const uint32_t id_width{3}; // width = 4 means max id = 9999 + uint32_t total_size{0}; + uint32_t num_calls{0}; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_enumerative.hpp b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_enumerative.hpp new file mode 100644 index 00000000000..b4368864114 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_enumerative.hpp @@ -0,0 +1,284 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_enumerative.hpp + \brief MIG enumerative resynthesis + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee + + Based on previous implementation of MIG resubstitution by + Eleonora Testa, Heinz Riener, and Mathias Soeken +*/ + +#pragma once + +#include "../../utils/index_list/index_list.hpp" +#include "../../utils/null_utils.hpp" +#include +#include +#include + +namespace mockturtle +{ + +template +struct mig_enumerative_resyn +{ +public: + using stats = null_stats; + using index_list_t = mig_index_list; + using truth_table_t = TT; + +public: + explicit mig_enumerative_resyn( stats& st ) noexcept + : st( st ) + { + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + (void)care; + assert( is_const0( ~care ) && "enumerative resynthesis does not support don't cares" ); + + index_list_t il( std::distance( begin, end ) ); + const TT ntarget = ~target; + uint32_t i, j, k, l; + iterator_type it = begin, jt = begin; + + /* C-resub */ + if ( kitty::is_const0( target ) ) + { + il.add_output( 0 ); + return il; + } + if ( kitty::is_const0( ntarget ) ) // unreachable if normalized + { + il.add_output( 1 ); + return il; + } + + /* 0-resub */ + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + if ( target == tts[*it] ) + { + il.add_output( make_lit( i ) ); + return il; + } + if ( ntarget == tts[*it] ) // unreachable if normalized + { + il.add_output( make_lit( i, true ) ); + return il; + } + } + + /* R-resub: doesn't work with this problem definition (need fanins of root) */ + + if ( max_size == 0 ) + { + return std::nullopt; + } + + /* collect candidate pairs using MAJ filtering rule */ + std::vector> maj1pairs; + std::vector binate; + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + for ( jt = it + 1, j = i + 1; jt != end; ++jt, ++j ) + { + if ( kitty::ternary_majority( tts[*it], tts[*jt], target ) == target ) + { + maj1pairs.emplace_back( make_lit( i ), make_lit( j ) ); + } + else if ( kitty::ternary_majority( ~tts[*it], tts[*jt], target ) == target ) + { + maj1pairs.emplace_back( make_lit( i, true ), make_lit( j ) ); + } + else if ( kitty::ternary_majority( tts[*it], ~tts[*jt], target ) == target ) + { + maj1pairs.emplace_back( make_lit( i ), make_lit( j, true ) ); + } + else if ( kitty::ternary_majority( ~tts[*it], ~tts[*jt], target ) == target ) // unreachable if normalized + { + maj1pairs.emplace_back( make_lit( i, true ), make_lit( j, true ) ); + } + else if ( std::find( binate.begin(), binate.end(), make_lit( i ) ) == binate.end() ) + { + binate.emplace_back( make_lit( i ) ); + binate.emplace_back( make_lit( i, true ) ); /* 2x redundant memory*/ + } + } + if ( kitty::implies( tts[*it], target ) ) + { + maj1pairs.emplace_back( make_lit( i ), 1 ); + } + else if ( kitty::implies( ~tts[*it], target ) ) + { + maj1pairs.emplace_back( make_lit( i, true ), 1 ); + } + else if ( kitty::implies( target, tts[*it] ) ) + { + maj1pairs.emplace_back( make_lit( i ), 0 ); + } + else if ( kitty::implies( target, ~tts[*it] ) ) + { + maj1pairs.emplace_back( make_lit( i, true ), 0 ); + } + else if ( std::find( binate.begin(), binate.end(), make_lit( i ) ) == binate.end() ) + { + binate.emplace_back( make_lit( i ) ); + binate.emplace_back( make_lit( i, true ) ); /* 2x redundant memory*/ + } + } + + /* 1-resub */ + for ( i = 0u; i < maj1pairs.size(); ++i ) + { + TT const& x = get_tt_from_lit( maj1pairs[i].first, tts, begin ); + if ( maj1pairs[i].second < 2 ) + { + for ( j = i + 1; j < maj1pairs.size(); ++j ) + { + if ( maj1pairs[i].second == 0 && target == ( x & get_tt_from_lit( maj1pairs[j].first, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].first ) ); + return il; + } + else if ( maj1pairs[i].second == 1 && target == ( x | get_tt_from_lit( maj1pairs[j].first, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].first ) ); + return il; + } + + if ( maj1pairs[j].second < 2 ) + { + continue; + } + if ( maj1pairs[i].second == 0 && target == ( x & get_tt_from_lit( maj1pairs[j].second, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + else if ( maj1pairs[i].second == 1 && target == ( x | get_tt_from_lit( maj1pairs[j].second, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + } + } + else + { + TT const& y = get_tt_from_lit( maj1pairs[i].second, tts, begin ); + for ( j = i + 1; j < maj1pairs.size(); ++j ) + { + if ( target == kitty::ternary_majority( x, y, get_tt_from_lit( maj1pairs[j].first, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].first ) ); + return il; + } + if ( maj1pairs[j].second < 2 ) + { + if ( maj1pairs[j].second == 0 && target == ( x & y ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + else if ( maj1pairs[j].second == 1 && target == ( x | y ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + } + else if ( target == kitty::ternary_majority( x, y, get_tt_from_lit( maj1pairs[j].second, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + } + } + } + + if ( max_size == 1 ) + { + return std::nullopt; + } + + /* 2-resub */ + for ( i = 0u; i < binate.size(); ++i ) + { + auto const& x = get_tt_from_lit( binate[i], tts, begin ); + for ( j = i + 2u; j < binate.size(); ++j ) + { + auto const& y = get_tt_from_lit( binate[j], tts, begin ); + for ( k = j + 2u; k < binate.size(); ++k ) + { + auto const& z = get_tt_from_lit( binate[k], tts, begin ); + auto tt_binate = kitty::ternary_majority( x, y, z ); + if ( kitty::implies( tt_binate, target ) ) /* Boolean Over-Filtering */ + { + for ( l = 0u; l < maj1pairs.size(); ++l ) + { + auto const& a = get_tt_from_lit( maj1pairs[l].first, tts, begin ); + auto tt = maj1pairs[l].second >= 2 ? kitty::ternary_majority( a, get_tt_from_lit( maj1pairs[l].second, tts, begin ), tt_binate ) : maj1pairs[l].second ? a | tt_binate + : a & tt_binate; + if ( tt == target ) + { + il.add_output( il.add_maj( maj1pairs[l].first, maj1pairs[l].second, il.add_maj( binate[i], binate[j], binate[k] ) ) ); + return il; + } + } + } + } + } + } + + if ( max_size == 2 ) + { + return std::nullopt; + } + + return std::nullopt; + } + +private: + uint32_t make_lit( uint32_t const& var, bool const& inv = false ) + { + return ( var + 1 ) * 2 + (uint32_t)inv; + } + + template + TT get_tt_from_lit( uint32_t const& lit, truth_table_storage_type const& tts, iterator_type const& begin ) + { + return ( lit % 2 ) ? ~tts[*( begin + ( lit / 2 ) - 1 )] : tts[*( begin + ( lit / 2 ) - 1 )]; + } + +private: + stats& st; +}; /* mig_enumerative_resyn */ + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_resyn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_resyn.hpp new file mode 100644 index 00000000000..271074046a6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mig_resyn.hpp @@ -0,0 +1,1344 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_resyn.hpp + \brief Implements resynthesis methods for MIGs. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/index_list/index_list.hpp" + +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +struct mig_resyn_static_params +{ + using base_type = mig_resyn_static_params; + + /*! \brief Reserved capacity for divisor truth tables (number of divisors). */ + static constexpr uint32_t reserve{ 200u }; + + /*! \brief Whether to preserve depth. */ + static constexpr bool preserve_depth{ false }; + + /*! \brief Whether the divisors have uniform costs (size and depth, whenever relevant). */ + static constexpr bool uniform_div_cost{ true }; + + /*! \brief Size cost of each MAJ gate. */ + static constexpr uint32_t size_cost_of_maj{ 1u }; + + /*! \brief Depth cost of each MAJ gate (only relevant when `preserve_depth = true`). */ + static constexpr uint32_t depth_cost_of_maj{ 1u }; + + // Future work: Consider cost for inverter / MAJ with constant input; Add XOR support (XMG) +}; + +struct mig_resyn_stats +{ + void report() const {} +}; + +/*! \brief Logic resynthesis engine for MIGs with a bottom-up approach. + * + * This algorithm resynthesizes the target function with divisor functions + * by building a chain of majority gates from bottom to top. Divisors are + * chosen as side fanins based on some scoring functions aiming at covering + * more uncovered bits. + * + */ +template +class mig_resyn_bottomup +{ +public: + using stats = mig_resyn_stats; + using index_list_t = mig_index_list; + using truth_table_t = TT; + + explicit mig_resyn_bottomup( stats& st ) + : st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !static_params::preserve_depth && static_params::uniform_div_cost, "Advanced resynthesis is not implemented for this solver" ); + divisors.reserve( static_params::reserve ); + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max(), uint32_t max_level = std::numeric_limits::max() ) + { + (void)care; + (void)max_level; + divisors.clear(); + index_list.clear(); + + num_bits = target.num_bits(); + divisors.emplace_back( ~target ); + divisors.emplace_back( target ); + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + divisors.emplace_back( tt ^ divisors[0] ); // tt XNOR target = tt XOR ~target + divisors.emplace_back( tt ^ target ); // ~tt XNOR target = tt XOR target + index_list.add_inputs(); + ++begin; + } + + return compute_function( max_size ); + } + +private: + std::optional compute_function( uint32_t num_inserts ) + { + uint64_t max_score = 0u; + max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + uint32_t score = kitty::count_ones( divisors.at( i ) ); + if ( score > max_score ) + { + max_score = score; + max_i = i; + if ( max_score == num_bits ) + { + break; + } + } + } + /* 0-resub (including constants) */ + if ( max_score == num_bits ) + { + index_list.add_output( max_i ); + return index_list; + } + + if ( num_inserts == 0u ) + { + return std::nullopt; + } + size_limit = divisors.size() + num_inserts * 2; + + return bottom_up_approach(); + } + + std::optional bottom_up_approach() + { + // maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), {max_i}} ); + TT const& function_i = divisors.at( max_i ); + current_lit = divisors.size(); + return bottom_up_approach_rec( function_i ); + } + + std::optional bottom_up_approach_rec( TT const& function_i ) + { + /* THINK: Should we consider reusing newly-built nodes (nodes in maj_nodes) in addition to divisors? */ + + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + uint64_t max_score = 0u; + max_j = 0u; + auto const not_covered_by_i = ~function_i; + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ); + uint32_t score = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( score > max_score && ( j >> 1 ) != ( max_i >> 1 ) ) + { + max_score = score; + max_j = j; + } + } + // maj_nodes.back().fanins.emplace_back( max_j ); + + /* the third fanin: only care about the disagreed bits */ + max_score = 0u; + max_k = 0u; + auto const disagree_in_ij = function_i ^ divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + uint32_t score = kitty::count_ones( divisors.at( k ) & disagree_in_ij ); + if ( score > max_score && ( k >> 1 ) != ( max_i >> 1 ) && ( k >> 1 ) != ( max_j >> 1 ) ) + { + max_score = score; + max_k = k; + } + } + // maj_nodes.back().fanins.emplace_back( max_k ); + index_list.add_maj( max_i, max_j, max_k ); + + auto const current_function = kitty::ternary_majority( function_i, divisors.at( max_j ), divisors.at( max_k ) ); + if ( kitty::is_const0( ~current_function ) ) + { + index_list.add_output( current_lit ); + return index_list; + } + else if ( current_lit + 2 < size_limit ) + { + // maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, {maj_nodes.back().id}} ); + max_i = current_lit; + current_lit += 2; + return bottom_up_approach_rec( current_function ); + } + else + { + return std::nullopt; + } + } + +private: + uint32_t size_limit; + uint32_t num_bits; + uint32_t current_lit; /* literal of the current topmost node */ + + uint32_t max_i, max_j, max_k; + + std::vector divisors; + index_list_t index_list; + + stats& st; +}; /* mig_resyn_bottomup */ + +/*! \brief Logic resynthesis engine for MIGs with top-down decomposition. + * + * This algorithm resynthesizes the target function with divisor functions + * by first building the topmost node, and then iteratively refining its + * output function by expanding a leaf with a new node. The three fanins + * of the newly-created node are chosen from the divisors based on some + * scoring functions aiming at covering more *care* bits. + * + */ +template +class mig_resyn_topdown +{ +public: + using stats = mig_resyn_stats; + using index_list_t = mig_index_list; + using truth_table_t = TT; + +private: + /*! \brief Internal data structure */ + struct expansion_position + { + int32_t parent_position = -1; // maj_nodes.at( ... ) + int32_t fanin_num = -1; // 0, 1, 2 + + bool operator==( expansion_position const& e ) const + { + return parent_position == e.parent_position && fanin_num == e.fanin_num; + } + }; + + struct maj_node + { + uint32_t id; /* maj_nodes.at( id - divisors.size() ) */ + std::vector fanins; /* ids of its three fanins */ + + std::vector fanin_functions = std::vector(); + TT care = TT(); + expansion_position parent = expansion_position(); + }; + + struct simple_maj + { + std::vector fanins; /* ids of divisors */ + TT function = TT(); /* resulting function */ + }; + +public: + explicit mig_resyn_topdown( stats& st ) + : st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !( static_params::uniform_div_cost && static_params::preserve_depth ), "If depth is to be preserved, divisor depth cost must be provided (usually not uniform)" ); + divisors.reserve( static_params::reserve ); + } + + /*! \brief Perform MIG resynthesis. + * + * `*pTTs[*begin]` must be of type `TT`. + * + * \param target Truth table of the target function. + * \param care Truth table of the care set. + * \param begin Begin iterator to divisor nodes. + * \param end End iterator to divisor nodes. + * \param tts A data structure (e.g. std::vector) that stores the truth tables of the divisor functions. + * \param max_size Maximum number of nodes allowed in the dependency circuit. + */ + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + divisors.clear(); + maj_nodes.clear(); + computed_table.clear(); + leaves.clear(); + + divisors.emplace_back( ~target ); + divisors.emplace_back( target ); + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + divisors.emplace_back( tt ^ divisors[0] ); // tt XNOR target = tt XOR ~target + divisors.emplace_back( tt ^ target ); // ~tt XNOR target = tt XOR target + ++begin; + } + scores.resize( divisors.size() ); + size_limit = max_size; + num_bits = kitty::count_ones( care ); + + return compute_function( care ); + } + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, Fn&& size_cost, uint32_t max_size = std::numeric_limits::max() ) + { + static_assert( !static_params::uniform_div_cost && !static_params::preserve_depth, "" ); + } + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, Fn&& size_cost, Fn&& depth_cost, uint32_t max_size = std::numeric_limits::max(), uint32_t max_depth = std::numeric_limits::max() ) + {} + +private: + std::optional compute_function( TT const& care ) + { + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( kitty::is_const0( ~divisors.at( i ) & care ) ) + { + /* 0-resub (including constants) */ + mig_index_list index_list( divisors.size() / 2 - 1 ); + index_list.add_output( i ); + return index_list; + } + } + + if ( size_limit == 0u ) + { + return std::nullopt; + } + + return top_down_approach( care ); + } + + std::optional top_down_approach( TT const& top_care ) + { + maj_nodes.reserve( size_limit ); + std::vector top_node_choices = construct_top( top_care ); + + if ( top_node_choices.size() == 1u && kitty::is_const0( ~top_node_choices[0].function & top_care ) ) + { + /* 1-resub */ + mig_index_list index_list( divisors.size() / 2 - 1 ); + index_list.add_maj( top_node_choices[0].fanins[0], top_node_choices[0].fanins[1], top_node_choices[0].fanins[2] ); + index_list.add_output( divisors.size() ); + return index_list; + } + // for ( simple_maj& top_node : top_node_choices ) + //{ + // if ( easy_refine( top_node, top_care, 0 ) || easy_refine( top_node, top_care, 1 ) ) + // { + // /* 1-resub */ + // mig_index_list index_list( divisors.size() / 2 - 1 ); + // index_list.add_maj( top_node.fanins[0], top_node.fanins[1], top_node.fanins[2] ); + // index_list.add_output( divisors.size() ); + // return index_list; + // } + // } + if ( size_limit == 1u ) + { + return std::nullopt; + } + + std::vector maj_nodes_best; + for ( simple_maj const& top_node : top_node_choices ) + { + // for ( int32_t i = 0; i < 3; ++i ) + { + maj_nodes.clear(); + maj_nodes.emplace_back( maj_node{ uint32_t( divisors.size() ), top_node.fanins, { divisors.at( top_node.fanins[0] ), divisors.at( top_node.fanins[1] ), divisors.at( top_node.fanins[2] ) }, top_care } ); + + leaves.clear(); + // improve_in_parent.clear(); + // shuffle.clear(); + // first_round = true; + // leaves.emplace_back( expansion_position{0, (int32_t)sibling_index( i, 1 )} ); + // leaves.emplace_back( expansion_position{0, (int32_t)sibling_index( i, 2 )} ); + leaves.emplace_back( expansion_position{ 0, 0 } ); + leaves.emplace_back( expansion_position{ 0, 1 } ); + leaves.emplace_back( expansion_position{ 0, 2 } ); + + // TT const care = top_care & ~( divisors.at( top_node.fanins[sibling_index( i, 1 )] ) & divisors.at( top_node.fanins[sibling_index( i, 2 )] ) ); + // if ( evaluate_one( care, divisors.at( top_node.fanins[i] ), expansion_position{0, i} ) ) + //{ + // /* 2-resub */ + // maj_nodes_best = maj_nodes; + // return translate( maj_nodes_best ); + // } + + if ( !refine() ) + { + continue; + } + + if ( maj_nodes_best.size() == 0u || maj_nodes.size() < maj_nodes_best.size() ) + { + maj_nodes_best = maj_nodes; + } + } + } + + if ( maj_nodes_best.size() == 0u ) + { + return std::nullopt; + } + return translate( maj_nodes_best ); + } + + bool refine() + { + // while ( ( leaves.size() != 0u || improve_in_parent.size() != 0u || shuffle.size() != 0u ) && maj_nodes.size() < size_limit ) + while ( leaves.size() != 0u && maj_nodes.size() < size_limit ) + { + // if ( leaves.size() == 0u ) + //{ + // if ( improve_in_parent.size() != 0u ) + // { + // leaves = improve_in_parent; + // improve_in_parent.clear(); + // } + // else + // { + // leaves = shuffle; + // shuffle.clear(); + // } + // first_round = false; + // } + + uint32_t min_mismatch = num_bits + 1; + uint32_t pos = 0u; + for ( int32_t i = 0; (unsigned)i < leaves.size(); ++i ) + { + maj_node& parent_node = maj_nodes.at( leaves[i].parent_position ); + uint32_t const& fi = leaves[i].fanin_num; + TT const& original_function = parent_node.fanin_functions.at( fi ); + + if ( parent_node.fanins.at( fi ) >= divisors.size() ) /* already expanded */ + { + leaves.erase( leaves.begin() + i ); + --i; + continue; + } + + TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); + if ( fulfilled( original_function, care ) /* already fulfilled */ + || care == parent_node.care /* probably cannot improve */ + ) + { + leaves.erase( leaves.begin() + i ); + --i; + continue; + } + + uint32_t const mismatch = count_ones( care & ~original_function ); + if ( mismatch < min_mismatch ) + { + pos = i; + min_mismatch = mismatch; + } + } + if ( leaves.size() == 0u ) + { + break; + } + expansion_position node_position = leaves.at( pos ); + leaves.erase( leaves.begin() + pos ); + + maj_node& parent_node = maj_nodes.at( node_position.parent_position ); + uint32_t const& fi = node_position.fanin_num; + TT const& original_function = parent_node.fanin_functions.at( fi ); + TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); + + if ( evaluate_one( care, original_function, node_position ) ) + { + return true; + } + } + return false; + } + + bool evaluate_one( TT const& care, TT const& original_function, expansion_position const& node_position ) + { + maj_node& parent_node = maj_nodes.at( node_position.parent_position ); + uint32_t const& fi = node_position.fanin_num; + + simple_maj const new_node = expand_one( care ); + uint64_t const original_score = score( original_function, care ); + uint64_t const new_score = score( new_node.function, care ); + if ( new_score <= original_score ) + { + return false; + } + + // if ( new_score == original_score ) + //{ + // if ( kitty::count_ones( new_node.function & parent_node.care ) > kitty::count_ones( original_function & parent_node.care ) ) + // { + // if ( first_round ) + // { + // /* We put it into a back-up queue for now */ + // improve_in_parent.emplace_back( node_position ); + // return false; + // } + // else + // { + // /* When there is no other possibilities, we try one in the back-up queues, and go back to the stricter state */ + // first_round = true; + // } + // } + // else if ( kitty::count_ones( new_node.function & parent_node.care ) == kitty::count_ones( original_function & parent_node.care ) && new_node.function != original_function ) + // { + // if ( first_round ) + // { + // /* We put it into a back-up queue for now */ + // shuffle.emplace_back( node_position ); + // return false; + // } + // else + // { + // /* When there is no other possibilities, we try one in the back-up queues, and go back to the stricter state */ + // first_round = true; + // } + // } + // else + // { + // return false; + // } + // } + + /* construct the new node */ + uint32_t const new_id = maj_nodes.size() + divisors.size(); + maj_nodes.emplace_back( maj_node{ new_id, new_node.fanins, { divisors.at( new_node.fanins[0] ), divisors.at( new_node.fanins[1] ), divisors.at( new_node.fanins[2] ) }, care, node_position } ); + update_fanin( parent_node, fi, new_id, new_node.function ); + + if ( kitty::is_const0( ~new_node.function & care ) ) + { + if ( node_fulfilled( maj_nodes.at( 0u ) ) ) + { + return true; + } + // TODO: add all the fulfilled nodes (trace upwards) to the divisor list, determining their indices in topological order + // This also has to be taken care of in the different runs. + } + else + { + leaves.emplace_back( expansion_position{ int32_t( maj_nodes.size() - 1 ), 0 } ); + leaves.emplace_back( expansion_position{ int32_t( maj_nodes.size() - 1 ), 1 } ); + leaves.emplace_back( expansion_position{ int32_t( maj_nodes.size() - 1 ), 2 } ); + } + return false; + } + + simple_maj expand_one( TT const& care ) + { + /* look up in computed_table */ + auto computed = computed_table.find( care ); + if ( computed != computed_table.end() ) + { + // std::cout<<"cache hit!\n"; + return computed->second; + } + + /* the first fanin: cover most care bits */ + uint64_t max_score = 0u; + uint32_t max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + scores.at( i ) = kitty::count_ones( divisors.at( i ) & care ); + if ( scores.at( i ) > max_score ) + { + max_score = scores.at( i ); + max_i = i; + } + } + + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + max_score = 0u; + uint32_t max_j = 0u; + auto const not_covered_by_i = ~divisors.at( max_i ); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ) & care; + scores.at( j ) = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( scores.at( j ) > max_score && !same_divisor( j, max_i ) ) + { + max_score = scores.at( j ); + max_j = j; + } + } + + /* the third fanin: 2 * #cover-never-covered-bits + 1 * #cover-covered-once-bits */ + max_score = 0u; + uint32_t max_k = 0u; + auto const not_covered_by_j = ~divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + auto const covered_by_k = divisors.at( k ) & care; + scores.at( k ) = kitty::count_ones( covered_by_k & not_covered_by_i ) + kitty::count_ones( covered_by_k & not_covered_by_j ); + if ( scores.at( k ) > max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + max_score = scores.at( k ); + max_k = k; + } + } + + computed_table[care] = simple_maj( { { max_i, max_j, max_k }, kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) ) } ); + return computed_table[care]; + } + + std::vector construct_top( TT const& care ) + { + std::vector res; + + /* the first fanin: cover most bits */ + uint64_t max_score = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + scores.at( i ) = kitty::count_ones( divisors.at( i ) & care ); + if ( scores.at( i ) > max_score ) + { + max_score = scores.at( i ); + } + } + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( scores.at( i ) == max_score ) + { + if ( construct_top( care, i, res ) ) + { + break; + } + } + } + return res; + } + + bool construct_top( TT const& care, uint32_t max_i, std::vector& res ) + { + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + uint64_t max_score = 0u; + auto const not_covered_by_i = ~divisors.at( max_i ); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ) & care; + scores.at( j ) = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( scores.at( j ) > max_score && !same_divisor( j, max_i ) ) + { + max_score = scores.at( j ); + } + } + for ( auto j = 0u; j < divisors.size(); ++j ) + { + if ( scores.at( j ) == max_score && !same_divisor( j, max_i ) ) + { + if ( construct_top( care, max_i, j, res ) ) + { + break; + } + } + } + return false; + } + + bool construct_top( TT const& care, uint32_t max_i, uint32_t max_j, std::vector& res ) + { + /* the third fanin: 2 * #cover-never-covered-bits + 1 * #cover-covered-once-bits */ + uint64_t max_score = 0u; + auto const not_covered_by_i = ~divisors.at( max_i ); + auto const not_covered_by_j = ~divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + auto const covered_by_k = divisors.at( k ) & care; + scores.at( k ) = kitty::count_ones( covered_by_k & not_covered_by_i ) + kitty::count_ones( covered_by_k & not_covered_by_j ); + if ( scores.at( k ) > max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + max_score = scores.at( k ); + } + } + + for ( auto k = 0u; k < divisors.size(); ++k ) + { + if ( scores.at( k ) == max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + TT const func = kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( k ) ); + if ( kitty::is_const0( ~func & care ) ) + { + res.clear(); + res.emplace_back( simple_maj( { { max_i, max_j, k }, func } ) ); + return true; + } + res.emplace_back( simple_maj( { { max_i, max_j, k }, func } ) ); + } + } + return false; + } + + /* try to replace the first (fi=0) or the second (fi=1) fanin with another divisor to improve coverage */ + // bool easy_refine( simple_maj& n, TT const& care, uint32_t fi ) + //{ + // uint64_t const original_coverage = kitty::count_ones( n.function & care ); + // uint64_t current_coverage = original_coverage; + // auto const& tt1 = divisors.at( n.fanins[fi ? 0 : 1] ); + // auto const& tt2 = divisors.at( n.fanins[fi < 2 ? 2 : 1] ); + // for ( auto i = 0u; i < divisors.size(); ++i ) + // { + // if ( same_divisor( i, n.fanins[0] ) || same_divisor( i, n.fanins[1] ) || same_divisor( i, n.fanins[2] ) ) + // { + // continue; + // } + // auto const& tti = divisors.at( i ); + // uint64_t coverage = kitty::count_ones( kitty::ternary_majority( tti, tt1, tt2 ) & care ); + // if ( coverage > current_coverage ) + // { + // current_coverage = coverage; + // n.fanins[fi] = i; + // } + // } + // if ( current_coverage > original_coverage ) + // { + // n.function = kitty::ternary_majority( divisors.at( n.fanins[0] ), divisors.at( n.fanins[1] ), divisors.at( n.fanins[2] ) ); + // if ( current_coverage == num_bits ) + // { + // return true; + // } + // } + // return false; + // } + + mig_index_list translate( std::vector const& maj_nodes_best ) const + { + mig_index_list index_list( divisors.size() / 2 - 1 ); + std::unordered_map id_map; + for ( auto i = 0u; i < maj_nodes_best.size(); ++i ) + { + auto& n = maj_nodes_best.at( maj_nodes_best.size() - i - 1u ); + uint32_t lits[3]; + for ( auto j = 0u; j < 3u; ++j ) + { + if ( n.fanins[j] < divisors.size() ) + { + lits[j] = n.fanins[j]; + } + else + { + auto mapped = id_map.find( n.fanins[j] ); + assert( mapped != id_map.end() ); + lits[j] = mapped->second; + } + } + id_map[n.id] = divisors.size() + i * 2; + index_list.add_maj( lits[0], lits[1], lits[2] ); + } + index_list.add_output( ( id_map.find( maj_nodes_best.at( 0u ).id ) )->second ); + return index_list; + } + +private: + bool same_divisor( uint32_t const i, uint32_t const j ) + { + return ( i >> 1 ) == ( j >> 1 ); + } + + bool fulfilled( TT const& func, TT const& care ) + { + return kitty::is_const0( ~func & care ); + } + + bool node_fulfilled( maj_node const& node ) + { + return fulfilled( kitty::ternary_majority( node.fanin_functions.at( 0u ), node.fanin_functions.at( 1u ), node.fanin_functions.at( 2u ) ), node.care ); + } + + uint64_t score( TT const& func, TT const& care ) + { + return kitty::count_ones( func & care ); + } + + void update_fanin( maj_node& parent_node, uint32_t const fi, uint32_t const new_id, TT const& new_function ) + { + parent_node.fanins.at( fi ) = new_id; + TT const old_function = parent_node.fanin_functions.at( fi ); + parent_node.fanin_functions.at( fi ) = new_function; + + TT const& sibling_func1 = sibling_func( parent_node, fi, 1 ); + TT const& sibling_func2 = sibling_func( parent_node, fi, 2 ); + + update_sibling( parent_node, fi, 1, old_function, new_function, sibling_func1, sibling_func2 ); + update_sibling( parent_node, fi, 2, old_function, new_function, sibling_func2, sibling_func1 ); + + /* update grandparents */ + if ( parent_node.parent.parent_position != -1 ) /* not the topmost node */ + { + update_fanin( grandparent( parent_node ), parent_node.parent.fanin_num, parent_node.id, kitty::ternary_majority( new_function, sibling_func1, sibling_func2 ) ); + } + } + + /* Deal with the affects on siblings due to a change in one fanin function + * \param parent_node The node one of whose fanin functions is changed. + * \param fi The index of the changed fanin. (0 <= fi <= 2) + * \param sibling_num Which sibling we are updating. (1 or 2) + * \param old_function The original function of the changed fanin. + * \param new_function The new function of the changed fanin. + * \param sibling_func The function of the sibling being updated. + * \param other_sibling_func The function of the other sibling. + */ + void update_sibling( maj_node const& parent_node, uint32_t const fi, uint32_t const sibling_num, TT const& old_function, TT const& new_function, TT const& sibling_func, TT const& other_sibling_func ) + { + uint32_t index = sibling_index( fi, sibling_num ); + uint32_t id = parent_node.fanins.at( index ); + TT const old_care = care( parent_node.care, old_function, other_sibling_func ); + TT const new_care = care( parent_node.care, new_function, other_sibling_func ); + + if ( old_care != new_care ) + { + /* update care of the sibling (if it is not a divisor) */ + if ( id >= divisors.size() ) + { + update_node_care( id_to_node( id ), sibling_func, old_care, new_care ); + } + else /* add the position back to queue because there may be new opportunities */ + { + add_position( expansion_position( { int32_t( id_to_pos( parent_node.id ) ), int32_t( index ) } ) ); + } + } + } + + void update_node_care( maj_node& node, TT const& func, TT const& old_care, TT const& new_care ) + { + assert( node.care == old_care ); + /* check if it was fulfilled but becomes unfulfilled */ + if ( fulfilled( func, old_care ) && !fulfilled( func, new_care ) ) + { + /* add the fanin positions back to queue */ + for ( auto fi = 0; fi < 3; ++fi ) + { + if ( node.fanins.at( fi ) < divisors.size() ) + { + add_position( expansion_position( { int32_t( id_to_pos( node.id ) ), int32_t( fi ) } ) ); + } + } + } + node.care = new_care; + + /* the update may propagate to its children */ + for ( auto fi = 0; fi < 3; ++fi ) + { + if ( node.fanins.at( fi ) >= divisors.size() ) + { + TT const old_child_care = care( old_care, sibling_func( node, fi, 1 ), sibling_func( node, fi, 2 ) ); + TT const new_child_care = care( new_care, sibling_func( node, fi, 1 ), sibling_func( node, fi, 2 ) ); + if ( old_child_care != new_child_care ) + { + update_node_care( id_to_node( node.fanins.at( fi ) ), node.fanin_functions.at( fi ), old_child_care, new_child_care ); + } + } + } + } + + void add_position( expansion_position const& pos ) + { + for ( auto& l : leaves ) + { + if ( l == pos ) + { + return; + } + } + leaves.emplace_back( pos ); + } + + TT care( TT const& parent_care, TT const& sibling_func1, TT const& sibling_func2 ) + { + return parent_care & ~( sibling_func1 & sibling_func2 ); + } + + inline maj_node& grandparent( maj_node const& parent_node ) + { + return maj_nodes.at( parent_node.parent.parent_position ); + } + + inline uint32_t sibling_index( uint32_t const my_index, uint32_t const sibling_num ) + { + return ( my_index + sibling_num ) % 3; + } + + inline TT const& sibling_func( maj_node const& parent_node, uint32_t const my_index, uint32_t const sibling_num ) + { + return parent_node.fanin_functions.at( sibling_index( my_index, sibling_num ) ); + } + + inline uint32_t id_to_pos( uint32_t const id ) + { + assert( id >= divisors.size() ); + return ( id - divisors.size() ); + } + + inline maj_node& id_to_node( uint32_t const id ) + { + return maj_nodes.at( id_to_pos( id ) ); + } + +private: + uint32_t size_limit; + uint32_t num_bits; + + std::vector divisors; + std::vector scores; + std::vector maj_nodes; /* the really used nodes */ + std::unordered_map> computed_table; /* map from care to a simple_maj with divisors as fanins */ + + std::vector leaves; //, improve_in_parent, shuffle; + // bool first_round = true; + + stats& st; +}; /* mig_resyn_topdown */ + +/*! \brief Logic resynthesis engine for MIGs by Akers' majority synthesis algorithm. + * + * This engine is a re-implementation of Akers' algorithm based on the following paper: + * + * Akers, S. B. (1962, October). Synthesis of combinational logic using + * three-input majority gates. In 3rd Annual Symposium on Switching Circuit Theory + * and Logical Design (SWCT 1962) (pp. 149-158). IEEE. + * + */ +template +class mig_resyn_akers +{ +public: + using stats = mig_resyn_stats; + using index_list_t = mig_index_list; + using TT = kitty::partial_truth_table; + using truth_table_t = TT; + + explicit mig_resyn_akers( stats& st ) + : id_to_lit( { 0, 1 } ), st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !static_params::preserve_depth && static_params::uniform_div_cost, "Advanced resynthesis is not implemented for this solver" ); + divisors.reserve( static_params::reserve ); + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max(), uint32_t max_level = std::numeric_limits::max() ) + { + (void)care; + (void)max_level; + + divisors.clear(); + id_to_lit.resize( 2 ); + index_list.clear(); + + divisors.emplace_back( ~target ); + divisors.emplace_back( target ); + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == divisors[0].num_bits() ); + id_to_lit.emplace_back( divisors.size() ); + divisors.emplace_back( tt ^ divisors[0] ); + id_to_lit.emplace_back( divisors.size() ); + divisors.emplace_back( tt ^ target ); + index_list.add_inputs(); + ++begin; + } + + return compute_function( max_size ); + } + +private: + std::optional compute_function( uint32_t num_inserts ) + { + (void)st; + if ( !is_feasible() ) + { + return std::nullopt; + } + + /* search for 0-resub (including constants) */ + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( kitty::is_const0( ~divisors[i] ) ) + { + index_list.add_output( id_to_lit[i] ); + return index_list; + } + } + + reduce(); + + while ( divisors.size() > 1 ) + { + if ( index_list.num_gates() >= num_inserts ) + { + return std::nullopt; + } + find_gate(); + add_gate(); + if ( kitty::is_const0( ~divisors.back() ) ) + { + break; + } + reduce(); + } + + index_list.add_output( id_to_lit.back() ); + return index_list; + } + + void reduce() + { + uint32_t num_bits_before = 0u; + uint32_t num_divs_before = 0u; + while ( num_bits_before != divisors[0].num_bits() || num_divs_before != divisors.size() ) + { + num_bits_before = divisors[0].num_bits(); + num_divs_before = divisors.size(); + eliminate_divs(); /* reduce column */ + eliminate_bits(); /* reduce row */ + } + } + + void eliminate_divs() + { + for ( int32_t x = 0; x < (int32_t)divisors.size(); ++x ) /* try to remove divisors[x] */ + { + if ( is_feasible( x ) ) + { + divisors.erase( divisors.begin() + x ); + id_to_lit.erase( id_to_lit.begin() + x ); + --x; + } + } + } + + void eliminate_bits() + { + /* for each pair of bits, check if we can remove i or j */ + for ( int32_t i = 0; i < (int32_t)divisors[0].num_bits() - 1; ++i ) + { + for ( int32_t j = i + 1; j < (int32_t)divisors[0].num_bits(); ++j ) + { + bool can_remove_i = true, can_remove_j = true; + for ( auto x = 0u; x < divisors.size(); ++x ) + { + if ( !kitty::get_bit( divisors[x], i ) && kitty::get_bit( divisors[x], j ) ) + { + can_remove_i = false; + } + if ( kitty::get_bit( divisors[x], i ) && !kitty::get_bit( divisors[x], j ) ) + { + can_remove_j = false; + } + if ( !can_remove_i && !can_remove_j ) + { + break; + } + } + + if ( can_remove_i ) + { + for ( auto& d : divisors ) + { + d.erase_bit_swap( i ); + } + --i; + break; /* break loop j */ + } + else if ( can_remove_j ) + { + for ( auto& d : divisors ) + { + d.erase_bit_swap( j ); + } + --j; + } + } + } + } + + void find_gate() + { + /* 1. Try if there are some gates that can eliminate some columns */ + uint32_t best_num_eliminates = 0u; + /* for each column, find candidate gates that eliminates it */ + for ( auto i = 0u; i < divisors.size(); ++i ) + { + find_gate_to_eliminate( i, best_num_eliminates ); + if ( best_num_eliminates == 3 ) + { + /* cannot be better */ + break; + } + } + if ( best_num_eliminates > 0 ) + { + return; + } + + /* 2. No gate can eliminate any column. Choose a gate that misses the least essentials */ + uint32_t least_missed_essentials = divisors[0].num_bits() + 1; + /* for all possible gates (input combinations) */ + assert( divisors.size() >= 3 ); + for ( auto i = 0u; i < divisors.size() - 2; ++i ) + { + for ( auto j = i + 1; j < divisors.size() - 1; ++j ) + { + for ( auto k = j + 1; k < divisors.size(); ++k ) + { + kitty::partial_truth_table const gate_function = kitty::ternary_majority( divisors[i], divisors[j], divisors[k] ); + uint32_t missed_essentials = 0u; + /* for each bit */ + for ( auto b = 0u; b < gate_function.num_bits(); ++b ) + { + if ( kitty::get_bit( gate_function, b ) ) + continue; + if ( is_essential( i, b ) ) + { + ++missed_essentials; + } + else if ( is_essential( j, b ) ) + { + ++missed_essentials; + } + else if ( is_essential( k, b ) ) + { + ++missed_essentials; + } + } + + if ( missed_essentials < least_missed_essentials ) + { + fanins[0] = i; + fanins[1] = j; + fanins[2] = k; + least_missed_essentials = missed_essentials; + } + } + } + } + } + + void find_gate_to_eliminate( uint32_t column, uint32_t& best_num_eliminates ) + { + std::vector> candidates; + /* for each of its essential bits */ + for ( auto b = 0u; b < divisors[0].num_bits(); ++b ) + { + if ( !is_essential( column, b ) ) + continue; + candidates.emplace_back(); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + if ( column != j && kitty::get_bit( divisors[j], b ) ) + { + candidates.back().emplace_back( j ); + } + } + if ( candidates.back().size() == 0u ) + { + /* impossible to eliminate this column */ + return; + } + } + + assert( candidates.size() >= 2 ); // why must be? but what if not? + /* try all combinations of size 2 */ + for ( auto const& j : candidates[0] ) + { + for ( auto const& k : candidates[1] ) + { + if ( j == k ) + continue; + /* check if either j or k appears in all other sets */ + bool all_satisfied = true; + for ( auto s = 2u; s < candidates.size(); ++s ) + { + bool is_in_set = false; + for ( auto const& ele : candidates[s] ) + { + if ( ele == j || ele == k ) + { + is_in_set = true; + break; + } + } + if ( !is_in_set ) + { + all_satisfied = false; + break; + } + } + if ( all_satisfied ) + { + /* this gate eliminates column */ + uint32_t num_eliminates = 1u; + /* see if it also eliminates j and/or k */ + kitty::partial_truth_table const gate_function = kitty::ternary_majority( divisors[column], divisors[j], divisors[k] ); + if ( eliminates( gate_function, j ) ) + { + ++num_eliminates; + } + if ( eliminates( gate_function, k ) ) + { + ++num_eliminates; + } + if ( num_eliminates > best_num_eliminates ) + { + fanins[0] = column; + fanins[1] = j; + fanins[2] = k; + best_num_eliminates = num_eliminates; + if ( num_eliminates == 3 ) + { + /* cannot be better */ + return; + } + } + } + } + } + } + + void add_gate() + { + index_list.add_maj( id_to_lit[fanins[0]], id_to_lit[fanins[1]], id_to_lit[fanins[2]] ); + id_to_lit.emplace_back( ( index_list.num_pis() + index_list.num_gates() ) * 2 ); + divisors.emplace_back( kitty::ternary_majority( divisors[fanins[0]], divisors[fanins[1]], divisors[fanins[2]] ) ); + } + +private: + /* whether the table is feasible (lpsd) if divisors[x] is deleted */ + bool is_feasible( int32_t x = -1 ) const + { + if ( divisors.size() == 1 && x == 0 ) + { + /* x is the only remaining column */ + return false; + } + + /* for every pair of rows _bits[i], _bits[j] */ + for ( auto i = 0u; i < divisors[0].num_bits() - 1; ++i ) + { + for ( auto j = i + 1; j < divisors[0].num_bits(); ++j ) + { + /* check if there is another divisors[y] having both bits 1 */ + bool found = false; + for ( int32_t y = 0; y < (int32_t)divisors.size(); ++y ) + { + if ( y == x ) + continue; + if ( kitty::get_bit( divisors[y], i ) && kitty::get_bit( divisors[y], j ) ) + { + found = true; + break; + } + } + if ( !found ) + { + return false; + } + } + } + return true; + } + + /* whether divisors[x]._bits[i] is essential */ + bool is_essential( uint32_t x, uint32_t i ) const + { + if ( !kitty::get_bit( divisors[x], i ) ) + { + return false; + } + + kitty::partial_truth_table tt( divisors[0].num_bits() ); + for ( auto y = 0u; y < divisors.size(); ++y ) + { + if ( x == y ) + continue; + if ( !kitty::get_bit( divisors[y], i ) ) + continue; + tt |= divisors[y]; + } + + return !kitty::is_const0( ~tt ); + } + + /* whether the gate eliminates a given column */ + bool eliminates( kitty::partial_truth_table const& gate_function, uint32_t column ) const + { + /* for each of its essential bits */ + for ( auto b = 0u; b < gate_function.num_bits(); ++b ) + { + if ( !kitty::get_bit( gate_function, b ) && is_essential( column, b ) ) + { + return false; + } + } + return true; + } + +private: + void print_table() const + { + for ( auto i = 0u; i < divisors.size(); ++i ) + { + std::cout << "[" << std::setw( 2 ) << id_to_lit[i] << "] "; + kitty::print_binary( divisors[i] ); + std::cout << "\n"; + } + } + +private: + std::vector divisors; + std::vector id_to_lit; + index_list_t index_list; + uint32_t fanins[3]; + + stats& st; +}; /* mig_resyn_akers */ +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mux_resyn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mux_resyn.hpp new file mode 100644 index 00000000000..4cda61b81e5 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/mux_resyn.hpp @@ -0,0 +1,226 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mux_resyn.hpp + \brief Implements resynthesis methods for MuxIGs. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/index_list/index_list.hpp" +#include "../../utils/null_utils.hpp" + +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Logic resynthesis engine for MuxIGs with top-down decomposition. + * + */ +template +class mux_resyn +{ +public: + using stats = null_stats; + using index_list_t = muxig_index_list; + using truth_table_t = TT; + +public: + explicit mux_resyn( stats& st ) + : st( st ) + { + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + divisors.clear(); + normalized.clear(); + + normalized.emplace_back( ~target ); // 0 XNOR target = ~target + normalized.emplace_back( target ); // 1 XNOR target = target + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + normalized.emplace_back( tt ^ normalized[0] ); // tt XNOR target = tt XOR ~target + normalized.emplace_back( tt ^ target ); // ~tt XNOR target = tt XOR target + divisors.emplace_back( tt ); + ++begin; + } + + num_bits = kitty::count_ones( care ); + remaining_size = max_size; + max_depth = std::min( 10u, max_size ); + index_list.clear(); + index_list.add_inputs( divisors.size() ); + + auto res = compute_function( care, 0 ); + if ( res ) + { + index_list.add_output( *res ); + return index_list; + } + else + { + return std::nullopt; + } + } + +private: + std::optional compute_function( TT const& care, uint32_t depth ) + { + if ( depth > max_depth ) + return std::nullopt; + + uint32_t chosen_t{0}, chosen_s{0}, chosen_e{0}, score1{0}, score2{0}, max_score{0}, min_score1{num_bits}, min_score2{num_bits}; + for ( auto t = 0u; t < normalized.size(); ++t ) + { + score1 = kitty::count_ones( normalized.at( t ) & care ); + if ( score1 > max_score ) + { + max_score = score1; + chosen_t = t; + if ( score1 == num_bits ) + { + /* 0-resub */ + return t; + } + } + } + + if ( remaining_size == 0 ) + return std::nullopt; + + TT uncovered = ~normalized.at( chosen_t ) & care; + for ( auto s = 0u; s < divisors.size(); ++s ) + { + TT& tt_s = divisors.at( s ); + TT tt_not_s = ~divisors.at( s ); + + // try positive s + score1 = kitty::count_ones( uncovered & tt_s ); + if ( score1 < min_score1 ) + { + min_score1 = score1; + chosen_s = s * 2; + min_score2 = num_bits; + } + if ( score1 == min_score1 ) + { + score2 = kitty::count_ones( tt_not_s & care ); + if ( score2 < min_score2 ) + { + min_score2 = score2; + chosen_s = s * 2; + } + } + + // try negative s + score1 = kitty::count_ones( uncovered & tt_not_s ); + if ( score1 < min_score1 ) + { + min_score1 = score1; + chosen_s = s * 2 + 1; + min_score2 = num_bits; + } + if ( score1 == min_score1 ) + { + score2 = kitty::count_ones( tt_s & care ); + if ( score2 < min_score2 ) + { + min_score2 = score2; + chosen_s = s * 2 + 1; + } + } + } + + TT tt_chosen_s = chosen_s % 2 ? ~divisors.at( chosen_s / 2 ) : divisors.at( chosen_s / 2 ); + if ( min_score2 != 0 ) + { + TT to_cover = care & ~tt_chosen_s; + max_score = 0; + for ( auto e = 0u; e < normalized.size(); ++e ) + { + score1 = kitty::count_ones( normalized.at( e ) & to_cover ); + if ( score1 > max_score ) + { + max_score = score1; + chosen_e = e; + if ( max_score == min_score2 ) // best e-child + break; + } + } + } + + if ( min_score1 != 0 ) /* expand on t-child */ + { + TT t_care = care & tt_chosen_s; + auto res = compute_function( t_care, depth + 1 ); + if ( res && remaining_size > 0 ) + chosen_t = *res; + else + return std::nullopt; + } + + if ( max_score != min_score2 ) /* expand on e-child */ + { + TT e_care = care & ~tt_chosen_s; + auto res = compute_function( e_care, depth + 1 ); + if ( res && remaining_size > 0 ) + chosen_e = *res; + else + return std::nullopt; + } + + assert( remaining_size >= 1 ); + --remaining_size; + return index_list.add_mux( chosen_s + 2, chosen_t, chosen_e ); + } + +private: + uint32_t num_bits; + uint32_t remaining_size; + uint32_t max_depth; + + std::vector divisors; + std::vector normalized; + + muxig_index_list index_list; + + stats& st; +}; /* mux_resyn */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/xag_resyn.hpp b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/xag_resyn.hpp new file mode 100644 index 00000000000..03713c121ad --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/resyn_engines/xag_resyn.hpp @@ -0,0 +1,890 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_resyn.hpp + \brief Resynthesis by recursive decomposition for AIGs or XAGs. + (based on ABC's implementation in `giaResub.c` by Alan Mishchenko) + + \author Siang-Yun Lee +*/ + +#pragma once + +#include "../../utils/index_list/index_list.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace mockturtle +{ + +struct xag_resyn_static_params +{ + using base_type = xag_resyn_static_params; + + /*! \brief Maximum number of binate divisors to be considered. */ + static constexpr uint32_t max_binates{ 50u }; + + /*! \brief Reserved capacity for divisor truth tables (number of divisors). */ + static constexpr uint32_t reserve{ 200u }; + + /*! \brief Whether to consider single XOR gates (i.e., using XAGs instead of AIGs). */ + static constexpr bool use_xor{ true }; + + /*! \brief Whether to copy truth tables. */ + static constexpr bool copy_tts{ false }; + + /*! \brief Whether to preserve depth. */ + static constexpr bool preserve_depth{ false }; + + /*! \brief Whether the divisors have uniform costs (size and depth, whenever relevant). */ + static constexpr bool uniform_div_cost{ true }; + + /*! \brief Size cost of each AND gate. */ + static constexpr uint32_t size_cost_of_and{ 1u }; + + /*! \brief Size cost of each XOR gate (only relevant when `use_xor = true`). */ + static constexpr uint32_t size_cost_of_xor{ 1u }; + + /*! \brief Depth cost of each AND gate (only relevant when `preserve_depth = true`). */ + static constexpr uint32_t depth_cost_of_and{ 1u }; + + /*! \brief Depth cost of each XOR gate (only relevant when `preserve_depth = true` and `use_xor = true`). */ + static constexpr uint32_t depth_cost_of_xor{ 1u }; + + using truth_table_storage_type = void; + using node_type = void; +}; + +template +struct xag_resyn_static_params_default : public xag_resyn_static_params +{ + using truth_table_storage_type = std::vector; + using node_type = uint32_t; +}; + +template +struct aig_resyn_static_params_default : public xag_resyn_static_params_default +{ + static constexpr bool use_xor = false; +}; + +template +struct xag_resyn_static_params_for_win_resub : public xag_resyn_static_params +{ + using truth_table_storage_type = unordered_node_map; + using node_type = typename Ntk::node; +}; + +template +struct xag_resyn_static_params_for_sim_resub : public xag_resyn_static_params +{ + using truth_table_storage_type = incomplete_node_map; + using node_type = typename Ntk::node; +}; + +template +struct aig_resyn_static_params_for_win_resub : public xag_resyn_static_params +{ + using truth_table_storage_type = unordered_node_map; + using node_type = typename Ntk::node; + static constexpr bool use_xor = false; +}; + +template +struct aig_resyn_static_params_for_sim_resub : public xag_resyn_static_params_for_sim_resub +{ + static constexpr bool use_xor = false; +}; + +struct xag_resyn_stats +{ + /*! \brief Time for finding 0-resub and collecting unate literals. */ + stopwatch<>::duration time_unate{ 0 }; + + /*! \brief Time for finding 1-resub. */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Time for finding 2-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Time for finding 3-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Time for sorting unate literals and unate pairs. */ + stopwatch<>::duration time_sort{ 0 }; + + /*! \brief Time for collecting unate pairs. */ + stopwatch<>::duration time_collect_pairs{ 0 }; + + /*! \brief Time for dividing the target and recursive call. */ + stopwatch<>::duration time_divide{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] 0-resub : {:>5.2f} secs\n", to_seconds( time_unate ) ); + fmt::print( "[i] 1-resub : {:>5.2f} secs\n", to_seconds( time_resub1 ) ); + fmt::print( "[i] 2-resub : {:>5.2f} secs\n", to_seconds( time_resub2 ) ); + fmt::print( "[i] 3-resub : {:>5.2f} secs\n", to_seconds( time_resub3 ) ); + fmt::print( "[i] sort : {:>5.2f} secs\n", to_seconds( time_sort ) ); + fmt::print( "[i] collect pairs: {:>5.2f} secs\n", to_seconds( time_collect_pairs ) ); + fmt::print( "[i] dividing : {:>5.2f} secs\n", to_seconds( time_divide ) ); + } +}; + +/*! \brief Logic resynthesis engine for AIGs or XAGs. + * + * The algorithm is based on ABC's implementation in `giaResub.c` by Alan Mishchenko. + * + * Divisors are classified as positive unate (not overlapping with target offset), + * negative unate (not overlapping with target onset), or binate (overlapping with + * both onset and offset). Furthermore, pairs of binate divisors are combined with + * an AND operation and considering all possible input polarities and again classified + * as positive unate, negative unate or binate. Simple solutions of zero cost + * (one unate divisor), one node (two unate divisors), two nodes (one unate divisor + + * one unate pair), and three nodes (two unate pairs) are exhaustively examined. + * When no simple solutions can be found, the algorithm heuristically chooses an unate + * divisor or an unate pair to divide the target function with and recursively calls + * itself to decompose the remainder function. + \verbatim embed:rst + + Example + + .. code-block:: c++ + + using TT = kitty::static_truth_table<6>; + const std::vector divisors = ...; + const node_map tts = ...; + const TT target = ..., care = ...; + xag_resyn_stats st; + xag_resyn_decompose, false, false, aig_network::node> resyn( st ); + auto result = resyn( target, care, divisors.begin(), divisors.end(), tts ); + \endverbatim + */ +template> +class xag_resyn_decompose +{ +public: + using stats = xag_resyn_stats; + using index_list_t = large_xag_index_list; + using truth_table_t = TT; + +private: + struct unate_lit + { + unate_lit( uint32_t l ) + : lit( l ) + {} + + bool operator==( unate_lit const& other ) const + { + return lit == other.lit; + } + + uint32_t lit; + uint32_t score{ 0 }; + }; + + struct fanin_pair + { + fanin_pair( uint32_t l1, uint32_t l2 ) + : lit1( l1 < l2 ? l1 : l2 ), lit2( l1 < l2 ? l2 : l1 ) + {} + + fanin_pair( uint32_t l1, uint32_t l2, bool is_xor ) + : lit1( l1 > l2 ? l1 : l2 ), lit2( l1 > l2 ? l2 : l1 ) + { + (void)is_xor; + } + + bool operator==( fanin_pair const& other ) const + { + return lit1 == other.lit1 && lit2 == other.lit2; + } + + uint32_t lit1, lit2; + uint32_t score{ 0 }; + }; + +public: + explicit xag_resyn_decompose( stats& st ) noexcept + : st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !( static_params::uniform_div_cost && static_params::preserve_depth ), "If depth is to be preserved, divisor depth cost must be provided (usually not uniform)" ); + divisors.reserve( static_params::reserve ); + } + + /*! \brief Perform XAG resynthesis. + * + * `tts[*begin]` must be of type `TT`. + * Moreover, if `static_params::copy_tts = false`, `*begin` must be of type `static_params::node_type`. + * + * \param target Truth table of the target function. + * \param care Truth table of the care set. + * \param begin Begin iterator to divisor nodes. + * \param end End iterator to divisor nodes. + * \param tts A data structure (e.g. std::vector) that stores the truth tables of the divisor functions. + * \param max_size Maximum number of nodes allowed in the dependency circuit. + */ + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, typename static_params::truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + static_assert( static_params::copy_tts || std::is_same_v::value_type, typename static_params::node_type>, "iterator_type does not dereference to static_params::node_type" ); + + ptts = &tts; + on_off_sets[0] = ~target & care; + on_off_sets[1] = target & care; + + divisors.resize( 1 ); /* clear previous data and reserve 1 dummy node for constant */ + while ( begin != end ) + { + if constexpr ( static_params::copy_tts ) + { + divisors.emplace_back( ( *ptts )[*begin] ); + } + else + { + divisors.emplace_back( *begin ); + } + ++begin; + } + + return compute_function( max_size ); + } + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, typename static_params::truth_table_storage_type const& tts, Fn&& size_cost, uint32_t max_size = std::numeric_limits::max() ) + {} + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, typename static_params::truth_table_storage_type const& tts, Fn&& size_cost, Fn&& depth_cost, uint32_t max_size = std::numeric_limits::max(), uint32_t max_depth = std::numeric_limits::max() ) + {} + +private: + std::optional compute_function( uint32_t num_inserts ) + { + index_list.clear(); + index_list.add_inputs( divisors.size() - 1 ); + auto const lit = compute_function_rec( num_inserts ); + if ( lit ) + { + assert( index_list.num_gates() <= num_inserts ); + index_list.add_output( *lit ); + return index_list; + } + return std::nullopt; + } + + std::optional compute_function_rec( uint32_t num_inserts ) + { + pos_unate_lits.clear(); + neg_unate_lits.clear(); + binate_divs.clear(); + pos_unate_pairs.clear(); + neg_unate_pairs.clear(); + + /* try 0-resub and collect unate literals */ + auto const res0 = call_with_stopwatch( st.time_unate, [&]() { + return find_one_unate(); + } ); + if ( res0 ) + { + return *res0; + } + if ( num_inserts == 0u ) + { + return std::nullopt; + } + + /* sort unate literals and try 1-resub */ + call_with_stopwatch( st.time_sort, [&]() { + sort_unate_lits( pos_unate_lits, 1 ); + sort_unate_lits( neg_unate_lits, 0 ); + } ); + auto const res1or = call_with_stopwatch( st.time_resub1, [&]() { + return find_div_div( pos_unate_lits, 1 ); + } ); + if ( res1or ) + { + return *res1or; + } + auto const res1and = call_with_stopwatch( st.time_resub1, [&]() { + return find_div_div( neg_unate_lits, 0 ); + } ); + if ( res1and ) + { + return *res1and; + } + + if ( binate_divs.size() > static_params::max_binates ) + { + binate_divs.resize( static_params::max_binates ); + } + + if constexpr ( static_params::use_xor ) + { + /* collect XOR-type unate pairs and try 1-resub with XOR */ + auto const res1xor = find_xor(); + if ( res1xor ) + { + return *res1xor; + } + } + if ( num_inserts == 1u ) + { + return std::nullopt; + } + + /* collect AND-type unate pairs and sort (both types), then try 2- and 3-resub */ + call_with_stopwatch( st.time_collect_pairs, [&]() { + collect_unate_pairs(); + } ); + call_with_stopwatch( st.time_sort, [&]() { + sort_unate_pairs( pos_unate_pairs, 1 ); + sort_unate_pairs( neg_unate_pairs, 0 ); + } ); + auto const res2or = call_with_stopwatch( st.time_resub2, [&]() { + return find_div_pair( pos_unate_lits, pos_unate_pairs, 1 ); + } ); + if ( res2or ) + { + return *res2or; + } + auto const res2and = call_with_stopwatch( st.time_resub2, [&]() { + return find_div_pair( neg_unate_lits, neg_unate_pairs, 0 ); + } ); + if ( res2and ) + { + return *res2and; + } + + if ( num_inserts >= 3u ) + { + auto const res3or = call_with_stopwatch( st.time_resub3, [&]() { + return find_pair_pair( pos_unate_pairs, 1 ); + } ); + if ( res3or ) + { + return *res3or; + } + auto const res3and = call_with_stopwatch( st.time_resub3, [&]() { + return find_pair_pair( neg_unate_pairs, 0 ); + } ); + if ( res3and ) + { + return *res3and; + } + } + + /* choose something to divide and recursive call on the remainder */ + /* Note: dividing = AND the on-set (if using positive unate) or the off-set (if using negative unate) + with the *negation* of the divisor/pair (subtracting) */ + uint32_t on_off_div, on_off_pair; + uint32_t score_div = 0, score_pair = 0; + + call_with_stopwatch( st.time_divide, [&]() { + if ( pos_unate_lits.size() > 0 ) + { + on_off_div = 1; /* use pos_lit */ + score_div = pos_unate_lits[0].score; + if ( neg_unate_lits.size() > 0 && neg_unate_lits[0].score > pos_unate_lits[0].score ) + { + on_off_div = 0; /* use neg_lit */ + score_div = neg_unate_lits[0].score; + } + } + else if ( neg_unate_lits.size() > 0 ) + { + on_off_div = 0; /* use neg_lit */ + score_div = neg_unate_lits[0].score; + } + + if ( num_inserts > 3u ) + { + if ( pos_unate_pairs.size() > 0 ) + { + on_off_pair = 1; /* use pos_pair */ + score_pair = pos_unate_pairs[0].score; + if ( neg_unate_pairs.size() > 0 && neg_unate_pairs[0].score > pos_unate_pairs[0].score ) + { + on_off_pair = 0; /* use neg_pair */ + score_pair = neg_unate_pairs[0].score; + } + } + else if ( neg_unate_pairs.size() > 0 ) + { + on_off_pair = 0; /* use neg_pair */ + score_pair = neg_unate_pairs[0].score; + } + } + } ); + + if ( score_div > score_pair / 2 ) /* divide with a divisor */ + { + /* if using pos_lit (on_off_div = 1), modify on-set and use an OR gate on top; + if using neg_lit (on_off_div = 0), modify off-set and use an AND gate on top + */ + uint32_t const lit = on_off_div ? pos_unate_lits[0].lit : neg_unate_lits[0].lit; + call_with_stopwatch( st.time_divide, [&]() { + on_off_sets[on_off_div] &= lit & 0x1 ? get_div( lit >> 1 ) : ~get_div( lit >> 1 ); + } ); + + auto const res_remain_div = compute_function_rec( num_inserts - 1 ); + if ( res_remain_div ) + { + auto const new_lit = index_list.add_and( ( lit ^ 0x1 ), *res_remain_div ^ on_off_div ); + return new_lit + on_off_div; + } + } + else if ( score_pair > 0 ) /* divide with a pair */ + { + fanin_pair const pair = on_off_pair ? pos_unate_pairs[0] : neg_unate_pairs[0]; + call_with_stopwatch( st.time_divide, [&]() { + if constexpr ( static_params::use_xor ) + { + if ( pair.lit1 > pair.lit2 ) /* XOR pair: ~(lit1 ^ lit2) = ~lit1 ^ lit2 */ + { + on_off_sets[on_off_pair] &= ( pair.lit1 & 0x1 ? get_div( pair.lit1 >> 1 ) : ~get_div( pair.lit1 >> 1 ) ) ^ ( pair.lit2 & 0x1 ? ~get_div( pair.lit2 >> 1 ) : get_div( pair.lit2 >> 1 ) ); + } + else /* AND pair: ~(lit1 & lit2) = ~lit1 | ~lit2 */ + { + on_off_sets[on_off_pair] &= ( pair.lit1 & 0x1 ? get_div( pair.lit1 >> 1 ) : ~get_div( pair.lit1 >> 1 ) ) | ( pair.lit2 & 0x1 ? get_div( pair.lit2 >> 1 ) : ~get_div( pair.lit2 >> 1 ) ); + } + } + else /* AND pair: ~(lit1 & lit2) = ~lit1 | ~lit2 */ + { + on_off_sets[on_off_pair] &= ( pair.lit1 & 0x1 ? get_div( pair.lit1 >> 1 ) : ~get_div( pair.lit1 >> 1 ) ) | ( pair.lit2 & 0x1 ? get_div( pair.lit2 >> 1 ) : ~get_div( pair.lit2 >> 1 ) ); + } + } ); + + auto const res_remain_pair = compute_function_rec( num_inserts - 2 ); + if ( res_remain_pair ) + { + uint32_t new_lit1; + if constexpr ( static_params::use_xor ) + { + new_lit1 = ( pair.lit1 > pair.lit2 ) ? index_list.add_xor( pair.lit1, pair.lit2 ) : index_list.add_and( pair.lit1, pair.lit2 ); + } + else + { + new_lit1 = index_list.add_and( pair.lit1, pair.lit2 ); + } + auto const new_lit2 = index_list.add_and( new_lit1 ^ 0x1, *res_remain_pair ^ on_off_pair ); + return new_lit2 + on_off_pair; + } + } + + return std::nullopt; + } + + /* See if there is a constant or divisor covering all on-set bits or all off-set bits. + 1. Check constant-resub + 2. Collect unate literals + 3. Find 0-resub (both positive unate and negative unate) and collect binate (neither pos nor neg unate) divisors + */ + std::optional find_one_unate() + { + num_bits[0] = kitty::count_ones( on_off_sets[0] ); /* off-set */ + num_bits[1] = kitty::count_ones( on_off_sets[1] ); /* on-set */ + if ( num_bits[0] == 0 ) + { + return 1; + } + if ( num_bits[1] == 0 ) + { + return 0; + } + + for ( auto v = 1u; v < divisors.size(); ++v ) + { + bool unateness[4] = { false, false, false, false }; + /* check intersection with off-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( v << 1 ); + unateness[0] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( v << 1 | 0x1 ); + unateness[1] = true; + } + + /* check intersection with on-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( v << 1 ); + unateness[2] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( v << 1 | 0x1 ); + unateness[3] = true; + } + + /* 0-resub */ + if ( unateness[0] && unateness[3] ) + { + return ( v << 1 ); + } + if ( unateness[1] && unateness[2] ) + { + return ( v << 1 ) + 1; + } + /* useless unate literal */ + if ( ( unateness[0] && unateness[2] ) || ( unateness[1] && unateness[3] ) ) + { + pos_unate_lits.pop_back(); + neg_unate_lits.pop_back(); + } + /* binate divisor */ + else if ( !unateness[0] && !unateness[1] && !unateness[2] && !unateness[3] ) + { + binate_divs.emplace_back( v ); + } + } + return std::nullopt; + } + + /* Sort the unate literals by the number of minterms in the intersection. + - For `pos_unate_lits`, `on_off` = 1, sort by intersection with on-set; + - For `neg_unate_lits`, `on_off` = 0, sort by intersection with off-set + */ + void sort_unate_lits( std::vector& unate_lits, uint32_t on_off ) + { + for ( auto& l : unate_lits ) + { + l.score = kitty::count_ones( ( l.lit & 0x1 ? ~get_div( l.lit >> 1 ) : get_div( l.lit >> 1 ) ) & on_off_sets[on_off] ); + } + std::stable_sort( unate_lits.begin(), unate_lits.end(), [&]( unate_lit const& l1, unate_lit const& l2 ) { + return l1.score > l2.score; // descending order + } ); + } + + void sort_unate_pairs( std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto& p : unate_pairs ) + { + if constexpr ( static_params::use_xor ) + { + p.score = ( p.lit1 > p.lit2 ) ? kitty::count_ones( ( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) ^ ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) ) & on_off_sets[on_off] ) + : kitty::count_ones( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) & ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) & on_off_sets[on_off] ); + } + else + { + p.score = kitty::count_ones( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) & ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) & on_off_sets[on_off] ); + } + } + std::stable_sort( unate_pairs.begin(), unate_pairs.end(), [&]( fanin_pair const& p1, fanin_pair const& p2 ) { + return p1.score > p2.score; // descending order + } ); + } + + /* See if there are two unate divisors covering all on-set bits or all off-set bits. + - For `pos_unate_lits`, `on_off` = 1, try covering all on-set bits by combining two with an OR gate; + - For `neg_unate_lits`, `on_off` = 0, try covering all off-set bits by combining two with an AND gate + */ + std::optional find_div_div( std::vector& unate_lits, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_lits.size(); ++i ) + { + uint32_t const& lit1 = unate_lits[i].lit; + if ( unate_lits[i].score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < unate_lits.size(); ++j ) + { + uint32_t const& lit2 = unate_lits[j].lit; + if ( unate_lits[i].score + unate_lits[j].score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? get_div( lit2 >> 1 ) : ~get_div( lit2 >> 1 ); + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + auto const new_lit = index_list.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + return new_lit + on_off; + } + } + } + return std::nullopt; + } + + std::optional find_div_pair( std::vector& unate_lits, std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_lits.size(); ++i ) + { + uint32_t const& lit1 = unate_lits[i].lit; + for ( auto j = 0u; j < unate_pairs.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs[j]; + if ( unate_lits[i].score + pair2.score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + TT ntt2; + if constexpr ( static_params::use_xor ) + { + if ( pair2.lit1 > pair2.lit2 ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + uint32_t new_lit1; + if constexpr ( static_params::use_xor ) + { + if ( pair2.lit1 > pair2.lit2 ) + { + new_lit1 = index_list.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + new_lit1 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + } + else + { + new_lit1 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + auto const new_lit2 = index_list.add_and( ( lit1 ^ 0x1 ), new_lit1 ^ 0x1 ); + return new_lit2 + on_off; + } + } + } + return std::nullopt; + } + + std::optional find_pair_pair( std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_pairs.size(); ++i ) + { + fanin_pair const& pair1 = unate_pairs[i]; + if ( pair1.score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < unate_pairs.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs[j]; + if ( pair1.score + pair2.score < num_bits[on_off] ) + { + break; + } + TT ntt1, ntt2; + if constexpr ( static_params::use_xor ) + { + if ( pair1.lit1 > pair1.lit2 ) + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) ^ ( pair1.lit2 & 0x1 ? ~get_div( pair1.lit2 >> 1 ) : get_div( pair1.lit2 >> 1 ) ); + } + else + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) | ( pair1.lit2 & 0x1 ? get_div( pair1.lit2 >> 1 ) : ~get_div( pair1.lit2 >> 1 ) ); + } + if ( pair2.lit1 > pair2.lit2 ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + } + else + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) | ( pair1.lit2 & 0x1 ? get_div( pair1.lit2 >> 1 ) : ~get_div( pair1.lit2 >> 1 ) ); + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + uint32_t fanin_lit1, fanin_lit2; + if constexpr ( static_params::use_xor ) + { + if ( pair1.lit1 > pair1.lit2 ) + { + fanin_lit1 = index_list.add_xor( pair1.lit1, pair1.lit2 ); + } + else + { + fanin_lit1 = index_list.add_and( pair1.lit1, pair1.lit2 ); + } + if ( pair2.lit1 > pair2.lit2 ) + { + fanin_lit2 = index_list.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + fanin_lit2 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + } + else + { + fanin_lit1 = index_list.add_and( pair1.lit1, pair1.lit2 ); + fanin_lit2 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + uint32_t const output_lit = index_list.add_and( fanin_lit1 ^ 0x1, fanin_lit2 ^ 0x1 ); + return output_lit + on_off; + } + } + } + return std::nullopt; + } + + std::optional find_xor() + { + /* collect XOR-type pairs (d1 ^ d2) & off = 0 or ~(d1 ^ d2) & on = 0, selecting d1, d2 from binate_divs */ + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + auto const tt_xor = get_div( binate_divs[i] ) ^ get_div( binate_divs[j] ); + bool unateness[4] = { false, false, false, false }; + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + unateness[0] = true; + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + unateness[1] = true; + } + + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + unateness[2] = true; + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + unateness[3] = true; + } + + if ( unateness[0] && unateness[2] ) + { + return index_list.add_xor( ( binate_divs[i] << 1 ), ( binate_divs[j] << 1 ) ); + } + if ( unateness[1] && unateness[3] ) + { + return index_list.add_xor( ( binate_divs[i] << 1 ) + 1, ( binate_divs[j] << 1 ) ); + } + } + } + + return std::nullopt; + } + + /* collect AND-type pairs (d1 & d2) & off = 0 or ~(d1 & d2) & on = 0, selecting d1, d2 from binate_divs */ + void collect_unate_pairs() + { + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + collect_unate_pairs_detail<1, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<1, 0>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 0>( binate_divs[i], binate_divs[j] ); + } + } + } + + template + void collect_unate_pairs_detail( uint32_t div1, uint32_t div2 ) + { + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + else if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + } + + inline TT const& get_div( uint32_t idx ) const + { + if constexpr ( static_params::copy_tts ) + { + return divisors[idx]; + } + else + { + return ( *ptts )[divisors[idx]]; + } + } + +private: + std::array on_off_sets; + std::array num_bits; /* number of bits in on-set and off-set */ + + const typename static_params::truth_table_storage_type* ptts; + std::vector> divisors; + + index_list_t index_list; + + /* positive unate: not overlapping with off-set + negative unate: not overlapping with on-set */ + std::vector pos_unate_lits, neg_unate_lits; + std::vector binate_divs; + std::vector pos_unate_pairs, neg_unate_pairs; + + stats& st; +}; /* xag_resyn_decompose */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/retiming.hpp b/third-party/mockturtle/include/mockturtle/algorithms/retiming.hpp new file mode 100644 index 00000000000..7cb070429b0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/retiming.hpp @@ -0,0 +1,808 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file retiming.hpp + \brief Retiming + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include + +namespace mockturtle +{ +/*! \brief Parameters for retiming. + * + * The data structure `retime_params` holds configurable parameters + * with default arguments for `retime`. + */ +struct retime_params +{ + /*! \brief Do forward only retiming. */ + bool forward_only{ false }; + + /*! \brief Do backward only retiming. */ + bool backward_only{ false }; + + /*! \brief Retiming max iterations. */ + uint32_t iterations{ UINT32_MAX }; + + /*! \brief Be verbose */ + bool verbose{ false }; +}; + +/*! \brief Statistics for retiming. + * + * The data structure `retime_stats` provides data collected by running + * `retime`. + */ +struct retime_stats +{ + /*! \brief Initial number of registers. */ + uint32_t registers_pre{ 0 }; + + /*! \brief Number of registers after retime. */ + uint32_t registers_post{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] Initial registers = {:7d}\t Final registers = {:7d}\n", registers_pre, registers_post ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class retime_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr uint32_t sink_node = UINT32_MAX; + +public: + explicit retime_impl( Ntk& ntk, retime_params const& ps, retime_stats& st ) + : _ntk( ntk ), + _ps( ps ), + _st( st ), + _flow_path( ntk ) + {} + +public: + void run() + { + stopwatch t( _st.time_total ); + + _st.registers_pre = _ntk.num_registers(); + + if ( !_ps.backward_only ) + { + bool improvement = true; + for ( auto i = 0; i < _ps.iterations && improvement == true; ++i ) + { + improvement = retime_area( i + 1 ); + } + } + + if ( !_ps.forward_only ) + { + bool improvement = true; + for ( auto i = 0; i < _ps.iterations && improvement == true; ++i ) + { + improvement = retime_area( i + 1 ); + } + } + + _st.registers_post = _ntk.num_registers(); + } + +private: + template + bool retime_area( uint32_t iteration ) + { + auto const num_registers_pre = _ntk.num_registers(); + + init_values(); + + auto min_cut = max_flow( iteration ); + + if ( _ps.verbose ) + { + float register_improvement = ( (float)num_registers_pre - min_cut.size() ) / num_registers_pre * 100; + std::cout << fmt::format( "[i] Retiming {}\t pre = {:7d}\t post = {:7d}\t improvement = {:>5.2f}%\n", + forward ? "forward" : "backward", num_registers_pre, min_cut.size(), register_improvement ); + } + + if ( min_cut.size() >= num_registers_pre ) + return false; + + /* move registers */ + update_registers_position( min_cut, iteration ); + + return true; + } + + template + std::vector max_flow( uint32_t iteration ) + { + uint32_t flow = 0; + + _flow_path.reset(); + _ntk.incr_trav_id(); + + /* run max flow from each register (capacity 1) */ + _ntk.foreach_register( [&]( auto const& n ) { + uint32_t local_flow; + if constexpr ( forward ) + { + local_flow = max_flow_forwards_compute_rec( _ntk.fanout( n )[0] ); + } + else + { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + local_flow = max_flow_backwards_compute_rec( fanin ); + } + + flow += local_flow; + + if ( local_flow ) + _ntk.incr_trav_id(); + + return true; + } ); + + /* run reachability */ + _ntk.incr_trav_id(); + _ntk.foreach_register( [&]( auto const& n ) { + uint32_t local_flow; + if constexpr ( forward ) + { + local_flow = max_flow_forwards_compute_rec( _ntk.fanout( n )[0] ); + } + else + { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + local_flow = max_flow_backwards_compute_rec( fanin ); + } + + assert( local_flow == 0 ); + return true; + } ); + + auto min_cut = get_min_cut(); + + // assert( check_min_cut( min_cut, iteration ) ); + + legalize_retiming( min_cut, iteration ); + + return min_cut; + } + + uint32_t max_flow_forwards_compute_rec( node const& n ) + { + uint32_t found_path = 0; + + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return 0; + + _ntk.set_visited( n, _ntk.trav_id() ); + + /* node is not in a flow path */ + if ( _flow_path[n] == 0 ) + { + /* cut boundary (sink) */ + if ( _ntk.value( n ) ) + { + _flow_path[n] = sink_node; + return 1; + } + + _ntk.foreach_fanout( n, [&]( auto const& f ) { + /* there is a path for flow */ + if ( max_flow_forwards_compute_rec( f ) ) + { + _flow_path[n] = _ntk.node_to_index( f ); + found_path = 1; + return false; + } + return true; + } ); + + return found_path; + } + + /* path has flow already, find alternative path from fanin with flow */ + node fanin_flow = 0; + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + if ( _flow_path[f] == _ntk.node_to_index( n ) ) + { + fanin_flow = _ntk.get_node( f ); + return false; + } + return true; + } ); + + if ( fanin_flow == 0 ) + return 0; + + /* augment path */ + _ntk.foreach_fanout( fanin_flow, [&]( auto const& f ) { + /* there is a path for flow */ + if ( max_flow_forwards_compute_rec( f ) ) + { + _flow_path[fanin_flow] = _ntk.node_to_index( f ); + found_path = 1; + return false; + } + return true; + } ); + + if ( found_path ) + return 1; + + if ( max_flow_forwards_compute_rec( fanin_flow ) ) + { + _flow_path[fanin_flow] = 0; + return 1; + } + + return 0; + } + + uint32_t max_flow_backwards_compute_rec( node const& n ) + { + uint32_t found_path = 0; + + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return 0; + + _ntk.set_visited( n, _ntk.trav_id() ); + + /* node is not in a flow path */ + if ( _flow_path[n] == 0 ) + { + /* cut boundary (sink) */ + if ( _ntk.value( n ) ) + { + _flow_path[n] = sink_node; + return 1; + } + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + /* there is a path for flow */ + if ( max_flow_backwards_compute_rec( _ntk.get_node( f ) ) ) + { + _flow_path[n] = _ntk.node_to_index( _ntk.get_node( f ) ); + found_path = 1; + return false; + } + return true; + } ); + + return found_path; + } + + /* path has flow already, find alternative path from fanout with flow */ + node fanout_flow = 0; + _ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( _flow_path[f] == _ntk.node_to_index( n ) ) + { + fanout_flow = _ntk.get_node( f ); + return false; + } + return true; + } ); + + if ( fanout_flow == 0 ) + return 0; + + /* augment path */ + _ntk.foreach_fanin( fanout_flow, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + /* there is a path for flow */ + if ( max_flow_backwards_compute_rec( _ntk.get_node( f ) ) ) + { + _flow_path[fanout_flow] = _ntk.node_to_index( _ntk.get_node( f ) ); + found_path = 1; + return false; + } + return true; + } ); + + if ( found_path ) + return 1; + + if ( max_flow_backwards_compute_rec( fanout_flow ) ) + { + _flow_path[fanout_flow] = 0; + return 1; + } + + return 0; + } + + std::vector get_min_cut() + { + std::vector min_cut; + min_cut.reserve( _ntk.num_registers() ); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( _flow_path[n] == 0 ) + return true; + if ( _ntk.visited( n ) != _ntk.trav_id() ) + return true; + + if ( _ntk.value( n ) || _ntk.visited( _flow_path[n] ) != _ntk.trav_id() ) + min_cut.push_back( n ); + return true; + } ); + + return min_cut; + } + + template + void legalize_retiming( std::vector& min_cut, uint32_t iteration ) + { + _ntk.clear_values(); + + _ntk.foreach_register( [&]( auto const& n ) { + _ntk.set_value( _ntk.fanout( n )[0], 1 ); + } ); + + for ( auto const& n : min_cut ) + { + rec_mark_tfi( n ); + } + + min_cut.clear(); + + if constexpr ( forward ) + { + _ntk.foreach_gate( [&]( auto const& n ) { + if ( _ntk.value( n ) == 1 ) + { + /* if is sink or before a register */ + _ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( _ntk.value( f ) != 1 ) + { + min_cut.push_back( n ); + return false; + } + return true; + } ); + } + } ); + } + else + { + _ntk.incr_trav_id(); + _ntk.foreach_register( [&]( auto const& n ) { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + collect_cut_nodes_tfi( fanin, min_cut ); + return true; + } ); + _ntk.foreach_node( [&]( auto const& n ) { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + _ntk.set_value( n, 1 ); + else + _ntk.set_value( n, 0 ); + } ); + for ( auto const& n : min_cut ) + _ntk.set_value( n, 0 ); + } + } + + void collect_cut_nodes_tfi( node const& n, std::vector& min_cut ) + { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return; + + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.value( n ) ) + { + min_cut.push_back( n ); + return; + } + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + collect_cut_nodes_tfi( _ntk.get_node( f ), min_cut ); + } ); + } + + template + void init_values() + { + _ntk.clear_values(); + + /* marks the frontiers */ + if constexpr ( forward ) + { + /* mark POs as sink */ + _ntk.foreach_po( [&]( auto const& f ) { + _ntk.set_value( _ntk.get_node( f ), 1 ); + } ); + + /* mark registers as sink */ + _ntk.foreach_register( [&]( auto const& n ) { + _ntk.set_value( n, 1 ); + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + _ntk.set_value( _ntk.get_node( f ), 1 ); + } ); + } ); + + /* exclude reachable nodes from PIs from retiming */ + _ntk.foreach_pi( [&]( auto const& n ) { + rec_mark_tfo( n ); + } ); + + /* mark childrens of marked nodes */ + std::vector to_mark; + to_mark.reserve( 200 ); + _ntk.foreach_gate( [&]( auto const& n ) { + if ( _ntk.value( n ) == 1 ) + { + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + if ( _ntk.value( _ntk.get_node( f ) ) == 0 ) + to_mark.push_back( _ntk.get_node( f ) ); + } ); + } + } ); + for ( auto const& n : to_mark ) + { + _ntk.set_value( n, 1 ); + } + } + else + { + /* mark PIs as sink */ + _ntk.foreach_pi( [&]( auto const& n ) { + _ntk.set_value( n, 1 ); + } ); + + /* mark registers as sink */ + _ntk.foreach_register( [&]( auto const& n ) { + _ntk.set_value( n, 1 ); + _ntk.foreach_fanout( n, [&]( auto const& f ) { + _ntk.set_value( f, 1 ); + } ); + } ); + + /* exclude reachable nodes from POs from retiming */ + _ntk.foreach_po( [&]( auto const& f ) { + rec_mark_tfi( _ntk.get_node( f ) ); + } ); + } + } + + template + void update_registers_position( std::vector const& min_cut, uint32_t iteration ) + { + _ntk.incr_trav_id(); + + /* create new registers and mark the ones to reuse */ + for ( auto const& n : min_cut ) + { + if constexpr ( forward ) + { + if ( _ntk.is_box_output( n ) ) + { + /* reuse the current register */ + auto node_register = _ntk.get_node( _ntk.get_fanin0( n ) ); + auto in_register = _ntk.get_node( _ntk.get_fanin0( node_register ) ); + auto in_in_register = _ntk.get_node( _ntk.get_fanin0( in_register ) ); + + /* check for marked fanouts to connect to register input */ + auto fanout = _ntk.fanout( n ); + for ( auto const& f : fanout ) + { + if ( _ntk.value( f ) ) + { + _ntk.replace_in_node( f, n, in_in_register ); + _ntk.decr_fanout_size( n ); + } + } + + _ntk.set_visited( node_register, _ntk.trav_id() ); + } + else + { + /* create a new register */ + auto const in_register = _ntk.create_box_input( _ntk.make_signal( n ) ); + auto const node_register = _ntk.create_register( in_register ); + auto const node_register_out = _ntk.create_box_output( node_register ); + + /* replace in n fanout */ + auto fanout = _ntk.fanout( n ); + for ( auto const& f : fanout ) + { + if ( f != _ntk.get_node( in_register ) && !_ntk.value( f ) ) + { + _ntk.replace_in_node( f, n, node_register_out ); + _ntk.decr_fanout_size( n ); + } + } + + _ntk.set_visited( _ntk.get_node( node_register ), _ntk.trav_id() ); + } + } + else + { + if ( _ntk.is_box_input( n ) ) + { + _ntk.foreach_fanout( n, [&]( auto const& f ) { + _ntk.set_visited( f, _ntk.trav_id() ); + } ); + } + else + { + /* create a new register */ + auto const in_register = _ntk.create_box_input( _ntk.make_signal( n ) ); + auto const node_register = _ntk.create_register( in_register ); + auto const node_register_out = _ntk.create_box_output( node_register ); + + /* replace in n fanout */ + auto fanout = _ntk.fanout( n ); + for ( auto const& f : fanout ) + { + if ( f != _ntk.get_node( in_register ) && _ntk.value( f ) ) + { + _ntk.replace_in_node( f, n, node_register_out ); + _ntk.decr_fanout_size( n ); + } + } + + _ntk.set_visited( _ntk.get_node( node_register ), _ntk.trav_id() ); + } + } + } + + /* remove retimed registers */ + _ntk.foreach_register( [&]( auto const& n ) { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return true; + + node node_register_out; + node node_register_in = _ntk.get_node( _ntk.get_fanin0( n ) ); + signal node_register_in_in = _ntk.get_fanin0( node_register_in ); + + _ntk.foreach_fanout( n, [&]( auto const& f ) { + node_register_out = f; + } ); + + auto node_register_fanout = _ntk.fanout_size( node_register_out ); + auto fanin_fanout = _ntk.fanout_size( _ntk.get_node( node_register_in_in ) ); + auto fanin_type = _ntk.is_box_output( _ntk.get_node( node_register_in_in ) ); + + _ntk.substitute_node( node_register_out, node_register_in_in ); + + return true; + } ); + } + + void rec_mark_tfo( node const& n ) + { + if ( _ntk.value( n ) ) + return; + + _ntk.set_value( n, 1 ); + _ntk.foreach_fanout( n, [&]( auto const& f ) { + rec_mark_tfo( f ); + } ); + } + + void rec_mark_tfi( node const& n ) + { + if ( _ntk.value( n ) ) + return; + + _ntk.set_value( n, 1 ); + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + rec_mark_tfi( _ntk.get_node( f ) ); + } ); + } + + template + bool check_min_cut( std::vector const& min_cut, uint32_t iteration ) + { + _ntk.incr_trav_id(); + + for ( node const& n : min_cut ) + { + _ntk.set_visited( n, _ntk.trav_id() ); + } + + bool check = true; + _ntk.foreach_register( [&]( auto const& n ) { + if constexpr ( forward ) + { + if ( !check_min_cut_rec( _ntk.fanout( n )[0] ) ) + check = false; + } + else + { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + if ( !check_min_cut_rec( fanin ) ) + check = false; + } + + return check; + } ); + + return check; + } + + template + bool check_min_cut_rec( node const& n ) + { + bool check = true; + + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return true; + + _ntk.set_visited( n, _ntk.trav_id() ); + + if constexpr ( forward ) + { + if ( _ntk.is_co( n ) ) + { + check = false; + return false; + } + + _ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( !check_min_cut_rec( f ) ) + { + check = false; + } + } ); + } + else + { + if ( _ntk.is_ci( n ) ) + { + check = false; + return false; + } + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + if ( !check_min_cut_rec( _ntk.get_node( f ) ) ) + { + check = false; + } + return check; + } ); + + return check; + } + + return check; + } + +private: + Ntk& _ntk; + retime_params const& _ps; + retime_stats& _st; + + node_map _flow_path; +}; + +} /* namespace detail */ + +/*! \brief Retiming. + * + * This function implements a retiming algorithm for registers minimization. + * The only supported network type is the `generic_network`. + * The algorithm excecutes the retiming inplace. + * + * Currently, only area-based retiming is implemented. Mixed register types + * such as (active high/low, rising/falling edge) are not supported yet. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_incr_value` + * - `has_decr_value` + * - `has_get_fanin0` + * + * \param ntk Network + * \param ps Retiming params + * \param pst Retiming statistics + * + * The implementation of this algorithm was inspired by the + * mapping command ``retime`` in ABC. + */ +template +void retime( Ntk& ntk, retime_params const& ps = {}, retime_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_get_fanin0_v, "Ntk does not implement the get_fanin0 method" ); + + retime_stats st; + + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + detail::retime_impl p( fanout_view, ps, st ); + p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/rewrite.hpp b/third-party/mockturtle/include/mockturtle/algorithms/rewrite.hpp new file mode 100644 index 00000000000..e515dae9361 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/rewrite.hpp @@ -0,0 +1,953 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file rewrite.hpp + \brief Inplace rewrite + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/color_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/window_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/rewrite_cut.hpp" +#include "reconv_cut.hpp" +#include "simulation.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for Rewrite. + * + * The data structure `rewrite_params` holds configurable parameters with + * default arguments for `rewrite`. + */ +struct rewrite_params +{ + rewrite_params() + { + /* 0 < Cut limit < 16 */ + cut_enumeration_ps.cut_limit = 8; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Cut enumeration parameters. */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief If true, candidates are only accepted if they do not increase logic depth. */ + bool preserve_depth{ false }; + + /*! \brief Allow rewrite with multiple structures */ + bool allow_multiple_structures{ true }; + + /*! \brief Allow zero-gain substitutions */ + bool allow_zero_gain{ false }; + + /*! \brief Use satisfiability don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 8u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for rewrite. + * + * The data structure `rewrite_stats` provides data collected by running + * `rewrite`. + */ +struct rewrite_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Expected gain. */ + uint32_t estimated_gain{ 0 }; + + /*! \brief Candidates */ + uint32_t candidates{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class rewrite_impl +{ + static constexpr uint32_t num_vars = 4u; + static constexpr uint32_t max_window_size = 8u; + using network_cuts_t = dynamic_network_cuts; + using cut_manager_t = detail::dynamic_cut_enumeration_impl; + using cut_t = typename network_cuts_t::cut_t; + using node_data = typename Ntk::storage::element_type::node_type; + +public: + rewrite_impl( Ntk& ntk, Library&& library, rewrite_params const& ps, rewrite_stats& st, NodeCostFn const& cost_fn ) + : ntk( ntk ), library( library ), ps( ps ), st( st ), cost_fn( cost_fn ), required( ntk, UINT32_MAX ) + { + register_events(); + } + + ~rewrite_impl() + { + if constexpr ( has_level_v ) + { + ntk.events().release_add_event( add_event ); + ntk.events().release_modified_event( modified_event ); + ntk.events().release_delete_event( delete_event ); + } + } + + void run() + { + stopwatch t( st.time_total ); + + ntk.incr_trav_id(); + + if ( ps.preserve_depth ) + { + compute_required(); + } + + if ( ps.use_dont_cares ) + perform_rewriting_dc(); + else + perform_rewriting(); + + st.estimated_gain = _estimated_gain; + st.candidates = _candidates; + } + +private: + void perform_rewriting() + { + /* initialize the cut manager */ + cut_enumeration_stats cst; + network_cuts_t cuts( ntk.size() + ( ntk.size() >> 1 ) ); + cut_manager_t cut_manager( ntk, ps.cut_enumeration_ps, cst, cuts ); + + /* initialize cuts for constant nodes and PIs */ + cut_manager.init_cuts(); + + auto& db = library.get_database(); + + std::array, num_vars> leaves; + std::array, num_vars> best_leaves; + std::array permutation; + signal best_signal; + + const auto size = ntk.size(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( ntk.fanout_size( n ) == 0u ) + return; + + int32_t best_gain = -1; + uint32_t best_level = UINT32_MAX; + bool best_phase = false; + + /* update level for node */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth ) + { + uint32_t level = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + level = std::max( level, ntk.level( ntk.get_node( f ) ) ); + } ); + ntk.set_level( n, level + 1 ); + best_level = level + 1; + } + } + + cut_manager.clear_cuts( n ); + cut_manager.compute_cuts( n ); + + uint32_t cut_index = 0; + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + /* skip trivial cut */ + if ( ( cut->size() == 1 && *cut->begin() == ntk.node_to_index( n ) ) ) + { + ++cut_index; + continue; + } + + /* Boolean matching */ + auto config = kitty::exact_npn_canonization( cuts.truth_table( *cut ) ); + auto tt_npn = std::get<0>( config ); + auto neg = std::get<1>( config ); + auto perm = std::get<2>( config ); + + auto const structures = library.get_supergates( tt_npn ); + + if ( structures == nullptr ) + { + ++cut_index; + continue; + } + + uint32_t negation = 0; + for ( auto j = 0u; j < num_vars; ++j ) + { + permutation[perm[j]] = j; + negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + + /* save output negation to apply */ + bool phase = ( neg >> num_vars == 1 ) ? true : false; + + { + auto j = 0u; + for ( auto const leaf : *cut ) + { + leaves[permutation[j++]] = ntk.make_signal( ntk.index_to_node( leaf ) ); + } + + while ( j < num_vars ) + leaves[permutation[j++]] = ntk.get_constant( false ); + } + + for ( auto j = 0u; j < num_vars; ++j ) + { + if ( ( negation >> j ) & 1 ) + { + leaves[j] = !leaves[j]; + } + } + + { + /* measure the MFFC contained in the cut */ + int32_t mffc_size = measure_mffc_deref( n, cut ); + + for ( auto const& dag : *structures ) + { + auto [nodes_added, level] = evaluate_entry( n, db.get_node( dag.root ), leaves ); + int32_t gain = mffc_size - nodes_added; + + /* discard if dag.root and n are the same */ + if ( ntk.node_to_index( n ) == db.value( db.get_node( dag.root ) ) >> 1 ) + continue; + + /* discard if no gain */ + if ( gain < 0 || ( !ps.allow_zero_gain && gain == 0 ) ) + continue; + + /* discard if level increases */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth && level > required[n] ) + continue; + } + + if ( ( gain > best_gain ) || ( gain == best_gain && level < best_level ) ) + { + ++_candidates; + best_gain = gain; + best_signal = dag.root; + best_leaves = leaves; + best_phase = phase; + best_level = level; + } + + if ( !ps.allow_multiple_structures ) + break; + } + + /* restore contained MFFC */ + measure_mffc_ref( n, cut ); + ++cut_index; + + if ( cut->size() == 0 || ( cut->size() == 1 && *cut->begin() != ntk.node_to_index( n ) ) ) + break; + } + } + + if ( best_gain > 0 || ( ps.allow_zero_gain && best_gain == 0 ) ) + { + /* replace node wth the new structure */ + topo_view topo{ db, best_signal }; + auto new_f = cleanup_dangling( topo, ntk, best_leaves.begin(), best_leaves.end() ).front(); + + assert( n != ntk.get_node( new_f ) ); + + _estimated_gain += best_gain; + ntk.substitute_node_no_restrash( n, new_f ^ best_phase ); + + if constexpr ( has_level_v ) + { + /* propagate new required to leaves */ + if ( ps.preserve_depth ) + { + propagate_required_rec( ntk.node_to_index( n ), ntk.get_node( new_f ), size, required[n] ); + assert( ntk.level( ntk.get_node( new_f ) ) <= required[n] ); + } + } + + clear_cuts_fanout_rec( cuts, cut_manager, ntk.get_node( new_f ) ); + } + } ); + } + + void perform_rewriting_dc() + { + /* initialize the cut manager */ + cut_enumeration_stats cst; + network_cuts_t cuts( ntk.size() + ( ntk.size() >> 1 ) ); + cut_manager_t cut_manager( ntk, ps.cut_enumeration_ps, cst, cuts ); + + /* initialize cuts for constant nodes and PIs */ + cut_manager.init_cuts(); + + auto& db = library.get_database(); + + std::array, num_vars> leaves; + std::array, num_vars> best_leaves; + std::array permutation; + signal best_signal; + + reconvergence_driven_cut_parameters rps; + rps.max_leaves = ps.window_size; + reconvergence_driven_cut_statistics rst; + detail::reconvergence_driven_cut_impl> reconv_cuts( ntk, rps, rst ); + unordered_node_map, Ntk> tts( ntk ); + + color_view color_ntk{ ntk }; + std::array divisors; + for ( uint32_t i = 0; i < num_vars; ++i ) + { + divisors[i] = i; + } + + const auto size = ntk.size(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( ntk.fanout_size( n ) == 0u ) + return; + + int32_t best_gain = -1; + uint32_t best_level = UINT32_MAX; + bool best_phase = false; + + /* update level for node */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth ) + { + uint32_t level = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + level = std::max( level, ntk.level( ntk.get_node( f ) ) ); + } ); + ntk.set_level( n, level + 1 ); + best_level = level + 1; + } + } + + cut_manager.clear_cuts( n ); + cut_manager.compute_cuts( n ); + + /* compute window */ + std::vector> roots = { n }; + auto const extended_leaves = reconv_cuts.run( roots ).first; + std::vector> gates{ collect_nodes( color_ntk, extended_leaves, roots ) }; + window_view window_ntk{ color_ntk, extended_leaves, roots, gates }; + + default_simulator> sim; + tts.reset(); + simulate_nodes_with_node_map>( window_ntk, tts, sim ); + + uint32_t cut_index = 0; + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + /* skip trivial cut */ + if ( ( cut->size() == 1 && *cut->begin() == ntk.node_to_index( n ) ) ) + { + ++cut_index; + continue; + } + + /* Boolean matching */ + auto config = kitty::exact_npn_canonization( cuts.truth_table( *cut ) ); + auto tt_npn = std::get<0>( config ); + auto neg = std::get<1>( config ); + auto perm = std::get<2>( config ); + + kitty::static_truth_table care; + + bool containment = true; + for ( auto const& l : *cut ) + { + if ( color_ntk.color( ntk.index_to_node( l ) ) != color_ntk.current_color() ) + { + containment = false; + break; + } + } + + if ( containment ) + { + /* compute care set */ + for ( auto i = 0u; i < ( 1u << window_ntk.num_pis() ); ++i ) + { + uint32_t entry{ 0u }; + auto j = 0u; + for ( auto const& l : *cut ) + { + entry |= kitty::get_bit( tts[l], i ) << j; + ++j; + } + kitty::set_bit( care, entry ); + } + } + else + { + /* completely specified */ + care = ~care; + } + + auto const dc_npn = apply_npn_transformation( ~care, neg & ~( 1 << num_vars ), perm ); + auto const structures = library.get_supergates( tt_npn, dc_npn, neg, perm ); + + if ( structures == nullptr ) + { + ++cut_index; + continue; + } + + uint32_t negation = 0; + for ( auto j = 0u; j < num_vars; ++j ) + { + permutation[perm[j]] = j; + negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + + /* save output negation to apply */ + bool phase = ( neg >> num_vars == 1 ) ? true : false; + + { + auto j = 0u; + for ( auto const leaf : *cut ) + { + leaves[permutation[j++]] = ntk.make_signal( ntk.index_to_node( leaf ) ); + } + + while ( j < num_vars ) + leaves[permutation[j++]] = ntk.get_constant( false ); + } + + for ( auto j = 0u; j < num_vars; ++j ) + { + if ( ( negation >> j ) & 1 ) + { + leaves[j] = !leaves[j]; + } + } + + { + /* measure the MFFC contained in the cut */ + int32_t mffc_size = measure_mffc_deref( n, cut ); + + for ( auto const& dag : *structures ) + { + auto [nodes_added, level] = evaluate_entry( n, db.get_node( dag.root ), leaves ); + int32_t gain = mffc_size - nodes_added; + + /* discard if dag.root and n are the same */ + if ( ntk.node_to_index( n ) == db.value( db.get_node( dag.root ) ) >> 1 ) + continue; + + /* discard if no gain */ + if ( gain < 0 || ( !ps.allow_zero_gain && gain == 0 ) ) + continue; + + /* discard if level increases */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth && level > required[n] ) + continue; + } + + if ( ( gain > best_gain ) || ( gain == best_gain && level < best_level ) ) + { + ++_candidates; + best_gain = gain; + best_signal = dag.root; + best_leaves = leaves; + best_phase = phase; + best_level = level; + } + + if ( !ps.allow_multiple_structures ) + break; + } + + /* restore contained MFFC */ + measure_mffc_ref( n, cut ); + ++cut_index; + + if ( cut->size() == 0 || ( cut->size() == 1 && *cut->begin() != ntk.node_to_index( n ) ) ) + break; + } + } + + if ( best_gain > 0 || ( ps.allow_zero_gain && best_gain == 0 ) ) + { + /* replace node wth the new structure */ + topo_view topo{ db, best_signal }; + auto new_f = cleanup_dangling( topo, ntk, best_leaves.begin(), best_leaves.end() ).front(); + + assert( n != ntk.get_node( new_f ) ); + + _estimated_gain += best_gain; + ntk.substitute_node_no_restrash( n, new_f ^ best_phase ); + + if constexpr ( has_level_v ) + { + /* propagate new required to leaves */ + if ( ps.preserve_depth ) + { + propagate_required_rec( ntk.node_to_index( n ), ntk.get_node( new_f ), size, required[n] ); + assert( ntk.level( ntk.get_node( new_f ) ) <= required[n] ); + } + } + + clear_cuts_fanout_rec( cuts, cut_manager, ntk.get_node( new_f ) ); + } + } ); + } + + int32_t measure_mffc_ref( node const& n, cut_t const* cut ) + { + /* reference cut leaves */ + for ( auto leaf : *cut ) + { + ntk.incr_fanout_size( ntk.index_to_node( leaf ) ); + } + + int32_t mffc_size = static_cast( recursive_ref( n ) ); + + /* dereference leaves */ + for ( auto leaf : *cut ) + { + ntk.decr_fanout_size( ntk.index_to_node( leaf ) ); + } + + return mffc_size; + } + + int32_t measure_mffc_deref( node const& n, cut_t const* cut ) + { + /* reference cut leaves */ + for ( auto leaf : *cut ) + { + ntk.incr_fanout_size( ntk.index_to_node( leaf ) ); + } + + int32_t mffc_size = static_cast( recursive_deref( n ) ); + + /* dereference leaves */ + for ( auto leaf : *cut ) + { + ntk.decr_fanout_size( ntk.index_to_node( leaf ) ); + } + + return mffc_size; + } + + uint32_t recursive_deref( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref( ntk.get_node( s ) ); + } + } ); + return value; + } + + uint32_t recursive_ref( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.incr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_ref( ntk.get_node( s ) ); + } + } ); + return value; + } + + inline std::pair evaluate_entry( node const& current_root, node const& n, std::array, num_vars> const& leaves ) + { + auto& db = library.get_database(); + db.incr_trav_id(); + + return evaluate_entry_rec( current_root, n, leaves ); + } + + std::pair evaluate_entry_rec( node const& current_root, node const& n, std::array, num_vars> const& leaves ) + { + auto& db = library.get_database(); + if ( db.is_pi( n ) || db.is_constant( n ) ) + return { 0, 0 }; + if ( db.visited( n ) == db.trav_id() ) + return { 0, 0 }; + + db.set_visited( n, db.trav_id() ); + + int32_t area = 0; + uint32_t level = 0; + bool hashed = true; + + std::array, Ntk::max_fanin_size> node_data; + db.foreach_fanin( n, [&]( auto const& f, auto i ) { + node g = db.get_node( f ); + if ( db.is_constant( g ) ) + { + node_data[i] = f; /* ntk.get_costant( db.is_complemented( f ) ) */ + return; + } + if ( db.is_pi( g ) ) + { + node_data[i] = leaves[db.node_to_index( g ) - 1] ^ db.is_complemented( f ); + if constexpr ( has_level_v ) + { + level = std::max( level, ntk.level( ntk.get_node( leaves[db.node_to_index( g ) - 1] ) ) ); + } + return; + } + + auto [area_rec, level_rec] = evaluate_entry_rec( current_root, g, leaves ); + area += area_rec; + level = std::max( level, level_rec ); + + /* check value */ + if ( db.value( g ) < UINT32_MAX ) + { + signal s; + s.data = static_cast( db.value( g ) ); + node_data[i] = s ^ db.is_complemented( f ); + } + else + { + hashed = false; + } + } ); + + if ( hashed ) + { + /* try hash */ + /* AIG, XAG, MIG, and XMG are supported now */ + std::optional> val; + do + { + /* XAG */ + if constexpr ( has_has_and_v && has_has_xor_v ) + { + if ( db.is_and( n ) ) + val = ntk.has_and( node_data[0], node_data[1] ); + else + val = ntk.has_xor( node_data[0], node_data[1] ); + break; + } + + /* AIG */ + if constexpr ( has_has_and_v ) + { + val = ntk.has_and( node_data[0], node_data[1] ); + break; + } + + /* XMG */ + if constexpr ( has_has_maj_v && has_has_xor3_v ) + { + if ( db.is_maj( n ) ) + val = ntk.has_maj( node_data[0], node_data[1], node_data[2] ); + else + val = ntk.has_xor3( node_data[0], node_data[1], node_data[2] ); + break; + } + + /* MAJ */ + if constexpr ( has_has_maj_v ) + { + val = ntk.has_maj( node_data[0], node_data[1], node_data[2] ); + break; + } + std::cerr << "[e] Only AIGs, XAGs, MAJs, and XMGs are currently supported \n"; + } while ( false ); + + if ( val.has_value() ) + { + /* bad condition (current root is contained in the DAG): return a very high cost */ + if ( db.get_node( *val ) == current_root ) + return { UINT32_MAX / 2, level + 1 }; + + /* annotate hashing info */ + db.set_value( n, val->data ); + return { area + ( ntk.fanout_size( ntk.get_node( *val ) ) > 0 ? 0 : cost_fn( ntk, n ) ), level + 1 }; + } + } + + db.set_value( n, UINT32_MAX ); + return { area + cost_fn( ntk, n ), level + 1 }; + } + + void compute_required() + { + if constexpr ( has_level_v ) + { + ntk.foreach_po( [&]( auto const& f ) { + required[f] = ntk.depth(); + } ); + + for ( uint32_t index = ntk.size() - 1; index > ntk.num_pis(); index-- ) + { + node n = ntk.index_to_node( index ); + uint32_t req = required[n]; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + required[f] = std::min( required[f], req - 1 ); + } ); + } + } + } + + void propagate_required_rec( uint32_t root, node const& n, uint32_t size, uint32_t req ) + { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return; + + /* recursively update required time */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto const g = ntk.get_node( f ); + + /* recur if it is still a node to explore and to update */ + if ( ntk.node_to_index( g ) > root && ( ntk.node_to_index( g ) >= size || required[g] > req ) ) + propagate_required_rec( root, g, size, req - 1 ); + + /* update the required time */ + if ( ntk.node_to_index( g ) < size ) + required[g] = std::min( required[g], req - 1 ); + } ); + } + + void clear_cuts_fanout_rec( network_cuts_t& cuts, cut_manager_t& cut_manager, node const& n ) + { + ntk.foreach_fanout( n, [&]( auto const& g ) { + auto const index = ntk.node_to_index( g ); + if ( cuts.cuts( index ).size() > 0 ) + { + cut_manager.clear_cuts( g ); + clear_cuts_fanout_rec( cuts, cut_manager, g ); + } + } ); + } + +private: + void register_events() + { + if constexpr ( has_level_v ) + { + auto const update_level_of_new_node = [&]( const auto& n ) { + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_deleted_node = [&]( node const& n ) { + ntk.set_level( n, -1 ); + }; + + add_event = ntk.events().register_add_event( update_level_of_new_node ); + modified_event = ntk.events().register_modified_event( update_level_of_existing_node ); + delete_event = ntk.events().register_delete_event( update_level_of_deleted_node ); + } + } + + /* maybe it should be moved to depth_view */ + void update_node_level( node const& n, bool top_most = true ) + { + if constexpr ( has_level_v ) + { + uint32_t curr_level = ntk.level( n ); + + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + + /* update only one more level */ + if ( top_most ) + { + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level( p, false ); + } ); + } + } + } + } + +private: + Ntk& ntk; + Library&& library; + rewrite_params const& ps; + rewrite_stats& st; + NodeCostFn cost_fn; + + node_map required; + + uint32_t _candidates{ 0 }; + uint32_t _estimated_gain{ 0 }; + + /* events */ + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +} /* namespace detail */ + +/*! \brief Boolean rewrite. + * + * This algorithm rewrites enumerated cuts using new network structures from a database. + * The algorithm performs changes in-place and keeps the substituted structures dangling + * in the network. + * + * **Required network functions:** + * - `get_node` + * - `size` + * - `make_signal` + * - `foreach_gate` + * - `substitute_node` + * - `clear_visited` + * - `clear_values` + * - `fanout_size` + * - `set_value` + * - `foreach_node` + * + * \param ntk Input network (will be changed in-place) + * \param library Exact library containing pre-computed structures + * \param ps Rewrite params + * \param pst Rewrite statistics + * \param cost_fn Node cost function (a functor with signature `uint32_t(Ntk const&, node const&)`) + */ +template> +void rewrite( Ntk& ntk, Library&& library, rewrite_params const& ps = {}, rewrite_stats* pst = nullptr, NodeCostFn const& cost_fn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_clear_visited_v, "Ntk does not implement the clear_visited method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + rewrite_stats st; + + if ( ps.preserve_depth || ps.use_dont_cares ) + { + using depth_view_t = depth_view; + depth_view_t depth_ntk{ ntk }; + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ depth_ntk }; + + detail::rewrite_impl p( fanout_view, library, ps, st, cost_fn ); + p.run(); + } + else + { + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + detail::rewrite_impl p( fanout_view, library, ps, st, cost_fn ); + p.run(); + } + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + ntk = cleanup_dangling( ntk ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/satlut_mapping.hpp b/third-party/mockturtle/include/mockturtle/algorithms/satlut_mapping.hpp new file mode 100644 index 00000000000..fab0ccafba8 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/satlut_mapping.hpp @@ -0,0 +1,480 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file satlut_mapping.hpp + \brief SAT LUT mapping + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "../generators/sorting.hpp" +#include "../utils/include/percy.hpp" +#include "../utils/node_map.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" +#include "cell_window.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/mf_cut.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for satlut_mapping. + * + * The data structure `satlut_mapping_params` holds configurable parameters with + * default arguments for `satlut_mapping`. + */ +struct satlut_mapping_params +{ + satlut_mapping_params() + { + cut_enumeration_ps.cut_size = 6; + cut_enumeration_ps.cut_limit = 8; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut size is 6, the default cut limit is 8. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Conflict limit for SAT solver. + * + * The default limit is 0, which means the number of conflicts is not used + * as a resource limit. + */ + uint32_t conflict_limit{ 0u }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose. */ + bool very_verbose{ false }; +}; + +/*! \brief Statistics for satlut_mapping. + * + * The data structure `satlut_mapping_stats` provides data collected by running + * `satlut_mapping`. + */ +struct satlut_mapping_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Number of SAT variables. */ + uint64_t num_vars{ 0u }; + + /*! \brief Number of SAT clauses. */ + uint64_t num_clauses{ 0u }; + + void report() + { + std::cout << fmt::format( "[i] total time = {:>7.2f} secs\n", to_seconds( time_total ) ) + << fmt::format( "[i] SAT solving time = {:>7.2f} secs\n", to_seconds( time_sat ) ) + << fmt::format( "[i] number of SAT variables = {}\n", num_vars ) + << fmt::format( "[i] number of SAT clauses = {}\n", num_clauses ); + } +}; + +namespace detail +{ + +template +std::vector cardinality_network( Solver& solver, std::vector const& vars, int& next_var ) +{ + int lits[3]; + + auto logn = static_cast( ceil( log2( vars.size() ) ) ); + auto current = vars; + + if ( current.size() != static_cast( 1u ) << logn ) + { + current.resize( static_cast( 1u ) << logn, next_var ); + lits[0] = pabc::Abc_Var2Lit( next_var++, 1 ); + solver.add_clause( lits, lits + 1 ); + } + + batcher_sorting_network( static_cast( current.size() ), [&]( auto a, auto b ) { + auto va = current[a]; + auto vb = current[b]; + auto va_next = next_var++; + auto vb_next = next_var++; + + // AND(a, b) a + !c , b + !c , !a + !b + c + lits[0] = pabc::Abc_Var2Lit( va, 0 ); + lits[1] = pabc::Abc_Var2Lit( va_next, 1 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( vb, 0 ); + lits[1] = pabc::Abc_Var2Lit( va_next, 1 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( va, 1 ); + lits[1] = pabc::Abc_Var2Lit( vb, 1 ); + lits[2] = pabc::Abc_Var2Lit( va_next, 0 ); + solver.add_clause( lits, lits + 3 ); + + // OR(a, b) !a + c , !b + c , a + b + !c + lits[0] = pabc::Abc_Var2Lit( va, 1 ); + lits[1] = pabc::Abc_Var2Lit( vb_next, 0 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( vb, 1 ); + lits[1] = pabc::Abc_Var2Lit( vb_next, 0 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( va, 0 ); + lits[1] = pabc::Abc_Var2Lit( vb, 0 ); + lits[2] = pabc::Abc_Var2Lit( vb_next, 1 ); + solver.add_clause( lits, lits + 3 ); + + current[a] = va_next; + current[b] = vb_next; + } ); + + for ( auto i = 0u; i < current.size() - 1; ++i ) + { + lits[0] = pabc::Abc_Var2Lit( current[i], 1 ); + lits[1] = pabc::Abc_Var2Lit( current[i + 1], 0 ); + solver.add_clause( lits, lits + 2 ); + } + + return current; +} + +template +class satlut_mapping_impl +{ +public: + using network_cuts_t = network_cuts; + using cut_t = typename network_cuts_t::cut_t; + +public: + satlut_mapping_impl( Ntk& ntk, satlut_mapping_params const& ps, satlut_mapping_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cut_enumeration( ntk, ps.cut_enumeration_ps ) ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + std::vector card_inp; + node_map gate_var( ntk ); + node_map, Ntk> cut_vars( ntk ); + auto next_var = 0; + + percy::bsat_wrapper solver; + + /* initialize gate vars */ + ntk.foreach_gate( [&]( auto n ) { + card_inp.push_back( next_var ); + gate_var[n] = next_var++; + } ); + + const auto card_out = cardinality_network( solver, card_inp, next_var ); + + /* create clauses */ + int cut_lits[2]; + ntk.foreach_gate( [&]( auto n ) { + std::vector gate_is_mapped; + gate_is_mapped.push_back( pabc::Abc_Var2Lit( gate_var[n], 1 ) ); + + for ( auto const& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + if ( cut->size() == 1 ) + { + break; /* we assume that trivial cuts are in the end of the set */ + } + gate_is_mapped.push_back( pabc::Abc_Var2Lit( next_var, 0 ) ); + cut_lits[0] = pabc::Abc_Var2Lit( next_var, 1 ); + cut_vars[n].push_back( next_var++ ); + for ( auto leaf : *cut ) + { + if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + continue; + cut_lits[1] = pabc::Abc_Var2Lit( gate_var[ntk.index_to_node( leaf )], 0 ); + solver.add_clause( cut_lits, cut_lits + 2 ); + } + } + + solver.add_clause( &gate_is_mapped[0], &gate_is_mapped[0] + gate_is_mapped.size() ); + } ); + + /* outputs must be mapped */ + ntk.foreach_po( [&]( auto f ) { + auto lit = pabc::Abc_Var2Lit( gate_var[f], 0 ); + solver.add_clause( &lit, &lit + 1 ); + } ); + + st.num_vars = solver.nr_vars(); + st.num_clauses = solver.nr_clauses(); + + auto best_size = ntk.has_mapping() ? ntk.num_cells() + 1 : card_inp.size(); + + progress_bar pbar{ "satlut iteration = {0} try size = {1}", ps.progress }; + auto iteration = 0u; + while ( true ) + { + pbar( ++iteration, best_size ); + if ( best_size > card_out.size() ) + { + std::cout << fmt::format( "[e] best_size = {} card_inp.size() = {} card_out.size() = {} ntk.num_cells = {} ntk.has_mapping = {}\n", + best_size, card_inp.size(), card_out.size(), ntk.num_cells(), ntk.has_mapping() ); + assert( false ); + } + auto assump = pabc::Abc_Var2Lit( card_out[card_out.size() - best_size], 1 ); + + const auto result = call_with_stopwatch( st.time_sat, [&]() { return solver.solve( &assump, &assump + 1, ps.conflict_limit ); } ); + if ( result == percy::success ) + { + ntk.clear_mapping(); + ntk.foreach_gate( [&]( auto n ) { + if ( solver.var_value( gate_var[n] ) ) + { + for ( auto i = 0u; i < cut_vars[n].size(); ++i ) + { + if ( solver.var_value( cut_vars[n][i] ) ) + { + const auto index = ntk.node_to_index( n ); + std::vector> nodes; + for ( auto const& l : cuts.cuts( index )[i] ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + ntk.set_cell_function( n, cuts.truth_table( cuts.cuts( index )[i] ) ); + } + break; + } + } + } + } ); + + if ( ntk.num_cells() == ntk.num_pos() ) + { + /* no further improvement possible */ + break; + } + + best_size = ntk.num_cells(); + } + else + { + break; + } + } + } + +private: + Ntk& ntk; + satlut_mapping_params const& ps; + satlut_mapping_stats& st; + network_cuts_t cuts; +}; + +} // namespace detail + +/*! \brief SAT-LUT mapping. + * + * This algorithm implements the SAT-based area-oriented LUT mapping algorithm + * presented in [B. Schmitt, A. Mishchenko, and R.K. Brayton, *ASP-DAC* **23** + * (2018), 586-591]. + * + * The interface is similar to the one in `lut_mapping`. + * + * This algorithm applies SAT-LUT mapping to the whole networking and therefore + * may show poor performance for larger networks. There exists a method with + * the same name that takes as input a window size to apply SAT-LUT mapping to + * windows. + * + * **Required network functions:** + * - `is_pi` + * - `index_to_node` + * - `node_to_index` + * - `foreach_gate` + * - `foreach_po` + * - `num_gates` + * - `num_cells` + * - `has_mapping` + * - `clear_mapping` + * - `add_to_mapping` + * - `set_cell_function` if `StoreFunction` is true + * + * \param ntk Logic network to be mapped + * \param ps Parameters + * \param st Statistics + */ +template +void satlut_mapping( Ntk& ntk, satlut_mapping_params const& ps = {}, satlut_mapping_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_cells_v, "Ntk does not implement the num_cells method" ); + static_assert( has_has_mapping_v, "Ntk does not implement the has_mapping method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + static_assert( !StoreFunction || has_set_cell_function_v, "Ntk does not implement the set_cell_function method" ); + + satlut_mapping_stats st; + detail::satlut_mapping_impl p( ntk, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +/*! \brief SAT-LUT mapping (windowed). + * + * This algorithm applies SAT-LUT mapping to windows of a given size (e.g., 32, + * 64, 128) and can therefore better deal with larger networks. It has + * otherwise the same interface as `satlut_mapping`. + * + * The initial network must already contain a mapping, e.g., found with + * `lut_mapping`. + * + * **Required network functions:** + * - `is_pi` + * - `index_to_node` + * - `node_to_index` + * - `foreach_gate` + * - `foreach_po` + * - `num_gates` + * - `num_cells` + * - `has_mapping` + * - `clear_mapping` + * - `add_to_mapping` + * - `is_cell_root` + * - `set_cell_function` if `StoreFunction` is true + * + * \param ntk Logic network to be mapped + * \param window_size Maximum number of gates in a window + * \param ps Parameters + * \param st Statistics + */ +template +void satlut_mapping( Ntk& ntk, uint32_t window_size, satlut_mapping_params ps = {}, satlut_mapping_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_cells_v, "Ntk does not implement the num_cells method" ); + static_assert( has_has_mapping_v, "Ntk does not implement the has_mapping method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + static_assert( has_is_cell_root_v, "Ntk does not implement the is_cell_root method" ); + static_assert( !StoreFunction || has_set_cell_function_v, "Ntk does not implement the set_cell_function method" ); + + if ( !ntk.has_mapping() ) + { + return; + } + + satlut_mapping_stats st; + stopwatch<>::duration time_total{}; + cell_window window( ntk, window_size ); + progress_bar pbar{ ntk.size(), "satlut (windowed) |{0}| node = {1:>4} / " + std::to_string( ntk.size() ), ps.progress }; + ps.progress = false; /* do not show inner progress */ + ntk.foreach_gate( [&]( auto n, int index ) { + stopwatch<> t( time_total ); + pbar( index, ntk.node_to_index( n ) ); + if ( ntk.is_cell_root( n ) ) + { + if ( !window.compute_window_for( n ) ) /* window has been visited before */ + { + return true; + } + + if ( ps.verbose ) + { + std::cout << fmt::format( "[i] cell {:>5} size = {:>4} nodes = {:>2} gates = {:>3} pis = {:>3} pos = {:>3}\n", + n, + window.size(), + window.num_cells(), + window.num_gates(), + window.num_pis(), + window.num_pos() ); + } + if ( window.num_cells() == window.num_pos() || window.num_pos() == 0 ) + { + return true; + } + topo_view window_topo{ window }; + detail::satlut_mapping_impl p( window_topo, ps, st ); + p.run(); + return true; + } + + return true; + } ); + + st.time_total = time_total; + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/sim_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/sim_resub.hpp new file mode 100644 index 00000000000..f1ab33d93e2 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/sim_resub.hpp @@ -0,0 +1,432 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sim_resub.hpp + \brief Simulation-Guided Resubstitution + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../io/write_patterns.hpp" +#include "../networks/aig.hpp" +#include "../networks/xag.hpp" +#include "../networks/mig.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "circuit_validator.hpp" +#include "pattern_generation.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/xag_resyn.hpp" +#include "resyn_engines/mig_resyn.hpp" +#include "simulation.hpp" + +#include +#include +#include + +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +struct sim_resub_stats +{ + /*! \brief Time for pattern generation. */ + stopwatch<>::duration time_patgen{ 0 }; + + /*! \brief Time for saving patterns. */ + stopwatch<>::duration time_patsave{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + stopwatch<>::duration time_sat_restart{ 0 }; + + /*! \brief Time for computing ODCs. */ + stopwatch<>::duration time_odc{ 0 }; + + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_resyn{ 0 }; + + /*! \brief Time for translating from index lists to network signals. */ + stopwatch<>::duration time_interface{ 0 }; + + /*! \brief Number of patterns used. */ + uint32_t num_pats{ 0 }; + + /*! \brief Number of counter-examples. */ + uint32_t num_cex{ 0 }; + + /*! \brief Number of successful resubstitutions. */ + uint32_t num_resub{ 0 }; + + /*! \brief Number of SAT solver timeout. */ + uint32_t num_timeout{ 0 }; + + /*! \brief Number of calls to the resynthesis engine. */ + uint32_t num_resyn{ 0 }; + + ResynSt resyn_st; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] ======== Stats ========\n" ); + fmt::print( "[i] #pat = {:6d}\n", num_pats ); + fmt::print( "[i] #resyn call = {:6d}\n", num_resyn ); + fmt::print( "[i] #valid = {:6d}\n", num_resub ); + fmt::print( "[i] #CEX = {:6d}\n", num_cex ); + fmt::print( "[i] #timeout = {:6d}\n", num_timeout ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] generate pattern: {:>5.2f} secs [excluded]\n", to_seconds( time_patgen ) ); + fmt::print( "[i] save pattern : {:>5.2f} secs [excluded]\n", to_seconds( time_patsave ) ); + fmt::print( "[i] simulation : {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( "[i] SAT solve : {:>5.2f} secs\n", to_seconds( time_sat ) ); + fmt::print( "[i] SAT restart : {:>5.2f} secs\n", to_seconds( time_sat_restart ) ); + fmt::print( "[i] compute ODCs : {:>5.2f} secs\n", to_seconds( time_odc ) ); + fmt::print( "[i] interfacing : {:>5.2f} secs\n", to_seconds( time_interface ) ); + fmt::print( "[i] compute function: {:>5.2f} secs\n", to_seconds( time_resyn ) ); + fmt::print( "[i] ======== Details ========\n" ); + resyn_st.report(); + fmt::print( "[i] =========================\n\n" ); + } +}; + +/*! \brief Simulation-based resubstitution engine. + * + * This engine simulates the entire network using partial truth tables and calls a + * resynthesis engine (template parameter `ResynEngine`) to find potential resubstitutions. + * If a resubstitution candidate is found, it then formally verifies it with SAT solving. + * If the validation fails, a counter-example will be added to the simulation patterns, + * and resynthesis will be invoked again with updated truth tables, looping until it returns + * `std::nullopt`. This engine only requires the divisor collector to prepare `divs`. + * + * Please refer to the following paper for further details. + * + * [1] A Simulation-Guided Paradigm for Logic Synthesis and Verification. TCAD, 2022. + * + * Required interface of `ResynEngine`: + * - A public `operator()`: `std::optional operator()` + * `( TT const& target, TT const& care, iterator_type begin, iterator_type end, + * truth_table_storage_type const& tts, uint32_t max_size )` + * + * All classes implemented in `algorithms/resyn_engines/` are compatible. + * + * \tparam validator_t Specialization of `circuit_validator`. + * \tparam ResynEngine A resynthesis solver to compute the resubstitution candidate. + * \tparam MffcRes Typename of `potential_gain`. + */ +template, class ResynEngine = xag_resyn_decompose>, typename MffcRes = uint32_t> +class simulation_based_resub_engine +{ +public: + static constexpr bool require_leaves_and_mffc = false; + using stats = sim_resub_stats; + using mffc_result_t = MffcRes; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using TT = kitty::partial_truth_table; + + explicit simulation_based_resub_engine( Ntk& ntk, resubstitution_params const& ps, stats& st ) + : ntk( ntk ), ps( ps ), st( st ), tts( ntk ), validator( ntk, { ps.max_clauses, ps.odc_levels, ps.conflict_limit, ps.random_seed } ), engine( st.resyn_st ) + { + if constexpr ( !validator_t::use_odc_ ) + { + assert( ps.odc_levels == 0 && "to consider ODCs, circuit_validator::use_odc (the last template parameter) has to be turned on" ); + } + + add_event = ntk.events().register_add_event( [&]( const auto& n ) { + tts.resize(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } ); + } + + ~simulation_based_resub_engine() + { + if ( ps.save_patterns ) + { + call_with_stopwatch( st.time_patsave, [&]() { + write_patterns( sim, *ps.save_patterns ); + } ); + } + + if ( add_event ) + { + ntk.events().release_add_event( add_event ); + } + } + + void init() + { + /* prepare simulation patterns */ + call_with_stopwatch( st.time_patgen, [&]() { + if ( ps.pattern_filename ) + { + sim = partial_simulator( *ps.pattern_filename ); + } + else + { + sim = partial_simulator( ntk.num_pis(), 1024 ); + pattern_generation( ntk, sim ); + } + + if constexpr ( has_EXCDC_interface_v ) + { + sim.remove_CDC_patterns( ntk ); + } + } ); + st.num_pats = sim.num_bits(); + assert( sim.num_bits() > 0 ); + + /* first simulation: the whole circuit; from 0 bits. */ + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + + void update() + { + if constexpr ( validator_t::use_odc_ || has_EXODC_interface_v ) + { + call_with_stopwatch( st.time_sat_restart, [&]() { + validator.update(); + } ); + tts.reset(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + } + + std::optional run( node const& n, std::vector const& divs, mffc_result_t potential_gain, uint32_t& last_gain ) + { + for ( auto j = 0u; j < ps.max_trials; ++j ) + { + check_tts( n ); + for ( auto const& d : divs ) + { + check_tts( d ); + } + + TT const care = call_with_stopwatch( st.time_odc, [&]() { + return ( ps.odc_levels == 0 ) ? sim.compute_constant( true ) : ~observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); + } ); + + const auto res = call_with_stopwatch( st.time_resyn, [&]() { + ++st.num_resyn; + return engine( tts[n], care, std::begin( divs ), std::end( divs ), tts, std::min( potential_gain - 1, ps.max_inserts ) ); + } ); + + if ( res ) + { + auto const& id_list = *res; + assert( id_list.num_pos() == 1u ); + last_gain = potential_gain - id_list.num_gates(); + auto valid = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( n, divs, id_list ); + } ); + if ( valid ) + { + if ( *valid ) + { + ++st.num_resub; + signal out_sig; + call_with_stopwatch( st.time_interface, [&]() { + std::vector divs_sig( divs.size() ); + std::transform( divs.begin(), divs.end(), divs_sig.begin(), [&]( const node n ) { + return ntk.make_signal( n ); + } ); + insert( ntk, divs_sig.begin(), divs_sig.end(), id_list, [&]( signal const& s ) { + out_sig = s; + } ); + } ); + return out_sig; + } + else + { + found_cex(); + continue; + } + } + else /* timeout */ + { + return std::nullopt; + } + } + else /* functor can not find any potential resubstitution */ + { + return std::nullopt; + } + } + return std::nullopt; + } + + void found_cex() + { + ++st.num_cex; + call_with_stopwatch( st.time_sim, [&]() { + sim.add_pattern( validator.cex ); + } ); + + /* re-simulate the whole circuit (for the last block) when a block is full */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + } + + void check_tts( node const& n ) + { + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + } + +private: + Ntk& ntk; + resubstitution_params const& ps; + stats& st; + + incomplete_node_map tts; + partial_simulator sim; + + validator_t validator; + ResynEngine engine; + + /* events */ + std::shared_ptr::add_event_type> add_event; +}; /* simulation_based_resub_engine */ + +template +void sim_resubstitution_run( Ntk& ntk, resubstitution_params const& ps, resubstitution_stats* pst ) +{ + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + st.time_resub -= engine_st.time_patgen; + st.time_total -= engine_st.time_patgen + engine_st.time_patsave; + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace detail */ + +template +void sim_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( std::is_same_v + || std::is_same_v + || std::is_same_v, "Currently only supports AIG, XAG, and MIG" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + if constexpr ( std::is_same_v ) + { + using resyn_engine_t = xag_resyn_decompose>; + + if ( ps.odc_levels != 0 ) + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + else + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + } + else if constexpr ( std::is_same_v ) + { + using resyn_engine_t = xag_resyn_decompose>; + + if ( ps.odc_levels != 0 ) + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + else + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + } + else if constexpr ( std::is_same_v ) + { + using resyn_engine_t = mig_resyn_topdown; + + if ( ps.odc_levels != 0 ) + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + else + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/simulation.hpp b/third-party/mockturtle/include/mockturtle/algorithms/simulation.hpp new file mode 100644 index 00000000000..119c96adaf3 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/simulation.hpp @@ -0,0 +1,982 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file simulation.hpp + \brief Simulate networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Abstract template class for simulation. */ +template +class default_simulator +{ +public: + default_simulator() = delete; +}; + +/*! \brief Simulates Boolean assignments. + * + * This simulator simulates Boolean values. A vector with assignments for each + * primary input must be passed to the constructor. + */ +template<> +class default_simulator +{ +public: + default_simulator() = delete; + default_simulator( std::vector const& assignments ) : assignments( assignments ) {} + + bool compute_constant( bool value ) const { return value; } + bool compute_pi( uint32_t index ) const { return assignments[index]; } + bool compute_not( bool value ) const { return !value; } + +private: + std::vector assignments; +}; + +/*! \brief Simulates Boolean assignments with input word. + * + * This simulator simulates Boolean values. A bitstring with assignments for + * each primary input must be passed to the constructor. Because this + * bitstring can have at most 64 bits, this simulator is not suitable for + * logic networks with more than 64 primary inputs. + */ +class input_word_simulator +{ +public: + input_word_simulator( uint64_t word ) : word( word ) {} + + bool compute_constant( bool value ) const { return value; } + bool compute_pi( uint32_t index ) const { return ( word >> index ) & 1; } + bool compute_not( bool value ) const { return !value; } + +private: + uint64_t word; +}; + +/*! \brief Simulates truth tables. + * + * This simulator simulates truth tables. Each primary input is assigned the + * projection function according to the index. The number of variables be + * passed to the constructor of the simulator. + */ +template<> +class default_simulator +{ +public: + default_simulator() = delete; + default_simulator( unsigned num_vars ) : num_vars( num_vars ) {} + + kitty::dynamic_truth_table compute_constant( bool value ) const + { + kitty::dynamic_truth_table tt( num_vars ); + return value ? ~tt : tt; + } + + kitty::dynamic_truth_table compute_pi( uint32_t index ) const + { + kitty::dynamic_truth_table tt( num_vars ); + kitty::create_nth_var( tt, index ); + return tt; + } + + kitty::dynamic_truth_table compute_not( kitty::dynamic_truth_table const& value ) const + { + return ~value; + } + +private: + unsigned num_vars; +}; + +/*! \brief Simulates truth tables. + * + * This simulator simulates truth tables. Each primary input is assigned the + * projection function according to the index. The number of variables must be + * known at compile time. + */ +template +class default_simulator> +{ +public: + kitty::static_truth_table compute_constant( bool value ) const + { + kitty::static_truth_table tt; + return value ? ~tt : tt; + } + + kitty::static_truth_table compute_pi( uint32_t index ) const + { + kitty::static_truth_table tt; + kitty::create_nth_var( tt, index ); + return tt; + } + + kitty::static_truth_table compute_not( kitty::static_truth_table const& value ) const + { + return ~value; + } +}; + +/*! \brief Simulates partial truth tables. + * + * This simulator simulates partial truth tables, whose length is flexible + * and new simulation patterns can be added. + */ +class partial_simulator +{ + friend class bit_packed_simulator; + +public: + partial_simulator() {} + + /*! \brief Create a `partial_simulator` with random simulation patterns. + * + * \param num_pis Number of primary inputs, which is the same as the length of a simulation pattern. + * \param num_patterns Number of initial random simulation patterns. + */ + partial_simulator( uint32_t num_pis, uint32_t num_patterns, std::default_random_engine::result_type seed = 1 ) + : num_patterns( num_patterns ) + { + assert( num_pis > 0u ); + + for ( auto i = 0u; i < num_pis; ++i ) + { + patterns.emplace_back( num_patterns ); + kitty::create_random( patterns.back(), seed + i ); + } + } + + /* copy constructors */ + partial_simulator( partial_simulator const& sim ) = default; + partial_simulator& operator=( partial_simulator const& sim ) = default; + + /*! \brief Create a `partial_simulator` with given simulation patterns. + * + * \param initial_patterns Initial simulation patterns. + */ + partial_simulator( std::vector const& initial_patterns ) + : patterns( initial_patterns ), num_patterns( patterns.at( 0 ).num_bits() ) + {} + + /*! \brief Create a `partial_simulator` with simulation patterns read from a file. + * + * The simulation pattern file should contain `num_pis` lines of the same length. + * Each line is the simulation signature of a primary input, represented in hexadecimal. + * + * \param filename Name of the simulation pattern file. + * \param length Number of simulation patterns to keep. Should not be greater than 4 times + * the length of a line in the file. Setting this parameter to 0 means to keep all patterns in the file. + */ + partial_simulator( const std::string& filename, uint32_t length = 0u ) + { + std::ifstream in( filename, std::ifstream::in ); + std::string line; + + while ( getline( in, line ) ) + { + patterns.emplace_back( line.length() * 4 ); + kitty::create_from_hex_string( patterns.back(), line ); + if ( length != 0u ) + { + patterns.back().resize( length ); + } + } + + in.close(); + + assert( patterns.size() > 0 ); + num_patterns = patterns[0].num_bits(); + } + + kitty::partial_truth_table compute_constant( bool value ) const + { + kitty::partial_truth_table zero( num_patterns ); + return value ? ~zero : zero; + } + + kitty::partial_truth_table compute_pi( uint32_t index ) const + { + return patterns.at( index ); + } + + kitty::partial_truth_table compute_not( kitty::partial_truth_table const& value ) const + { + return ~value; + } + + /*! \brief Get the current number of simulation patterns. */ + uint32_t num_bits() const + { + return num_patterns; + } + + /*! \brief Add a pattern (primary input assignment) into the pattern set. + * + * \param pattern The pattern. Length should be the same as number of PIs. + */ + void add_pattern( std::vector const& pattern ) + { + assert( pattern.size() == patterns.size() ); + + for ( auto i = 0u; i < pattern.size(); ++i ) + { + patterns.at( i ).add_bit( pattern.at( i ) ); + } + ++num_patterns; + } + + /*! \brief Get the simulation patterns. + * + * \return A vector of `num_pis()` patterns stored in `kitty::partial_truth_table`s. + */ + std::vector get_patterns() const + { + return patterns; + } + + template, typename = std::enable_if_t> + void remove_CDC_patterns( Ntk const& ntk ) + { + std::vector pattern( patterns.size() ); + for ( int i = 0; i < (int)num_patterns; ++i ) + { + for ( auto j = 0u; j < patterns.size(); ++j ) + { + pattern[j] = kitty::get_bit( patterns[j], i ); + } + if ( ntk.pattern_is_EXCDC( pattern ) ) + { + for ( auto j = 0u; j < patterns.size(); ++j ) + { + kitty::copy_bit( patterns[j], num_patterns - 1, patterns[j], i ); + } + --num_patterns; + --i; + } + } + for ( auto j = 0u; j < patterns.size(); ++j ) + { + patterns[j].resize( num_patterns ); + } + } + +private: + std::vector patterns; + uint32_t num_patterns; +}; + +/*! \brief Simulates partial truth tables, and performs bit packing when requested. + * + * This class has the same interfaces as `partial_simulator`, except that + * (1) care bits should be provided as the second argument of `add_pattern`; and + * (2) `pack_bits` can be called to reduce the size of pattern set. + */ +class bit_packed_simulator : public partial_simulator +{ +public: + using partial_simulator::compute_constant; + using partial_simulator::compute_not; + using partial_simulator::compute_pi; + using partial_simulator::get_patterns; + using partial_simulator::num_bits; + + bit_packed_simulator() {} + + bit_packed_simulator( uint32_t num_pis, uint32_t num_patterns, std::default_random_engine::result_type seed = 1 ) + : partial_simulator( num_pis, num_patterns, seed ), packed_patterns( num_patterns ) + { + fill_cares( num_pis ); + } + + /* copy constructors */ + bit_packed_simulator( bit_packed_simulator const& sim ) = default; + bit_packed_simulator& operator=( bit_packed_simulator const& sim ) = default; + + /* copy constructor from `partial_simulator` */ + bit_packed_simulator( partial_simulator const& sim ) + : partial_simulator( sim ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + bit_packed_simulator( std::vector const& initial_patterns ) + : partial_simulator( initial_patterns ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + bit_packed_simulator( const std::string& filename, uint32_t length = 0u ) + : partial_simulator( filename, length ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + /*! \brief Add a pattern (primary input assignment) into the pattern set. + * + * \param pattern The pattern. Length should be the same as number of PIs. + * \param care_bits Care bits of the pattern. Length should be the same as `pattern`. + */ + void add_pattern( std::vector const& pattern, std::vector const& care_bits ) + { + assert( pattern.size() == care_bits.size() ); + assert( pattern.size() == patterns.size() ); + + for ( auto i = 0u; i < pattern.size(); ++i ) + { + patterns.at( i ).add_bit( pattern.at( i ) ); + care.at( i ).add_bit( care_bits.at( i ) ); + } + ++num_patterns; + } + + /*! \brief Try to pack the newly added patterns (since the last call) into preceding patterns. + * + * \return `true` when some patterns are packed (so that update of simulated truth tables is needed) + */ + bool pack_bits() + { + if ( num_patterns == 0u ) + { + return false; + } + if ( num_patterns == packed_patterns ) + { + return false; + } + assert( num_patterns > packed_patterns ); + + std::vector empty_slots; + /* for each unpacked pattern (at `p`), try to pack it into one of the patterns before it (at `pos` in block `block`). */ + for ( int64_t p = num_patterns - 1; p >= (int64_t)packed_patterns; --p ) + { + for ( auto block = p < 1024 ? 0 : std::rand() % ( p >> 6 ); block <= ( p >> 6 ); ++block ) + { + uint64_t unavailable = 0u; + /* check each PI */ + for ( auto i = 0u; i < patterns.size(); ++i ) + { + if ( !kitty::get_bit( care[i], p ) ) + { + continue; + } /* only check for the cared PIs of p */ + unavailable |= care[i]._bits[block]; + } + auto pos = kitty::find_first_bit_in_word( ~unavailable ); + if ( pos != -1 && ( block < ( p >> 6 ) || pos < ( p % 64 ) ) ) + { + move_pattern( p, pos + ( block << 6 ) ); + empty_slots.emplace_back( p ); + break; + } + } + } + + if ( empty_slots.size() > 0u ) + { + /* fill the empty slots (from smaller values; `empty_slots` should be reversely sorted) */ + /* `empty_slots[j]` is the smallest position where larger positions are all empty */ + int64_t j = 0; + for ( int64_t i = empty_slots.size() - 1; i >= 0; --i ) + { + while ( j <= i && empty_slots[j] >= num_patterns - 1 ) + { + if ( empty_slots[j] == num_patterns - 1 ) + { + --num_patterns; + } + ++j; + if ( j == (int64_t)empty_slots.size() ) + { + break; + } + } + if ( j > i ) + { + break; + } + move_pattern( num_patterns - 1, empty_slots[i] ); + --num_patterns; + } + assert( patterns[0].num_bits() - num_patterns == empty_slots.size() ); + for ( auto i = 0u; i < patterns.size(); ++i ) + { + patterns[i].resize( num_patterns ); + care[i].resize( num_patterns ); + } + packed_patterns = num_patterns; + return true; + } + packed_patterns = num_patterns; + return false; + } + + void randomize_dont_care_bits( std::default_random_engine::result_type seed = 1 ) + { + for ( auto i = 0u; i < patterns.size(); ++i ) + { + kitty::partial_truth_table tt( num_patterns ); + kitty::create_random( tt, std::default_random_engine::result_type( seed + patterns.size() + i ) ); + patterns.at( i ) = ( patterns.at( i ) & care.at( i ) ) | ( tt & ~care.at( i ) ); + } + } + +private: + /* all bits in patterns generated before construction are care bits */ + void fill_cares( uint64_t const num_pis ) + { + for ( auto i = 0u; i < num_pis; ++i ) + { + care.emplace_back( num_patterns ); + care.back() = ~care.back(); + } + } + + /* move the pattern at position `from` to position `to`. */ + void move_pattern( uint64_t const from, uint64_t const to ) + { + for ( auto i = 0u; i < patterns.size(); ++i ) + { + if ( !kitty::get_bit( care[i], from ) ) + { + continue; + } + assert( !kitty::get_bit( care[i], to ) ); + kitty::copy_bit( patterns[i], from, patterns[i], to ); + kitty::set_bit( care[i], to ); + kitty::clear_bit( care[i], from ); + } + } + +private: + std::vector care; + uint32_t packed_patterns; +}; + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a map that maps each node to its computed simulation + * value. + * + * **Required network functions:** + * - `foreach_po` + * - `get_constant` + * - `constant_value` + * - `get_node` + * - `foreach_pi` + * - `foreach_gate` + * - `fanin_size` + * - `num_pos` + * - `compute` + * + * \param ntk Network + * \param sim Simulator, which implements the simulator interface + */ +template> +node_map simulate_nodes( Ntk const& ntk, Simulator const& sim = Simulator() ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for SimulationType" ); + + node_map node_to_value( ntk ); + + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); + + ntk.foreach_gate( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[f]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } ); + + return node_to_value; +} + +namespace detail +{ + +template +void simulate_nodes_with_node_map( Ntk const& ntk, Container& node_to_value, Simulator const& sim ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for SimulationType" ); + + /* constants */ + if ( !node_to_value.has( ntk.get_node( ntk.get_constant( false ) ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + } + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + if ( !node_to_value.has( ntk.get_node( ntk.get_constant( true ) ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + } + + /* pis */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + if ( !node_to_value.has( n ) ) + { + node_to_value[n] = sim.compute_pi( i ); + } + } ); + + /* gates */ + ntk.foreach_gate( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + if ( !node_to_value.has( n ) ) + { + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + } ); +} + +} // namespace detail + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a map that maps each node to its computed simulation + * value. + * + * **Required network functions:** + * - `foreach_po` + * - `get_constant` + * - `constant_value` + * - `get_node` + * - `foreach_pi` + * - `foreach_gate` + * - `fanin_size` + * - `num_pos` + * - `compute` + * + * \param ntk Network + * \param node_to_value A map from nodes to values + * \param sim Simulator, which implements the simulator interface + */ +template> +void simulate_nodes( Ntk const& ntk, unordered_node_map& node_to_value, Simulator const& sim = Simulator() ) +{ + detail::simulate_nodes_with_node_map>( ntk, node_to_value, sim ); +} + +template> +void simulate_nodes( Ntk const& ntk, incomplete_node_map& node_to_value, Simulator const& sim = Simulator() ) +{ + detail::simulate_nodes_with_node_map>( ntk, node_to_value, sim ); +} + +namespace detail +{ +/* Forward declaration */ +template +void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ); + +template +void simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + else if ( node_to_value[ntk.get_node( f )].num_bits() != sim.num_bits() ) + { + re_simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); +} + +template +void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + else if ( node_to_value[ntk.get_node( f )].num_bits() != sim.num_bits() ) + { + re_simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + ntk.compute( n, node_to_value[n], fanin_values.begin(), fanin_values.end() ); +} + +template +void update_const_pi( Ntk const& ntk, Container& node_to_value, Simulator const& sim ) +{ + /* constants */ + node_to_value[ntk.get_constant( false )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_constant( true )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + + /* pis */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); +} + +} // namespace detail + +/*! \brief (Re-)simulate `n` and its transitive fanin cone. + * + * Note that re-simulation (when `node_to_value.has( n ) == true`) is only done + * for the last block, no matter how many bits are used in this block. + * Hence, it is advised to call `simulate_nodes` with `simulate_whole_tt = false` + * whenever `sim.num_bits() % 64 == 0`. + * + */ +template> +void simulate_node( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_compute_v, "Ntk does not implement the compute specialization for kitty::partial_truth_table" ); + static_assert( has_compute_inplace_v, "Ntk does not implement the in-place compute specialization for kitty::partial_truth_table" ); + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + if ( node_to_value[ntk.get_node( ntk.get_constant( false ) )].num_bits() != sim.num_bits() ) + { + detail::update_const_pi( ntk, node_to_value, sim ); + } + + if ( !node_to_value.has( n ) ) + { + detail::simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + else if ( node_to_value[n].num_bits() != sim.num_bits() ) + { + detail::re_simulate_fanin_cone( ntk, n, node_to_value, sim ); + } +} + +/*! \brief Simulates a network with `partial_simulator` (or `bit_packed_simulator`). + * + * This is the specialization for `partial_truth_table`. + * This function simulates every node in the circuit. + * + * \param simulate_whole_tt When this parameter is true, it is assumed that `node_to_value.has( n )` is false for every node. + * In contrast, when this parameter is false, only the last block of `partial_truth_table` will be re-computed, + * and it is assumed that `node_to_value.has( n )` is true for every node. + */ +template> +void simulate_nodes( Ntk const& ntk, Container& node_to_value, Simulator const& sim, bool simulate_whole_tt ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_compute_v, "Ntk does not implement the compute specialization for kitty::partial_truth_table" ); + static_assert( has_compute_inplace_v, "Ntk does not implement the in-place compute specialization for kitty::partial_truth_table" ); + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + detail::update_const_pi( ntk, node_to_value, sim ); + + /* gates */ + if ( simulate_whole_tt ) + { + ntk.foreach_gate( [&]( auto const& n ) { + if ( !node_to_value.has( n ) ) + { + detail::simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + } ); + } + else + { + ntk.foreach_gate( [&]( auto const& n ) { + assert( node_to_value.has( n ) ); + if ( node_to_value[n].num_bits() != sim.num_bits() ) + { + detail::re_simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + } ); + } +} + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a vector that maps each primary output (ordered by + * position) to it's simulation value (taking possible complemented attributes + * into account). + * + * **Required network functions:** + * - `foreach_po` + * - `is_complemented` + * - `compute` + * + * \param ntk Network + * \param sim Simulator, which implements the simulator interface + */ +template> +std::vector simulate( Ntk const& ntk, Simulator const& sim = Simulator() ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po function" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented function" ); + static_assert( has_compute_v, "Ntk does not implement the compute function for SimulationType" ); + + auto const node_to_value = simulate_nodes( ntk, sim ); + + std::vector po_values( ntk.num_pos() ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk.is_complemented( f ) ) + { + po_values[i] = sim.compute_not( node_to_value[f] ); + } + else + { + po_values[i] = node_to_value[f]; + } + } ); + return po_values; +} + +/*! \brief Simulates a buffered network + * + * The implementation is only slightly different from `simulate` by + * replacing `foreach_gate` with `foreach_node` and checking `fanin_size` + * because buffers are not counted as gates but still need to be simulated. + */ +template +std::vector> simulate_buffered( Ntk const& ntk ) +{ + static_assert( has_is_buf_v, "Ntk is not a buffered network type" ); + static_assert( has_compute_v>, "Ntk does not implement the compute function for static_truth_table" ); + assert( ntk.num_pis() == NumPIs ); + + default_simulator> sim; + node_map, Ntk> node_to_value( ntk ); + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); + ntk.foreach_node( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + if ( ntk.fanin_size( n ) > 0 ) + { + std::vector> fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[f]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + } ); + + std::vector> po_values( ntk.num_pos() ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk.is_complemented( f ) ) + { + po_values[i] = sim.compute_not( node_to_value[f] ); + } + else + { + po_values[i] = node_to_value[f]; + } + } ); + return po_values; +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/testcase_minimizer.hpp b/third-party/mockturtle/include/mockturtle/algorithms/testcase_minimizer.hpp new file mode 100644 index 00000000000..34a9a72bf1a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/testcase_minimizer.hpp @@ -0,0 +1,558 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file testcase_minimizer.hpp + \brief Minimize testcase for debugging + + \author Siang-Yun (Sonia) Lee +*/ + +#include "../io/aiger_reader.hpp" +#include "../io/verilog_reader.hpp" +#include "../io/write_aiger.hpp" +#include "../io/write_verilog.hpp" +#include "../networks/aig.hpp" +#include "../utils/debugging_utils.hpp" +#include "../views/color_view.hpp" +#include "cleanup.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for testcase_minimizer. */ +struct testcase_minimizer_params +{ + /*! \brief File format of the testcase. */ + enum + { + verilog, + aiger + } file_format = verilog; + + /*! \brief Path to find the initial test case and to store the minimized test case. */ + std::string path{ "." }; + + /*! \brief File name of the initial test case (excluding extension). */ + std::string init_case{ "testcase" }; + + /*! \brief File name of the minimized test case (excluding extension). */ + std::string minimized_case{ "minimized" }; + + /*! \brief Maximum number of iterations in total. nullopt = infinity */ + std::optional num_iterations{ std::nullopt }; + + /*! \brief Step into the next stage if nothing works for this number of iterations. */ + std::optional num_iterations_stage{ std::nullopt }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; +}; /* testcase_minimizer_params */ + +/*! \brief Debugging testcase minimizer + * + * Given a (sequence of) algorithm(s) and a testcase that is + * known to trigger a bug in the algorithm(s), this utility + * minimizes the testcase by trying to remove parts of the network + * with an increasing granularity in each stage. Only changes after + * which the bug is still triggered are kept; otherwise, the change + * is reverted. + * + * The script of algorithm(s) to be run can be provided as (1) a + * lambda function taking a network as input and returning a Boolean, + * which is true if the script runs normally and is false otherwise + * (i.e. the buggy behavior is observed); or (2) (not supported on + * Windows platform) a lambda function making a command string to be + * called, taking a filename string as input. The command should return + * 0 if the program runs normally, return 1 if the concerned buggy + * behavior is observed, and return other values if the input network + * is not valid (thus the latest change will not be kept). If the + * command segfaults or an assertion fails, it is treated as observing + * the buggy behavior. + * + * + * + \verbatim embed:rst + + Usage + + .. code-block:: c++ + + auto opt = []( mig_network ntk ) -> bool { + direct_resynthesis resyn; + refactoring( ntk, resyn ); + return network_is_acyclic( ntk ); + }; + + auto make_command = []( std::string const& filename ) -> std::string { + return "./abc -c \"read " + filename + "; drw\""; + }; + + testcase_minimizer_params ps; + ps.path = "."; // current directory + testcase_minimizer minimizer( ps ); + minimizer.run( opt ); // debug algorithms implemented in mockturtle + minimizer.run( make_command ); // debug external scripts + \endverbatim +*/ +template +class testcase_minimizer +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit testcase_minimizer( testcase_minimizer_params const ps = {} ) + : ps( ps ) + { + // assert( ps.file_format != testcase_minimizer_params::aiger || std::is_same_v ); + switch ( ps.file_format ) + { + case testcase_minimizer_params::verilog: + file_extension = ".v"; + break; + case testcase_minimizer_params::aiger: + file_extension = ".aig"; + break; + default: + fmt::print( "[e] Unsupported format\n" ); + } + std::srand( ps.seed ); + } + + void run( std::function const& fn ) + { + if ( !read_initial_testcase() ) + { + return; + } + + if ( !test( fn ) ) + { + fmt::print( "[e] The initial test case does not trigger the buggy behavior\n" ); + return; + } + + uint32_t counter{ 0 }; + while ( !ps.num_iterations || counter++ < ps.num_iterations ) + { + ntk_backup2 = cleanup_dangling( ntk ); + if ( !reduce() ) + { + write_testcase( ps.minimized_case ); + break; + } + if ( ntk.num_gates() == 0 ) + { + ++stage_counter; + ntk = ntk_backup2; + continue; + } + + if ( test( fn ) ) + { + fmt::print( "[i] Testcase with I/O = {}/{} gates = {} triggers the buggy behavior\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + write_testcase( ps.minimized_case ); + stage_counter = 0; + sampled.clear(); + } + else + { + ++stage_counter; + ntk = ntk_backup2; + } + } + + if ( init_PIs != ntk.num_pis() || init_POs != ntk.num_pos() || init_gates != ntk.num_gates() ) + { + fmt::print( "[i] Minimized the testcase from I/O = {}/{} gates = {}\n", init_PIs, init_POs, init_gates ); + fmt::print( " to I/O = {}/{} gates = {}\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + } + } + +#ifndef _MSC_VER + void run( std::function const& make_command ) + { + if ( !read_initial_testcase() ) + { + return; + } + + if ( !test( make_command, ps.init_case ) ) + { + fmt::print( "[e] The initial test case does not trigger the buggy behavior\n" ); + return; + } + + uint32_t counter{ 0 }; + while ( !ps.num_iterations || counter++ < ps.num_iterations ) + { + ntk_backup2 = cleanup_dangling( ntk ); + if ( !reduce() ) + { + break; + } + if ( ntk.num_gates() == 0 ) + { + ++stage_counter; + ntk = ntk_backup2; + continue; + } + + write_testcase( "tmp" ); + + if ( test( make_command, "tmp" ) ) + { + fmt::print( "[i] Testcase with I/O = {}/{} gates = {} triggers the buggy behavior\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + write_testcase( ps.minimized_case ); + stage_counter = 0; + sampled.clear(); + } + else + { + ++stage_counter; + ntk = ntk_backup2; + } + } + + if ( init_PIs != ntk.num_pis() || init_POs != ntk.num_pos() || init_gates != ntk.num_gates() ) + { + fmt::print( "[i] Minimized the testcase from I/O = {}/{} gates = {}\n", init_PIs, init_POs, init_gates ); + fmt::print( " to I/O = {}/{} gates = {}\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + } + } +#endif + +private: + bool read_initial_testcase() + { + switch ( ps.file_format ) + { + case testcase_minimizer_params::verilog: + if ( lorina::read_verilog( ps.path + "/" + ps.init_case + file_extension, verilog_reader( ntk ) ) != lorina::return_code::success ) + { + fmt::print( "[e] Could not read test case `{}`\n", ps.path + "/" + ps.init_case + file_extension ); + return false; + } + break; + case testcase_minimizer_params::aiger: + if ( lorina::read_aiger( ps.path + "/" + ps.init_case + file_extension, aiger_reader( ntk ) ) != lorina::return_code::success ) + { + fmt::print( "[e] Could not read test case `{}`\n", ps.path + "/" + ps.init_case + file_extension ); + return false; + } + break; + default: + fmt::print( "[e] Unsupported format\n" ); + return false; + } + init_PIs = ntk.num_pis(); + init_POs = ntk.num_pos(); + init_gates = ntk.num_gates(); + return true; + } + + void write_testcase( std::string const& filename ) + { + switch ( ps.file_format ) + { + case testcase_minimizer_params::verilog: + write_verilog( ntk, ps.path + "/" + filename + file_extension ); + break; + case testcase_minimizer_params::aiger: + write_aiger( ntk, ps.path + "/" + filename + file_extension ); + break; + default: + fmt::print( "[e] Unsupported format\n" ); + } + } + + bool test( std::function const& fn ) + { + ntk_backup = cleanup_dangling( ntk ); + bool res = fn( ntk ); + ntk = ntk_backup; + was_FIT = !res; + return was_FIT; + } + +#ifndef _MSC_VER + bool test( std::function const& make_command, std::string const& filename ) + { + was_FIT = test_inner( make_command, filename ); + return was_FIT; + } + + bool test_inner( std::function const& make_command, std::string const& filename ) + { + std::string const command = make_command( ps.path + "/" + filename + file_extension ); + int status = std::system( command.c_str() ); + if ( status < 0 ) + { + std::cout << "[e] Unexpected error when calling command: " << strerror( errno ) << '\n'; + return false; + } + else + { + if ( WIFEXITED( status ) ) + { + if ( WEXITSTATUS( status ) == 0 ) // normal + return false; + else if ( WEXITSTATUS( status ) == 1 ) // buggy + return true; + else if ( WEXITSTATUS( status ) == 134 ) // assertion fail + return true; + else + { + std::cout << "[e] Unexpected return value: " << WEXITSTATUS( status ) << '\n'; + return false; + } + } + else // segfault + { + return true; + } + } + } +#endif + + bool reduce() + { + pos_to_remove = ntk.num_pos() >> 3; // 1/8 + + if ( ( ps.num_iterations_stage && stage_counter >= *ps.num_iterations_stage ) || + ( reducing_stage == many_pos && pos_to_remove > ntk.num_pos() - sampled.size() ) || + ( reducing_stage == many_pos && pos_to_remove < 2 ) || + ( reducing_stage == po && ntk.num_pos() == 1 ) || + ( reducing_stage == po && sampled.size() == ntk.num_pos() ) || + ( reducing_stage == pi && ntk.num_pis() > ntk.num_pos() ) || + ( reducing_stage == pi && sampled.size() == ntk.num_pis() ) || + ( reducing_stage == const_gate && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == mffc && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == half_mffc && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == simplify_tfo && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == single_gate && sampled.size() == ntk.num_gates() ) ) + { + reducing_stage = static_cast( static_cast( reducing_stage ) + 1 ); + stage_counter = 0; + sampled.clear(); + } + + switch ( reducing_stage ) + { + case many_pos: + { + assert( pos_to_remove <= ntk.num_pos() - sampled.size() ); + if ( ps.verbose ) + fmt::print( "[i] Remove {} POs\n", pos_to_remove ); + for ( auto i = 0u; i < pos_to_remove; ++i ) + { + auto const& [ith_po, n] = get_random_po(); + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + continue; + ntk.substitute_node( n, ntk.get_constant( false ) ); + } + break; + } + case po: + { + auto const& [ith_po, n] = get_random_po(); + if ( ps.verbose ) + fmt::print( "[i] Remove {}-th PO (node {})\n", ith_po, n ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case pi: + { + const node n = get_random_pi(); + if ( ps.verbose ) + fmt::print( "[i] Remove PI {}\n", n ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case const_gate: + { + node const& n = get_random_gate(); + if ( ps.verbose ) + fmt::print( "[i] Substitute gate {} with const0\n", n ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case mffc: + { + node const& n = get_random_gate(); + signal fi = ntk.create_pi(); + if ( ps.verbose ) + fmt::print( "[i] Substitute gate {} with a new PI {}\n", n, ntk.get_node( fi ) ); + ntk.substitute_node( n, fi ); + break; + } + case half_mffc: + { + node const& n = get_random_gate(); + signal fi; + uint32_t const ith_fanin = std::rand() % ntk.fanin_size( n ); + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( i == ith_fanin ) + fi = f; + } ); + if ( ps.verbose ) + fmt::print( "[i] Substitute gate {} with its {}-th fanin {}{}\n", n, ith_fanin, ntk.is_complemented( fi ) ? "!" : "", ntk.get_node( fi ) ); + ntk.substitute_node( n, fi ); + assert( network_is_acyclic( color_view{ ntk } ) ); + break; + } + case simplify_tfo: + { + node const& n = get_random_gate(); + if ( ps.verbose ) + fmt::print( "[i] Simplify TFO of gate {} with const0 but keep all its fanins\n", n ); + ntk.foreach_fanin( n, [&]( auto f ) { + ntk.create_po( f ); + } ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case single_gate: + { + node const& n = get_random_gate(); + if ( ps.verbose ) + fmt::print( "[i] Remove a single gate {}\n", n ); + ntk.foreach_fanin( n, [&]( auto f ) { + ntk.create_po( f ); + } ); + signal fi = ntk.create_pi(); + ntk.substitute_node( n, fi ); + break; + } + default: + { + ntk = cleanup_dangling( ntk, true, true ); + return false; // all stages done, nothing to reduce + } + } + + ntk = cleanup_dangling( ntk, true, true ); + return true; + } + + std::pair get_random_po() + { + while ( true ) + { + uint32_t const ith_po = std::rand() % ntk.num_pos(); + const node n = ntk.get_node( ntk.po_at( ith_po ) ); + if ( sampled.find( ith_po ) == sampled.end() ) + { + sampled.insert( ith_po ); + return std::make_pair( ith_po, n ); + } + } + } + + node get_random_pi() + { + while ( true ) + { + uint32_t const ith_pi = std::rand() % ntk.num_pis() + 1; + if ( sampled.find( ith_pi ) == sampled.end() ) + { + sampled.insert( ith_pi ); + return ntk.index_to_node( ith_pi ); + } + } + } + + node get_random_gate() + { + while ( true ) + { + uint32_t const node_index = ( std::rand() % ntk.num_gates() ) + ntk.num_pis() + 1; + node n = ntk.index_to_node( node_index ); + if ( sampled.find( node_index ) == sampled.end() ) + { + sampled.insert( node_index ); + assert( !ntk.is_dead( n ) && !ntk.is_pi( n ) ); + return n; + } + } + } + + std::pair get_random_gate_with_gate_fanin() + { + while ( true ) + { + node const& n = get_random_gate(); + node ni; + bool has_gate_fanin = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + ni = ntk.get_node( f ); + if ( !ntk.is_pi( ni ) && !ntk.is_constant( ni ) ) + { + has_gate_fanin = true; + return false; // break + } + return true; // next fanin + } ); + if ( has_gate_fanin ) + { + return std::make_pair( n, ni ); + } + } + } + +private: + testcase_minimizer_params const ps; + std::string file_extension; + Ntk ntk, ntk_backup, ntk_backup2; + + enum stages : int + { + pi = 1, // remove a PI (substitute with const0) + many_pos, + po, // remove a PO (substitute with const0) + const_gate, // substitute a gate with const0 + simplify_tfo, // create PO for all of a gate's fanins, then substitute it with const0 + mffc, // substitute a gate with a new PI + half_mffc, // substitute a gate with one of its fanin + single_gate // remove a single gate by creating POs for its fanins and substitute it with a new PI + } reducing_stage{ pi }; + uint32_t stage_counter{ 0u }; + uint32_t pos_to_remove{ 1000 }; + bool was_FIT{ true }; + std::set sampled; + + uint32_t init_PIs, init_POs, init_gates; +}; /* testcase_minimizer */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/window_rewriting.hpp b/third-party/mockturtle/include/mockturtle/algorithms/window_rewriting.hpp new file mode 100644 index 00000000000..e6797ccb6bc --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/window_rewriting.hpp @@ -0,0 +1,799 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file window_rewriting.hpp + \brief Window rewriting + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#include "../networks/aig.hpp" +#include "../networks/events.hpp" +#include "../networks/xag.hpp" +#include "../utils/debugging_utils.hpp" +#include "../utils/index_list/index_list.hpp" +#include "../utils/network_utils.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/window_utils.hpp" +#include "../views/color_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "detail/resub_utils.hpp" +#include "resyn_engines/xag_resyn.hpp" +#include "simulation.hpp" + +#include +#include +#include + +#pragma once + +namespace mockturtle +{ + +struct window_rewriting_params +{ + uint64_t cut_size{ 6 }; + uint64_t num_levels{ 5 }; + + /* Level information guides the windowing construction and as such impacts QoR: + -- dont_update: fastest, but levels are wrong (QoR degrades) + -- eager: fast, some levels are wrong + -- precise: fast, all levels are correct (best QoR) + -- recompute: slow, same as precise (used only for debugging) + */ + enum + { + /* do not update any levels */ + dont_update, + /* eagerly update the levels of changed nodes but avoid + topological sorting (some levels will be wrong) */ + eager, + /* precisely update the levels of changed nodes bottom-to-top and + in topological order */ + precise, + /* recompute all levels (also precise, but more expensive to + compute) */ + recompute, + } level_update_strategy = dont_update; + + uint64_t max_num_divs{ 100 }; + + bool filter_cyclic_substitutions{ false }; +}; /* window_rewriting_params */ + +struct window_rewriting_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for constructing windows. */ + stopwatch<>::duration time_window{ 0 }; + + /*! \brief Time for optimizing windows. */ + stopwatch<>::duration time_optimize{ 0 }; + + /*! \brief Time for substituting. */ + stopwatch<>::duration time_substitute{ 0 }; + + /*! \brief Time for updating level information. */ + stopwatch<>::duration time_levels{ 0 }; + + /*! \brief Time for topological sorting. */ + stopwatch<>::duration time_topo_sort{ 0 }; + + /*! \brief Time for encoding index_list. */ + stopwatch<>::duration time_encode{ 0 }; + + /*! \brief Time for computing dependency circuit. */ + stopwatch<>::duration time_resyn{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_simulate{ 0 }; + + /*! \brief Time for marking TFO and MFFC. */ + stopwatch<>::duration time_mark{ 0 }; + + /*! \brief Time for adding divisor truth tables. */ + stopwatch<>::duration time_add_divisor{ 0 }; + + /*! \brief Time for substitution within windows. */ + stopwatch<>::duration time_window_substitute{ 0 }; + + /*! \brief Time for constructing fanout_view within windows. */ + stopwatch<>::duration time_fanout_view{ 0 }; + + /*! \brief Time for detecting cycles. */ + stopwatch<>::duration time_cycle{ 0 }; + + /*! \brief Total number of calls to the resub. engine. */ + uint64_t num_resyn_invokes{ 0 }; + uint64_t num_substitutions{ 0 }; + uint64_t num_restrashes{ 0 }; + uint64_t num_windows{ 0 }; + uint64_t gain{ 0 }; + + window_rewriting_stats operator+=( window_rewriting_stats const& other ) + { + time_total += other.time_total; + time_window += other.time_window; + time_optimize += other.time_optimize; + time_substitute += other.time_substitute; + time_levels += other.time_levels; + time_topo_sort += other.time_topo_sort; + time_encode += other.time_encode; + time_resyn += other.time_resyn; + time_simulate += other.time_simulate; + time_mark += other.time_mark; + time_add_divisor += other.time_add_divisor; + time_window_substitute += other.time_window_substitute; + time_fanout_view += other.time_fanout_view; + num_substitutions += other.num_substitutions; + num_restrashes += other.num_restrashes; + num_windows += other.num_windows; + num_resyn_invokes += other.num_resyn_invokes; + gain += other.gain; + return *this; + } + + void report() const + { + stopwatch<>::duration time_other = + time_total - time_window - time_topo_sort - time_optimize - time_substitute - time_levels; + + fmt::print( "===========================================================================\n" ); + fmt::print( "[i] Windowing = {:7.2f} ({:5.2f}%) (#win = {})\n", + to_seconds( time_window ), to_seconds( time_window ) / to_seconds( time_total ) * 100, num_windows ); + fmt::print( "[i] Top.sort = {:7.2f} ({:5.2f}%)\n", to_seconds( time_topo_sort ), to_seconds( time_topo_sort ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Enc.list = {:7.2f} ({:5.2f}%)\n", to_seconds( time_encode ), to_seconds( time_encode ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Optimize = {:7.2f} ({:5.2f}%) (#invokes = {}, #resubs = {}, est. gain = {})\n", + to_seconds( time_optimize ), to_seconds( time_optimize ) / to_seconds( time_total ) * 100, num_resyn_invokes, num_substitutions, gain ); + fmt::print( "[i] >> resynthesis = {:7.2f} ({:5.2f}%)\n", to_seconds( time_resyn ), to_seconds( time_resyn ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> simulate = {:7.2f} ({:5.2f}%)\n", to_seconds( time_simulate ), to_seconds( time_simulate ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> marking = {:7.2f} ({:5.2f}%)\n", to_seconds( time_mark ), to_seconds( time_mark ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> add div. = {:7.2f} ({:5.2f}%)\n", to_seconds( time_add_divisor ), to_seconds( time_add_divisor ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> substitute = {:7.2f} ({:5.2f}%)\n", to_seconds( time_window_substitute ), to_seconds( time_window_substitute ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> fanout_view = {:7.2f} ({:5.2f}%)\n", to_seconds( time_fanout_view ), to_seconds( time_fanout_view ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] Substitute = {:7.2f} ({:5.2f}%) (#hash upd. = {})\n", + to_seconds( time_substitute ), + to_seconds( time_substitute ) / to_seconds( time_total ) * 100, + num_restrashes ); + fmt::print( "[i] Upd.levels = {:7.2f} ({:5.2f}%)\n", to_seconds( time_levels ), to_seconds( time_levels ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Other = {:7.2f} ({:5.2f}%)\n", to_seconds( time_other ), to_seconds( time_other ) / to_seconds( time_total ) * 100 ); + fmt::print( "---------------------------------------------------------------------------\n" ); + fmt::print( "[i] TOTAL = {:7.2f}\n", to_seconds( time_total ) ); + fmt::print( "===========================================================================\n" ); + } +}; /* window_rewriting_stats */ + +namespace detail +{ + +template +bool is_contained_in_tfi_recursive( Ntk const& ntk, typename Ntk::node const& node, typename Ntk::node const& n ) +{ + if ( ntk.color( node ) == ntk.current_color() ) + { + return false; + } + ntk.paint( node ); + + if ( n == node ) + { + return true; + } + + bool found = false; + ntk.foreach_fanin( node, [&]( typename Ntk::signal const& fi ) { + if ( is_contained_in_tfi_recursive( ntk, ntk.get_node( fi ), n ) ) + { + found = true; + return false; + } + return true; + } ); + + return found; +} + +} /* namespace detail */ + +template +bool is_contained_in_tfi( Ntk const& ntk, typename Ntk::node const& node, typename Ntk::node const& n ) +{ + /* do not even build the TFI, but just search for the node */ + ntk.new_color(); + return detail::is_contained_in_tfi_recursive( ntk, node, n ); +} + +namespace detail +{ + +template +struct resyn_sparams : public xag_resyn_static_params +{ + using truth_table_storage_type = node_map; + using node_type = typename NtkWin::signal; + static constexpr bool use_xor = false; +}; + +template>> +class window_rewriting_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit window_rewriting_impl( Ntk& ntk, window_rewriting_params const& ps, window_rewriting_stats& st ) + : ntk( ntk ), ps( ps ), st( st ) + /* initialize levels to network depth */ + , + levels( ntk.depth() ), engine( engine_st ) + { + register_events(); + } + + ~window_rewriting_impl() + { + ntk.events().release_add_event( add_event ); + ntk.events().release_modified_event( modified_event ); + ntk.events().release_delete_event( delete_event ); + } + + void run() + { + stopwatch t( st.time_total ); + + if constexpr ( std::is_same_v ) + { + sim = new default_simulator( ps.cut_size ); + } + else + { + sim = new default_simulator(); + } + + create_window_impl windowing( ntk ); + uint32_t const size = ntk.size(); + for ( uint32_t n = 0u; n < size; ++n ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) || ntk.is_dead( n ) ) + { + continue; + } + + if ( auto w = call_with_stopwatch( st.time_window, [&]() { return windowing.run( n, ps.cut_size, ps.num_levels ); } ) ) + { + ++st.num_windows; + + NtkWin win; + call_with_stopwatch( st.time_encode, [&]() { + clone_subnetwork( ntk, w->inputs, w->outputs, w->nodes, win ); + } ); + + if ( !optimize( win ) ) + { + continue; + } + + std::vector signals; + for ( auto const& i : w->inputs ) + { + signals.push_back( ntk.make_signal( i ) ); + } + + uint32_t counter{ 0 }; + ++st.num_substitutions; + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + + std::list> substitutions; + insert_ntk( ntk, std::begin( signals ), std::end( signals ), win, + [&]( signal const& _new ) { + assert( !ntk.is_dead( ntk.get_node( _new ) ) ); + auto const _old = w->outputs.at( counter++ ); + if ( _old == _new ) + { + return true; + } + + /* ensure that _old is not in the TFI of _new */ + // assert( !is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ); + if ( ps.filter_cyclic_substitutions && + call_with_stopwatch( st.time_window, [&]() { return is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ); } ) ) + { + std::cout << "undo resubstitution " << ntk.get_node( _old ) << std::endl; + substitutions.emplace_back( std::make_pair( ntk.get_node( _old ), ntk.is_complemented( _old ) ? !_new : _new ) ); + for ( auto it = std::rbegin( substitutions ); it != std::rend( substitutions ); ++it ) + { + if ( ntk.fanout_size( ntk.get_node( it->second ) ) == 0u ) + { + ntk.take_out_node( ntk.get_node( it->second ) ); + } + } + substitutions.clear(); + return false; + } + + substitutions.emplace_back( std::make_pair( ntk.get_node( _old ), ntk.is_complemented( _old ) ? !_new : _new ) ); + return true; + } ); + + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + substitute_nodes( substitutions ); + + /* recompute levels and depth */ + if ( ps.level_update_strategy == window_rewriting_params::recompute ) + { + call_with_stopwatch( st.time_levels, [&]() { ntk.update_levels(); } ); + } + if ( ps.level_update_strategy != window_rewriting_params::dont_update ) + { + update_depth(); + } + + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + + /* ensure that the network structure is still acyclic */ + assert( network_is_acyclic( ntk ) ); + + if ( ps.level_update_strategy == window_rewriting_params::precise || + ps.level_update_strategy == window_rewriting_params::recompute ) + { + /* ensure that the levels and depth is correct */ + assert( check_network_levels( ntk ) ); + } + + /* update internal data structures in windowing */ + windowing.resize( ntk.size() ); + } + } + + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + + delete sim; + } + +private: + void register_events() + { + auto const update_level_of_new_node = [&]( const auto& n ) { + stopwatch t( st.time_total ); + update_levels( n ); + }; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + stopwatch t( st.time_total ); + update_levels( n ); + }; + + auto const update_level_of_deleted_node = [&]( node const& n ) { + stopwatch t( st.time_total ); + assert( ntk.fanout_size( n ) == 0u ); + assert( ntk.is_dead( n ) ); + ntk.set_level( n, -1 ); + }; + + add_event = ntk.events().register_add_event( update_level_of_new_node ); + modified_event = ntk.events().register_modified_event( update_level_of_existing_node ); + delete_event = ntk.events().register_delete_event( update_level_of_deleted_node ); + } + + bool optimize( NtkWin& win ) + { + stopwatch t( st.time_optimize ); + bool changed = false; + + node_map tts = call_with_stopwatch( st.time_simulate, [&]() { + return simulate_nodes( win, *sim ); + } ); + auto win_add_event = win.events().register_add_event( [&]( auto const& n ) { + call_with_stopwatch( st.time_simulate, [&]() { + tts.resize(); + std::vector fanin_values( win.fanin_size( n ) ); + win.foreach_fanin( n, [&]( auto const& f, auto i ) { + fanin_values[i] = tts[f]; + } ); + tts[n] = win.compute( n, fanin_values.begin(), fanin_values.end() ); + } ); + } ); + fanout_view fanout_win = make_with_stopwatch, NtkWin&>( st.time_fanout_view, win ); + // fanout_view fanout_win{win}; + + win.foreach_po( [&]( auto const& f ) { + auto root = win.get_node( f ); + if ( win.value( root ) != 1 ) + { + win.set_value( root, 1 ); + changed |= optimize_node( win, fanout_win, tts, root ); + } + } ); + + win.foreach_gate( [&]( auto const& root ) { + if ( win.value( root ) != 1 ) + { + win.set_value( root, 1 ); + bool all_fanin_is_pi = true; + win.foreach_fanin( root, [&]( auto const& fi ) { + if ( !win.is_pi( win.get_node( fi ) ) ) + { + all_fanin_is_pi = false; + } + } ); + if ( !all_fanin_is_pi ) + changed |= optimize_node( win, fanout_win, tts, root ); + } + } ); + + win.events().release_add_event( win_add_event ); + return changed; + } + + bool optimize_node( NtkWin& win, fanout_view& fanout_win, node_map& tts, typename NtkWin::node const& root ) + { + st.num_resyn_invokes++; + + auto mffc_size = call_with_stopwatch( st.time_mark, [&]() { + /* mark MFFC */ + std::vector mffc; + node_mffc_inside mffc_mgr( win ); + auto mffc_size = mffc_mgr.run( root, {}, mffc ); + win.incr_trav_id(); + for ( auto const& n : mffc ) + { + win.set_visited( n, win.trav_id() ); + } + /* mark TFO */ + mark_tfo( fanout_win, root ); + + /* exclude constant node */ + if constexpr ( std::is_same_v || std::is_same_v ) + { + win.set_visited( win.get_node( win.get_constant( false ) ), win.trav_id() ); + } + return mffc_size; + } ); + + /* add divisors (all nodes in the window except TFO and MFFC) */ + std::vector divs; + call_with_stopwatch( st.time_add_divisor, [&]() { + win.foreach_node( [&]( auto const& n ) { + if ( win.visited( n ) != win.trav_id() ) + { + divs.emplace_back( win.make_signal( n ) ); + if ( divs.size() > ps.max_num_divs ) + { + return false; + } + } + return true; + } ); + } ); + + /* run resynthesis */ + auto const il = call_with_stopwatch( st.time_resyn, [&]() { + return engine( tts[root], ~tts[win.get_constant( false )], divs.begin(), divs.end(), tts, mffc_size - 1 ); + } ); + if ( il ) + { + st.gain += mffc_size - il->num_gates(); + call_with_stopwatch( st.time_window_substitute, [&]() { + insert( win, divs.begin(), divs.end(), *il, [&]( auto const& s ) { + win.substitute_node( root, s ); + } ); + } ); + return true; + } + return false; + } + + void mark_tfo( fanout_view& fanout_win, typename NtkWin::node const& n ) + { + fanout_win.set_visited( n, fanout_win.trav_id() ); + fanout_win.foreach_fanout( n, [&]( auto const& fo ) { + if ( fanout_win.visited( fo ) != fanout_win.trav_id() ) + { + mark_tfo( fanout_win, fo ); + } + } ); + } + +private: + void substitute_nodes( std::list> substitutions ) + { + stopwatch t( st.time_substitute ); + + auto clean_substitutions = [&]( node const& n ) { + substitutions.erase( std::remove_if( std::begin( substitutions ), std::end( substitutions ), + [&]( auto const& s ) { + if ( s.first == n ) + { + node const nn = ntk.get_node( s.second ); + if ( ntk.is_dead( nn ) ) + return true; + + /* deref fanout_size of the node */ + if ( ntk.fanout_size( nn ) > 0 ) + { + ntk.decr_fanout_size( nn ); + } + /* remove the node if its fanout_size becomes 0 */ + if ( ntk.fanout_size( nn ) == 0 ) + { + ntk.take_out_node( nn ); + } + /* remove substitution from list */ + return true; + } + return false; /* keep */ + } ), + std::end( substitutions ) ); + }; + + /* register event to delete substitutions if their right-hand side + nodes get deleted */ + auto clean_subs_event = ntk.events().register_delete_event( clean_substitutions ); + + /* increment fanout_size of all signals to be used in + substitutions to ensure that they will not be deleted */ + for ( const auto& s : substitutions ) + { + ntk.incr_fanout_size( ntk.get_node( s.second ) ); + } + + while ( !substitutions.empty() ) + { + auto const [old_node, new_signal] = substitutions.front(); + substitutions.pop_front(); + + for ( auto index : ntk.fanout( old_node ) ) + { + /* skip CIs and dead nodes */ + if ( ntk.is_dead( index ) ) + { + continue; + } + + /* skip nodes that will be deleted */ + if ( std::find_if( std::begin( substitutions ), std::end( substitutions ), + [&index]( auto s ) { return s.first == index; } ) != std::end( substitutions ) ) + { + continue; + } + + /* replace in node */ + if ( const auto repl = ntk.replace_in_node( index, old_node, new_signal ); repl ) + { + ntk.incr_fanout_size( ntk.get_node( repl->second ) ); + substitutions.emplace_back( *repl ); + ++st.num_restrashes; + } + } + + /* replace in outputs */ + ntk.replace_in_outputs( old_node, new_signal ); + + /* replace in substitutions */ + for ( auto& s : substitutions ) + { + if ( ntk.get_node( s.second ) == old_node ) + { + s.second = ntk.is_complemented( s.second ) ? !new_signal : new_signal; + ntk.incr_fanout_size( ntk.get_node( new_signal ) ); + } + } + + /* finally remove the node: note that we never decrement the + fanout_size of the old_node. instead, we remove the node and + reset its fanout_size to 0 knowing that it must be 0 after + substituting all references. */ + assert( !ntk.is_dead( old_node ) ); + ntk.take_out_node( old_node ); + + /* decrement fanout_size when released from substitution list */ + ntk.decr_fanout_size( ntk.get_node( new_signal ) ); + if ( ntk.fanout_size( ntk.get_node( new_signal ) ) == 0 ) + { + ntk.take_out_node( ntk.get_node( new_signal ) ); + } + } + + ntk.events().release_delete_event( clean_subs_event ); + } + + void update_levels( node const& n ) + { + ntk.resize_levels(); + if ( ps.level_update_strategy == window_rewriting_params::precise ) + { + call_with_stopwatch( st.time_levels, [&]() { update_node_level_precise( n ); } ); + } + else if ( ps.level_update_strategy == window_rewriting_params::eager ) + { + call_with_stopwatch( st.time_levels, [&]() { update_node_level_eager( n ); } ); + } + + /* levels can be wrong until substitute_nodes has finished */ + // assert( check_network_levels( ntk ) ); + } + + /* precisely update node levels using an iterative topological sorting approach */ + void update_node_level_precise( node const& n ) + { + assert( count_reachable_dead_nodes_from_node( ntk, n ) == 0u ); + // assert( count_nodes_with_dead_fanins( ntk, n ) == 0u ); + + /* compute level of current node */ + uint32_t level_offset{ 0 }; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + level_offset = std::max( ntk.level( ntk.get_node( fi ) ), level_offset ); + } ); + ++level_offset; + + /* add node into levels */ + if ( levels.size() < 1u ) + { + levels.resize( 1u ); + } + levels[0].emplace_back( n ); + + for ( uint32_t level_index = 0u; level_index < levels.size(); ++level_index ) + { + if ( levels[level_index].empty() ) + continue; + + for ( uint32_t node_index = 0u; node_index < levels[level_index].size(); ++node_index ) + { + node const p = levels[level_index][node_index]; + + /* recompute level of this node */ + uint32_t lvl{ 0 }; + ntk.foreach_fanin( p, [&]( signal const& fi ) { + if ( ntk.is_dead( ntk.get_node( fi ) ) ) + return; + + lvl = std::max( ntk.level( ntk.get_node( fi ) ), lvl ); + return; + } ); + ++lvl; + assert( lvl > 0 ); + + /* update level and add fanouts to levels[.] if the recomputed + level is different from the current level */ + if ( lvl != ntk.level( p ) ) + { + ntk.set_level( p, lvl ); + ntk.foreach_fanout( p, [&]( node const& fo ) { + assert( std::max( ntk.level( fo ), lvl + 1 ) >= level_offset ); + uint32_t const pos = std::max( ntk.level( fo ), lvl + 1 ) - level_offset; + assert( pos >= 0u ); + assert( pos >= level_index ); + if ( levels.size() <= pos ) + { + levels.resize( std::max( uint32_t( levels.size() << 1 ), pos + 1 ) ); + } + levels[pos].emplace_back( fo ); + } ); + } + } + + /* clean the level */ + levels[level_index].clear(); + } + levels.clear(); + } + + /* eagerly update the node levels without topologically sorting (may + stack-overflow if the network is deep)*/ + void update_node_level_eager( node const& n ) + { + uint32_t const curr_level = ntk.level( n ); + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + ntk.foreach_fanout( n, [&]( const auto& p ) { + if ( !ntk.is_dead( p ) ) + { + update_node_level_eager( p ); + } + } ); + } + } + + /* update network depth (needs level information!) */ + void update_depth() + { + stopwatch t( st.time_levels ); + + uint32_t max_level{ 0 }; + ntk.foreach_co( [&]( signal const& s ) { + assert( !ntk.is_dead( ntk.get_node( s ) ) ); + max_level = std::max( ntk.level( ntk.get_node( s ) ), max_level ); + } ); + + if ( ntk.depth() != max_level ) + { + ntk.set_depth( max_level ); + } + } + +private: + Ntk& ntk; + window_rewriting_params ps; + window_rewriting_stats& st; + + std::vector> levels; + + /* events */ + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; + + default_simulator* sim; + typename ResynEngine::stats engine_st; + ResynEngine engine; +}; /* window_rewriting_impl */ + +} /* namespace detail */ + +template +void window_rewriting( Ntk& ntk, window_rewriting_params const& ps = {}, window_rewriting_stats* pst = nullptr ) +{ + fanout_view fntk{ ntk }; + depth_view dntk{ fntk }; + color_view cntk{ dntk }; + + window_rewriting_stats st; + using NtkWin = typename Ntk::base_type; + using TT = kitty::dynamic_truth_table; + detail::window_rewriting_impl( cntk, ps, st ).run(); + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xag_algebraic_rewriting.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xag_algebraic_rewriting.hpp new file mode 100644 index 00000000000..358ea7571b9 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xag_algebraic_rewriting.hpp @@ -0,0 +1,565 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_algebraic_rewriting.hpp + \brief xag algebraric rewriting + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for xag_algebraic_depth_rewriting. + * + * The data structure `xag_algebraic_depth_rewriting_params` holds configurable + * parameters with default arguments for `xag_algebraic_depth_rewriting`. + */ +struct xag_algebraic_depth_rewriting_params +{ + /*! \brief Rewriting strategy. */ + enum strategy_t + { + /*! \brief DFS rewriting strategy. + * + * Applies depth rewriting once to all output cones whose drivers have + * maximum levels + */ + dfs, + /*! \brief Aggressive rewriting strategy. + * + * Applies depth reduction multiple times until the number of nodes, which + * cannot be rewritten, matches the number of nodes, in the current + * network; or the new network size is larger than the initial size w.r.t. + * to an `overhead`. + */ + aggressive, + /*! \brief Selective rewriting strategy. + * + * Like `aggressive`, but only applies rewriting to nodes on critical paths + * and without `overhead`. + */ + selective + } strategy = dfs; + + /*! \brief Overhead factor in aggressive rewriting strategy. + * + * When comparing to the initial size in aggressive depth rewriting, also the + * number of dangling nodes are taken into account. + */ + float overhead{ 2.0f }; + + /*! \brief Allow area increase while optimizing depth. */ + bool allow_area_increase{ true }; + + /*! \brief Try rules that are rarely applied. */ + bool allow_rare_rules{ false }; +}; + +namespace detail +{ + +template +class xag_algebraic_depth_rewriting_impl +{ +public: + xag_algebraic_depth_rewriting_impl( Ntk& ntk, xag_algebraic_depth_rewriting_params const& ps ) + : ntk( ntk ), ps( ps ) + { + } + + void run() + { + switch ( ps.strategy ) + { + case xag_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case xag_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case xag_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [&]( auto n ) { + bool res = reduce_depth_and_associativity( n ); + res |= reduce_depth_xor_associativity( n ); + + if ( ps.allow_area_increase && !res ) + { + reduce_depth_and_or_distributivity( n ); + + reduce_depth_and_xor_distributivity( n ); + } + + if ( ps.allow_rare_rules && !res ) + res = reduce_depth_and_distributity( n ); + + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + + bool res = reduce_depth_and_associativity( n ); + res |= reduce_depth_xor_associativity( n ); + + if ( ps.allow_area_increase && !res ) + { + reduce_depth_and_or_distributivity( n ); + + reduce_depth_and_xor_distributivity( n ); + } + + if ( ps.allow_rare_rules && !res ) + res = reduce_depth_and_distributity( n ); + + if ( res ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + bool res = reduce_depth_and_associativity( n ); + res |= reduce_depth_xor_associativity( n ); + + if ( ps.allow_area_increase && !res ) + { + reduce_depth_and_or_distributivity( n ); + + reduce_depth_and_xor_distributivity( n ); + } + + if ( ps.allow_rare_rules && !res ) + res = reduce_depth_and_distributity( n ); + + if ( !res ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + /* AND associativity */ + bool reduce_depth_and_associativity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_and( ntk.get_node( ocs[1] ) ) || ntk.is_complemented( ocs[1] ) ) + return false; + + /* depth of second child must be (significantly) higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) + return false; + + /* get children of second child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + auto opt = ntk.create_and( ocs1[1], ntk.create_and( ocs[0], ocs1[0] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* XOR associativity */ + bool reduce_depth_xor_associativity( node const& n ) + { + if ( !ntk.is_xor( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor( ntk.get_node( ocs[1] ) ) ) + return false; + + /* depth of second child must be (significantly) higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + auto opt = ntk.create_xor( ocs1[1], ntk.create_xor( ocs[0], ocs1[0] ) ); + + if ( ntk.is_complemented( ocs[1] ) ) + opt = !opt; + + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* AND distributivity */ + bool reduce_depth_and_distributity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_and( ntk.get_node( ocs[0] ) ) || !ntk.is_complemented( ocs[0] ) ) + return false; + + if ( !ntk.is_and( ntk.get_node( ocs[1] ) ) || !ntk.is_complemented( ocs[1] ) ) + return false; + + /* children must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) != 1 || ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) ) + return false; + + /* get children of first child */ + auto ocs0 = ordered_children( ntk.get_node( ocs[0] ) ); + + /* get children of second child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + /* find common support */ + bool critical_common = false; + signal common, x, y; + if ( ocs0[0] == ocs1[0] ) + { + common = ocs0[0]; + x = ocs0[1]; + y = ocs1[1]; + } + else if ( ocs0[0] == ocs1[1] ) + { + common = ocs0[0]; + x = ocs0[1]; + y = ocs1[0]; + } + else if ( ocs0[1] == ocs1[0] ) + { + common = ocs0[1]; + x = ocs0[0]; + y = ocs1[1]; + } + else if ( ocs0[1] == ocs1[1] ) + { + common = ocs0[1]; + x = ocs0[0]; + y = ocs1[0]; + critical_common = true; + } + else + { + return false; + } + + /* common signal is not critical, children must have single fanout, to not increase the area */ + if ( !critical_common && ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) != 1 || ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) ) + return false; + + auto opt = !ntk.create_and( common, !ntk.create_and( !x, !y ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* AND-OR distributivity */ + bool reduce_depth_and_or_distributivity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) < 3 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_and( ntk.get_node( ocs[1] ) ) || !ntk.is_complemented( ocs[1] ) ) + return false; + + /* depth of second child must be significantly higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 2 ) + return false; + + /* get children of last child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + if ( !ntk.is_and( ntk.get_node( ocs1[1] ) ) || !ntk.is_complemented( ocs1[1] ) ) + return false; + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + /* get children of last grand-child */ + auto ocs11 = ordered_children( ntk.get_node( ocs1[1] ) ); + + /* depth of second grand-grand-child must be higher than depth of first grand-grand-child */ + if ( ntk.level( ntk.get_node( ocs11[1] ) ) == ntk.level( ntk.get_node( ocs11[0] ) ) ) + return false; + + auto opt = !ntk.create_and( !ntk.create_and( ocs[0], !ocs1[0] ), !ntk.create_and( ntk.create_and( ocs[0], ocs11[0] ), ocs11[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* AND-XOR distributivity */ + bool reduce_depth_and_xor_distributivity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) < 3 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor( ntk.get_node( ocs[1] ) ) ) + return false; + + /* depth of second child must be significantly higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 2 ) + return false; + + /* get children of last child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + if ( !ntk.is_and( ntk.get_node( ocs1[1] ) ) ) + return false; + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + /* get children of last grand-child */ + auto ocs11 = ordered_children( ntk.get_node( ocs1[1] ) ); + + /* depth of second grand-grand-child must be higher than depth of first grand-grand-child */ + if ( ntk.level( ntk.get_node( ocs11[1] ) ) == ntk.level( ntk.get_node( ocs11[0] ) ) ) + return false; + + /* if XOR is complemented, complement first grand-child */ + if ( ntk.is_complemented( ocs[1] ) != ntk.is_complemented( ocs1[1] ) ) + { + ocs1[0] = !ocs1[0]; + } + + auto opt = ntk.create_xor( ntk.create_and( ocs[0], ocs1[0] ), ntk.create_and( ntk.create_and( ocs[0], ocs11[0] ), ocs11[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + inline std::array, 2> ordered_children( node const& n ) const + { + std::array, 2> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { + children[i] = f; + } ); + if ( ntk.level( ntk.get_node( children[0] ) ) > ntk.level( ntk.get_node( children[1] ) ) ) + { + std::swap( children[0], children[1] ); + } + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + xag_algebraic_depth_rewriting_params const& ps; +}; + +} // namespace detail + +/*! \brief XAG algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with AND/XOR gates for depth + * optimization using the associativity and distributivity rule in + * AND-XOR logic. It can be applied to networks other than XAGs, but + * only considers pairs of nodes which both implement the AND + * function and the XOR function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_and` + * - `create_xor` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_and` + * - `is_xor` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + \endverbatim + */ +template +void xag_algebraic_depth_rewriting( Ntk& ntk, xag_algebraic_depth_rewriting_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_maj method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + detail::xag_algebraic_depth_rewriting_impl p( ntk, ps ); + p.run(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xag_balancing.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xag_balancing.hpp new file mode 100644 index 00000000000..e3e6cfcd95d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xag_balancing.hpp @@ -0,0 +1,729 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_balancing.hpp + \brief Balances the XAG to reduce the depth + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +#include "cleanup.hpp" +#include "../networks/aig.hpp" +#include "../traits.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" + +namespace mockturtle +{ + +struct xag_balancing_params +{ + /*! \brief Minimizes the number of levels. */ + bool minimize_levels{ true }; + + /*! \brief Use fast version, it may not find some area optimizations. */ + bool fast_mode{ true }; +}; + +namespace detail +{ + +template +class xag_balance_impl +{ +public: + static constexpr size_t storage_init_size = 30; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using storage_t = std::vector>; + +public: + xag_balance_impl( Ntk& ntk, xag_balancing_params const& ps ) + : ntk( ntk ), ps( ps ), storage( storage_init_size ) + { + } + + void run() + { + ntk.clear_values(); + + for ( auto i = 0; i < storage_init_size; ++i ) + storage[i].reserve( 10 ); + + /* balance every CO */ + ntk.foreach_co( [&]( auto const& f ) { + balance_rec( ntk.get_node( f ), 0 ); + } ); + } + +private: + signal balance_rec( node const& n, uint32_t level ) + { + if ( ntk.is_ci( n ) ) + return ntk.make_signal( n ); + + /* node has been replaced in a previous recursion */ + if ( ntk.is_dead( n ) || ntk.value( n ) > 0 ) + { + return ntk.make_signal( find_substituted_node( n ) ); + } + + if ( level >= storage.size() ) + { + storage.emplace_back( std::vector() ); + storage.back().reserve( 10 ); + } + + /* collect leaves of the AND or XOR trees */ + bool polarity = false; + bool is_and = true; + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_and( n ) ) + { + collect_leaves_and( n, storage[level] ); + } + else + { + polarity = collect_leaves_xor( n, storage[level] ); + is_and = false; + } + } + else + { + collect_leaves_and( n, storage[level] ); + } + + if ( storage[level].size() == 0 ) + { + ntk.substitute_node( n, ntk.get_constant( polarity ) ); + return ntk.get_constant( polarity ); + } + + /* recur over the leaves */ + for ( auto& f : storage[level] ) + { + signal new_signal = balance_rec( ntk.get_node( f ), level + 1 ); + f = new_signal ^ ntk.is_complemented( f ); + } + + assert( storage[level].size() > 1 ); + + /* sort by decreasing level */ + std::stable_sort( storage[level].begin(), storage[level].end(), [this]( auto const& a, auto const& b ) { + return ntk.level( ntk.get_node( a ) ) > ntk.level( ntk.get_node( b ) ); + } ); + + /* mark TFI cone of n */ + ntk.incr_trav_id(); + mark_tfi( ntk.make_signal( n ), true ); + + /* generate the AND or XOR tree */ + if ( is_and ) + { + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + if ( ps.fast_mode ) + { + if ( ps.minimize_levels ) + pick_nodes_and_fast( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_and_area_fast( storage[level] ); + } + else + { + if ( ps.minimize_levels ) + pick_nodes_and( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_and_area( storage[level] ); + } + + /* pop the two selected nodes to create the new AND gate */ + signal child1 = storage[level].back(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_and( child1, child2 ); + + /* update level for AND node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted_and( storage[level], new_sig ); + } + } + else + { + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + if ( ps.fast_mode ) + { + if ( ps.minimize_levels ) + pick_nodes_xor_fast( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_xor_area_fast( storage[level] ); + } + else + { + if ( ps.minimize_levels ) + pick_nodes_xor( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_xor_area( storage[level] ); + } + + /* pop the two selected nodes to create the new XOR gate */ + signal child1 = storage[level].back(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_xor( child1, child2 ); + + /* update level for XOR node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted_xor( storage[level], new_sig, polarity ); + } + } + + signal root = storage[level][0] ^ polarity; + + /* replace if new */ + if ( n != ntk.get_node( root ) ) + { + ntk.substitute_node_no_restrash( n, root ); + } + + /* remember the substitution and the new node as already balanced */ + ntk.set_value( n, ntk.node_to_index( ntk.get_node( root ) ) ); + ntk.set_value( ntk.get_node( root ), ntk.node_to_index( ntk.get_node( root ) ) ); + + /* clean leaves storage */ + storage[level].clear(); + + return root; + } + + void collect_leaves_and( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_and_rec( ntk.make_signal( n ), leaves, true ); + + /* check for constant false */ + if ( ret < 0 ) + { + leaves.clear(); + } + } + + int collect_leaves_and_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + for ( signal const& s : leaves ) + { + if ( ntk.get_node( s ) != n ) + continue; + + if ( s == f ) + return 1; /* same polarity: duplicate */ + else + return -1; /* opposite polarity: const0 */ + } + + return 0; + } + + /* set as leaf if signal is complemented or is a XOR or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_xor( n ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret |= collect_leaves_and_rec( child, leaves, false ); + } ); + + return ret; + } + + bool collect_leaves_xor( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_xor_rec( ntk.make_signal( n ), leaves, true ); + + /* return top polarity */ + return ret ? true : false; + } + + int collect_leaves_xor_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + auto it = leaves.begin(); + while ( it != leaves.end() ) + { + if ( ntk.get_node( *it ) != n ) + { + ++it; + continue; + } + + /* remove node (XOR property) */ + if ( ntk.get_node( *it ) == n ) + leaves.erase( it ); + + return 0; + } + + return 0; + } + + /* set as leaf if signal is an AND or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_and( n ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ^ ntk.is_complemented( f ) ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret ^= ntk.is_complemented( child ) ? 1 : 0; + ret ^= collect_leaves_xor_rec( child, leaves, false ); + } ); + + return ret; + } + + size_t find_left_most_at_level( std::vector const& leaves ) + { + size_t pointer = leaves.size() - 1; + uint32_t current_level = ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ); + + while ( pointer > 0 ) + { + if ( ntk.level( ntk.get_node( leaves[pointer - 1] ) ) > current_level ) + break; + + --pointer; + } + + assert( ntk.level( ntk.get_node( leaves[pointer] ) ) == current_level ); + return pointer; + } + + inline void pick_nodes_and( std::vector& leaves, size_t left_most ) + { + size_t right_most = leaves.size() - 2; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + inline void pick_nodes_and_fast( std::vector& leaves, size_t left_most ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_and_area( std::vector& leaves ) + { + for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + inline void pick_nodes_and_area_fast( std::vector& leaves ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_xor( std::vector& leaves, size_t left_most ) + { + size_t right_most = leaves.size() - 2; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + inline void pick_nodes_xor_fast( std::vector& leaves, size_t left_most ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_xor_area( std::vector& leaves ) + { + for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + inline void pick_nodes_xor_area_fast( std::vector& leaves ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + void insert_node_sorted_and( std::vector& leaves, signal const& f ) + { + node n = ntk.get_node( f ); + + /* check uniqueness */ + for ( auto const& s : leaves ) + { + if ( s == f ) + return; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void insert_node_sorted_xor( std::vector& leaves, signal const& f, bool& polarity ) + { + node n = ntk.get_node( f ); + + /* check uniqueness */ + auto it = leaves.begin(); + while ( it != leaves.end() ) + { + if ( ntk.get_node( *it ) == ntk.get_node( f ) ) + { + polarity ^= ntk.is_complemented( f ) ^ ntk.is_complemented( *it ); + leaves.erase( it ); + return; + } + + ++it; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void update_level( node const& n ) + { + uint32_t l = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + l = std::max( l, ntk.level( ntk.get_node( f ) ) ); + } ); + + ntk.set_level( n, l + 1 ); + } + + node find_substituted_node( node n ) + { + while ( ntk.is_dead( n ) ) + n = ntk.index_to_node( ntk.value( n ) ); + + return n; + } + + void mark_tfi( signal const& f, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + + /* set as leaf if signal is complemented or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + return; + } + + ntk.foreach_fanin( n, [&]( auto const& child ) { + mark_tfi( child, false ); + } ); + } + +private: + Ntk& ntk; + xag_balancing_params const& ps; + + storage_t storage; +}; + +} /* namespace detail */ + +/*! \brief XAG balancing. + * + * This method balance the XAG to reduce the + * depth. Level minimization can be turned off. + * In this case, balancing tries to reconstruct + * AND and XOR trees such that logic sharing is maximized. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + * - `has_and` + */ +template +void xag_balance( Ntk& ntk, xag_balancing_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_clone_node_v, "Ntk does not implement the clone_node method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_has_and_v, "Ntk does not implement the has_and method" ); + static_assert( has_has_xor_v, "Ntk does not implement the has_xor method" ); + + fanout_view f_ntk{ ntk }; + depth_view> d_ntk{ f_ntk }; + + detail::xag_balance_impl p( d_ntk, ps ); + p.run(); + + ntk = cleanup_dangling( ntk ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xag_optimization.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xag_optimization.hpp new file mode 100644 index 00000000000..d07ac240c94 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xag_optimization.hpp @@ -0,0 +1,273 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_optimization.hpp + \brief Various XAG optimization algorithms + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken + \author Zhufei Chu +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../algorithms/extract_linear.hpp" +#include "../algorithms/linear_resynthesis.hpp" +#include "../io/write_verilog.hpp" +#include "../networks/xag.hpp" +#include "../properties/mccost.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "dont_cares.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +class xag_constant_fanin_optimization_impl +{ +public: + xag_constant_fanin_optimization_impl( xag_network const& xag ) + : xag( xag ) + { + } + + xag_network run() + { + xag_network dest; + + node_map old2new( xag ); + node_map, xag_network> lfi( xag ); + + old2new[xag.get_node( xag.get_constant( false ) )] = dest.get_constant( false ); + if ( xag.get_node( xag.get_constant( true ) ) != xag.get_node( xag.get_constant( false ) ) ) + { + old2new[xag.get_node( xag.get_constant( true ) )] = dest.get_constant( true ); + } + xag.foreach_pi( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + lfi[n].emplace_back( n ); + } ); + topo_view topo{ xag }; + topo.foreach_node( [&]( auto const& n ) { + if ( xag.is_constant( n ) || xag.is_pi( n ) ) + return; + + if ( xag.is_xor( n ) ) + { + std::array children{}; + std::array*, 2> clfi{}; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = &old2new[f]; + clfi[i] = &lfi[f]; + } ); + lfi[n] = merge( *clfi[0], *clfi[1] ); + if ( lfi[n].size() == 0 ) + { + old2new[n] = dest.get_constant( false ); + } + else if ( lfi[n].size() == 1 ) + { + old2new[n] = old2new[lfi[n].front()]; + } + else + { + old2new[n] = dest.create_xor( *children[0], *children[1] ); + } + } + else /* is AND */ + { + lfi[n].emplace_back( n ); + std::vector children; + xag.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ xag.is_complemented( f ) ); + } ); + old2new[n] = dest.create_and( children[0], children[1] ); + } + } ); + + xag.foreach_po( [&]( auto const& f ) { + dest.create_po( old2new[f] ^ xag.is_complemented( f ) ); + } ); + + return cleanup_dangling( dest ); + } + +private: + std::vector merge( std::vector const& s1, std::vector const& s2 ) const + { + std::vector s; + std::set_symmetric_difference( s1.cbegin(), s1.cend(), s2.cbegin(), s2.cend(), std::back_inserter( s ) ); + return s; + } + +private: + xag_network const& xag; +}; + +} // namespace detail + +/*! \brief Optimizes some AND gates by computing transitive linear fanin + * + * This function reevaluates the transitive linear fanin for each AND gate. + * This is a subnetwork composed of all immediate XOR gates in the transitive + * fanin cone until primary inputs or AND gates are reached. This linear + * transitive fanin might be constant for some fanin due to the cancellation + * property of the XOR operation. In such cases the AND gate can be replaced + * by a constant or a fanin. + */ +inline xag_network xag_constant_fanin_optimization( xag_network const& xag ) +{ + return detail::xag_constant_fanin_optimization_impl( xag ).run(); +} + +/*! \brief Optimizes some AND gates using satisfiability don't cares + * + * If an AND gate is satisfiability don't care for assignment 00, it can be + * replaced by an XNOR gate, therefore reducing the multiplicative complexity. + */ +inline xag_network xag_dont_cares_optimization( xag_network const& xag ) +{ + node_map old_to_new( xag ); + + xag_network dest; + old_to_new[xag.get_constant( false )] = dest.get_constant( false ); + + xag.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = dest.create_pi(); + } ); + + satisfiability_dont_cares_checker checker( xag ); + + topo_view{ xag }.foreach_node( [&]( auto const& n ) { + if ( xag.is_constant( n ) || xag.is_pi( n ) ) + return; + + std::array fanin{}; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + fanin[i] = old_to_new[f] ^ xag.is_complemented( f ); + } ); + + if ( xag.is_and( n ) ) + { + if ( checker.is_dont_care( n, { false, false } ) ) + { + old_to_new[n] = dest.create_xnor( fanin[0], fanin[1] ); + } + else + { + old_to_new[n] = dest.create_and( fanin[0], fanin[1] ); + } + } + else /* is XOR */ + { + old_to_new[n] = dest.create_xor( fanin[0], fanin[1] ); + } + } ); + + xag.foreach_po( [&]( auto const& f ) { + dest.create_po( old_to_new[f] ^ xag.is_complemented( f ) ); + } ); + + return dest; +} + +/*! \brief Optimizes XOR gates by linear network resynthesis + * + * See `exact_linear_resynthesis_optimization` for an example implementation + * of this function. + */ +inline xag_network linear_resynthesis_optimization( xag_network const& xag, std::function linear_resyn, std::function const& )> const& on_ignore_inputs = {} ) +{ + const auto num_ands = *multiplicative_complexity( xag ); + if ( num_ands == 0u ) + { + return linear_resyn( xag ); + } + + const auto linear = extract_linear_circuit( xag ).first; + + /* ignore inputs (if linear resynthesis is not cancellation-free) */ + on_ignore_inputs( {} ); + for ( auto i = 0u; i < num_ands; ++i ) + { + std::vector ignore( num_ands - i ); + std::iota( ignore.begin(), ignore.end(), xag.num_pis() + i ); + on_ignore_inputs( ignore ); + on_ignore_inputs( ignore ); + } + + const auto linear_optimized = linear_resyn( linear ); + + assert( linear.num_pis() == linear_optimized.num_pis() ); + assert( linear.num_pos() == linear_optimized.num_pos() ); + assert( linear.num_pis() == xag.num_pis() + num_ands ); + assert( linear.num_pos() == 1 + 2 * num_ands ); + + return merge_linear_circuit( linear_optimized, num_ands ); +} + +/*! \brief Optimizes XOR gates by exact linear network resynthesis + */ +template +inline xag_network exact_linear_resynthesis_optimization( xag_network const& xag, uint32_t conflict_limit = 0u ) +{ + exact_linear_synthesis_params ps; + ps.conflict_limit = conflict_limit; + + const auto linear_resyn = [&]( xag_network const& linear ) { + if ( const auto optimized = exact_linear_resynthesis( linear, ps ); optimized ) + { + return *optimized; + } + else + { + return linear; + } + }; + + const auto on_ignore_inputs = [&]( std::vector const& ignore ) { + ps.ignore_inputs.push_back( ignore ); + }; + + return linear_resynthesis_optimization( xag, linear_resyn, on_ignore_inputs ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xag_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xag_resub.hpp new file mode 100644 index 00000000000..6d3cb2a421b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xag_resub.hpp @@ -0,0 +1,216 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2024 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_resub.hpp + \brief XAG-specific resubstitution rules + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../networks/xag.hpp" +#include "../utils/index_list/index_list.hpp" +#include "../utils/truth_table_utils.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/xag_resyn.hpp" + +#include + +namespace mockturtle +{ + +struct xag_resyn_resub_stats +{ + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_compute_function{ 0 }; + + /*! \brief Number of found solutions. */ + uint32_t num_success{ 0 }; + + /*! \brief Number of times that no solution can be found. */ + uint32_t num_fail{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] #solution = {:6d}\n", num_success ); + fmt::print( "[i] #invoke = {:6d}\n", num_success + num_fail ); + fmt::print( "[i] engine time: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + } +}; /* xag_resyn_resub_stats */ + +/*! \brief Interfacing resubstitution functor with XAG resynthesis engines for `window_based_resub_engine`. + */ +template>> +struct xag_resyn_functor +{ +public: + using node = xag_network::node; + using signal = xag_network::signal; + using stats = xag_resyn_resub_stats; + using TT = typename ResynEngine::truth_table_t; + + static_assert( std::is_same_v, "truth table type of the simulator does not match" ); + +public: + explicit xag_resyn_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), tts( ntk ), divs( divs ), st( st ) + { + assert( divs.size() == num_divs ); + (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TTcare care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)required; + TT target = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + TT care_transformed = target.construct(); + care_transformed = care; + + typename ResynEngine::stats st_eng; + ResynEngine engine( st_eng ); + for ( auto const& d : divs ) + { + div_signals.emplace_back( sim.get_phase( d ) ? !ntk.make_signal( d ) : ntk.make_signal( d ) ); + tts[d] = sim.get_tt( ntk.make_signal( d ) ); + } + + auto const res = call_with_stopwatch( st.time_compute_function, [&]() { + return engine( target, care_transformed, std::begin( divs ), std::end( divs ), tts, std::min( potential_gain - 1, max_inserts ) ); + } ); + if ( res ) + { + ++st.num_success; + signal ret; + real_gain = potential_gain - ( *res ).num_gates(); + insert( ntk, div_signals.begin(), div_signals.end(), *res, [&]( signal const& s ) { ret = s; } ); + return ret; + } + else + { + ++st.num_fail; + return std::nullopt; + } + } + +private: + Ntk& ntk; + Simulator const& sim; + unordered_node_map tts; + std::vector const& divs; + std::vector div_signals; + stats& st; +}; /* xag_resyn_functor */ + +/*! \brief XAG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from xag_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void xag_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not xag_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = xag_resyn_functor, truthtable_dc_t>; + + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xag_resub_withDC.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xag_resub_withDC.hpp new file mode 100644 index 00000000000..e3c851f9ceb --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xag_resub_withDC.hpp @@ -0,0 +1,989 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_resub_withDC.hpp + \brief Resubstitution with free xor (works for XAGs, XOR gates are considered for free) + + \author Eleonora Testa + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/xag.hpp" +#include "dont_cares.hpp" +#include "resubstitution.hpp" +#include + +namespace mockturtle +{ + +struct xag_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Accumulated runtime for three-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1_and{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub2_and{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for 12-resub. */ + stopwatch<>::duration time_resub12{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions for AND */ + uint64_t num_div1_and_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions for AND */ + uint64_t num_div2_and_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions using triples of unate divisors */ + uint64_t num_div12_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: xag_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} ({:>5.2f} secs)\n", + num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] 2-resub {:6d} ({:>5.2f} secs)\n", + num_div2_accepts, to_seconds( time_resub2 ) ); + std::cout << fmt::format( "[i] 1-resub AND {:6d} ({:>5.2f} secs)\n", + num_div1_and_accepts, to_seconds( time_resub1_and ) ); + std::cout << fmt::format( "[i] 12-resub {:6d} ({:>5.2f} secs)\n", + num_div12_accepts, to_seconds( time_resub12 ) ); + std::cout << fmt::format( "[i] 2-resub AND {:6d} ({:>5.2f} secs)\n", + num_div2_and_accepts, to_seconds( time_resub2_and ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_div1_accepts + num_div2_accepts + num_div1_and_accepts + num_div12_accepts + num_div2_and_accepts ) ); + } +}; /* xag_resub_stats */ + +namespace detail +{ + +template +class node_mffc_inside_xag +{ +public: + using node = typename Ntk::node; + +public: + explicit node_mffc_inside_xag( Ntk const& ntk ) + : ntk( ntk ) + { + } + + std::pair run( node const& n, std::vector const& leaves, std::vector& inside ) + { + /* increment the fanout counters for the leaves */ + for ( const auto& l : leaves ) + ntk.incr_fanout_size( l ); + + /* dereference the node */ + auto count1 = node_deref_rec( n ); + + /* collect the nodes inside the MFFC */ + node_mffc_cone( n, inside ); + + /* reference it back */ + auto count2 = node_ref_rec( n ); + (void)count2; + + assert( count1.first == count2.first ); + assert( count1.second == count2.second ); + + for ( const auto& l : leaves ) + ntk.decr_fanout_size( l ); + + return count1; + } + +private: + /* ! \brief Dereference the node's MFFC */ + std::pair node_deref_rec( node const& n ) + { + + if ( ntk.is_pi( n ) ) + return { 0, 0 }; + + int32_t counter_and = 0; + int32_t counter_xor = 0; + + if ( ntk.is_and( n ) ) + { + counter_and = 1; + } + else if ( ntk.is_xor( n ) ) + { + counter_xor = 1; + } + + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + ntk.decr_fanout_size( p ); + if ( ntk.fanout_size( p ) == 0 ) + { + auto counter = node_deref_rec( p ); + counter_and += counter.first; + counter_xor += counter.second; + } + } ); + + return { counter_and, counter_xor }; + } + + /* ! \brief Reference the node's MFFC */ + std::pair node_ref_rec( node const& n ) + { + if ( ntk.is_pi( n ) ) + return { 0, 0 }; + + int32_t counter_and = 0; + int32_t counter_xor = 0; + + if ( ntk.is_and( n ) ) + { + counter_and = 1; + } + else if ( ntk.is_xor( n ) ) + { + counter_xor = 1; + } + + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + auto v = ntk.fanout_size( p ); + ntk.incr_fanout_size( p ); + if ( v == 0 ) + { + auto counter = node_ref_rec( p ); + counter_and += counter.first; + counter_xor += counter.second; + } + } ); + + return { counter_and, counter_xor }; + } + + void node_mffc_cone_rec( node const& n, std::vector& cone, bool top_most ) + { + /* skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + ntk.set_visited( n, ntk.trav_id() ); + + if ( !top_most && ( ntk.is_pi( n ) || ntk.fanout_size( n ) > 0 ) ) + return; + + /* recurse on children */ + ntk.foreach_fanin( n, [&]( const auto& f ) { + node_mffc_cone_rec( ntk.get_node( f ), cone, false ); + } ); + + /* collect the internal nodes */ + cone.emplace_back( n ); + } + + void node_mffc_cone( node const& n, std::vector& cone ) + { + cone.clear(); + ntk.incr_trav_id(); + node_mffc_cone_rec( n, cone, true ); + } + +private: + Ntk const& ntk; +}; + +} /* namespace detail */ + +template +struct xag_resub_functor +{ +public: + using node = xag_network::node; + using signal = xag_network::signal; + using stats = xag_resub_stats; + + struct unate_divisors + { + using signal = typename xag_network::signal; + + std::vector positive_divisors; + std::vector negative_divisors; + std::vector next_candidates; + + void clear() + { + positive_divisors.clear(); + negative_divisors.clear(); + next_candidates.clear(); + } + }; + + struct binate_divisors + { + using signal = typename xag_network::signal; + + std::vector positive_divisors0; + std::vector positive_divisors1; + std::vector negative_divisors0; + std::vector negative_divisors1; + + void clear() + { + positive_divisors0.clear(); + positive_divisors1.clear(); + negative_divisors0.clear(); + negative_divisors1.clear(); + } + }; + +public: + explicit xag_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, std::pair num_mffc, uint32_t& last_gain ) + { + + uint32_t num_and_mffc = num_mffc.first; + uint32_t num_xor_mffc = num_mffc.second; + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, care, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, care, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + if ( num_and_mffc == 0 ) + { + return std::nullopt; + if ( max_inserts == 0 || num_xor_mffc == 1 ) + return std::nullopt; + + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, care, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = 0; + return g; /* accepted resub */ + } + + if ( max_inserts == 1 || num_xor_mffc == 2 ) + return std::nullopt; + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, care, required ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = 0; + return g; /* accepted resub */ + } + } + else + { + + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, care, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, care, required ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + if ( num_and_mffc < 2 ) /* it is worth trying also AND resub here */ + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, required ); + } ); + + g = call_with_stopwatch( st.time_resub1_and, [&]() { return resub_div1_and( root, care, required ); } ); + if ( g ) + { + ++st.num_div1_and_accepts; + last_gain = num_and_mffc - 1; + return g; /* accepted resub */ + } + if ( num_and_mffc < 3 ) /* it is worth trying also AND-12 resub here */ + return std::nullopt; + + /* consider triples */ + g = call_with_stopwatch( st.time_resub12, [&]() { return resub_div12( root, care, required ); } ); + if ( g ) + { + ++st.num_div12_accepts; + last_gain = num_and_mffc - 2; + return g; /* accepted resub */ + } + + /* collect level two divisors */ + call_with_stopwatch( st.time_collect_binate_divisors, [&]() { + collect_binate_divisors( root, required ); + } ); + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2_and, [&]() { return resub_div2_and( root, care, required ); } ); + if ( g ) + { + ++st.num_div2_and_accepts; + last_gain = num_and_mffc - 2; + return g; /* accepted resub */ + } + } + return std::nullopt; + } + + std::optional resub_const( node const& root, TT care, uint32_t required ) const + { + (void)required; + auto tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + if ( binary_and( tt, care ) == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, TT care, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( binary_and( tt, care ) != binary_and( sim.get_tt( ntk.make_signal( d ) ), care ) ) + continue; + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + return std::nullopt; + } + + std::optional resub_div1( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + /* check for divisors */ + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const& s0 = divs.at( i ); + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const& s1 = divs.at( j ); + auto const& tt_s0 = sim.get_tt( ntk.make_signal( s0 ) ); + auto const& tt_s1 = sim.get_tt( ntk.make_signal( s1 ) ); + + if ( binary_and( ( tt_s0 ^ tt_s1 ), care ) == binary_and( tt, care ) ) + { + auto const l = sim.get_phase( s0 ) ? !ntk.make_signal( s0 ) : ntk.make_signal( s0 ); + auto const r = sim.get_phase( s1 ) ? !ntk.make_signal( s1 ) : ntk.make_signal( s1 ); + return sim.get_phase( root ) ? !ntk.create_xor( l, r ) : ntk.create_xor( l, r ); + } + else if ( binary_and( ( tt_s0 ^ tt_s1 ), care ) == binary_and( kitty::unary_not( tt ), care ) ) + { + auto const l = sim.get_phase( s0 ) ? !ntk.make_signal( s0 ) : ntk.make_signal( s0 ); + auto const r = sim.get_phase( s1 ) ? !ntk.make_signal( s1 ) : ntk.make_signal( s1 ); + return sim.get_phase( root ) ? ntk.create_xor( l, r ) : !ntk.create_xor( l, r ); + } + } + } + return std::nullopt; + } + + std::optional resub_div2( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const s0 = divs.at( i ); + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const s1 = divs.at( j ); + + for ( auto k = j + 1; k < num_divs; ++k ) + { + auto const s2 = divs.at( k ); + auto const& tt_s0 = sim.get_tt( ntk.make_signal( s0 ) ); + auto const& tt_s1 = sim.get_tt( ntk.make_signal( s1 ) ); + auto const& tt_s2 = sim.get_tt( ntk.make_signal( s2 ) ); + + if ( binary_and( ( tt_s0 ^ tt_s1 ^ tt_s2 ), care ) == binary_and( tt, care ) ) + { + auto const max_level = std::max( { ntk.level( s0 ), + ntk.level( s1 ), + ntk.level( s2 ) } ); + assert( max_level <= required - 1 ); + + signal max = ntk.make_signal( s0 ); + signal min0 = ntk.make_signal( s1 ); + signal min1 = ntk.make_signal( s2 ); + if ( ntk.level( s1 ) == max_level ) + { + max = ntk.make_signal( s1 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s2 ); + } + else if ( ntk.level( s2 ) == max_level ) + { + max = ntk.make_signal( s2 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s1 ); + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? !ntk.create_xor( a, ntk.create_xor( b, c ) ) : ntk.create_xor( a, ntk.create_xor( b, c ) ); + } + else if ( binary_and( ( tt_s0 ^ tt_s1 ^ tt_s2 ), care ) == binary_and( kitty::unary_not( tt ), care ) ) + { + auto const max_level = std::max( { ntk.level( s0 ), + ntk.level( s1 ), + ntk.level( s2 ) } ); + assert( max_level <= required - 1 ); + + signal max = ntk.make_signal( s0 ); + signal min0 = ntk.make_signal( s1 ); + signal min1 = ntk.make_signal( s2 ); + if ( ntk.level( s1 ) == max_level ) + { + max = ntk.make_signal( s1 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s2 ); + } + else if ( ntk.level( s2 ) == max_level ) + { + max = ntk.make_signal( s2 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s1 ); + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? ntk.create_xor( a, ntk.create_xor( b, c ) ) : !ntk.create_xor( a, ntk.create_xor( b, c ) ); + } + } + } + } + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t required ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( ntk.level( d ) > required - 1 ) + continue; + + auto const& tt_d = sim.get_tt( ntk.make_signal( d ) ); + + /* check positive containment */ + if ( kitty::implies( tt_d, tt ) ) + { + udivs.positive_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + /* check negative containment */ + if ( kitty::implies( tt, tt_d ) ) + { + udivs.negative_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + udivs.next_candidates.emplace_back( ntk.make_signal( d ) ); + } + } + + std::optional resub_div1_and( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* check for positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const& s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const& s1 = udivs.positive_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( binary_and( ( tt_s0 | tt_s1 ), care ) == binary_and( tt, care ) ) + { + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + return sim.get_phase( root ) ? !ntk.create_or( l, r ) : ntk.create_or( l, r ); + } + } + } + /* check for negative unate divisors */ + for ( auto i = 0u; i < udivs.negative_divisors.size(); ++i ) + { + auto const& s0 = udivs.negative_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.negative_divisors.size(); ++j ) + { + auto const& s1 = udivs.negative_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( binary_and( ( tt_s0 & tt_s1 ), care ) == binary_and( tt, care ) ) + { + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + return sim.get_phase( root ) ? !ntk.create_and( l, r ) : ntk.create_and( l, r ); + } + } + } + + return std::nullopt; + } + + std::optional resub_div12( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* check positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( binary_and( ( tt_s0 | tt_s1 | tt_s2 ), care ) == binary_and( tt, care ) ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= required - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_or( b, c ) ) : ntk.create_or( a, ntk.create_or( b, c ) ); + } + } + } + } + + /* check negative unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( binary_and( ( tt_s0 & tt_s1 & tt_s2 ), care ) == binary_and( tt, care ) ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= required - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_and( b, c ) ) : ntk.create_and( a, ntk.create_and( b, c ) ); + } + } + } + } + + return std::nullopt; + } + + void collect_binate_divisors( node const& root, uint32_t required ) + { + bdivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < udivs.next_candidates.size(); ++i ) + { + auto const& s0 = udivs.next_candidates.at( i ); + if ( ntk.level( ntk.get_node( s0 ) ) > required - 2 ) + continue; + + for ( auto j = i + 1; j < udivs.next_candidates.size(); ++j ) + { + auto const& s1 = udivs.next_candidates.at( j ); + if ( ntk.level( ntk.get_node( s1 ) ) > required - 2 ) + continue; + + if ( bdivs.positive_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( ~tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( ~tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + } + + if ( bdivs.negative_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt, tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + } + } + } + } + + std::optional resub_div2_and( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* check positive unate divisors */ + for ( const auto& s0 : udivs.positive_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.positive_divisors0.size(); ++j ) + { + auto const s1 = bdivs.positive_divisors0.at( j ); + auto const s2 = bdivs.positive_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( binary_and( ( tt_s0 | ( tt_s1 & tt_s2 ) ), care ) == binary_and( tt, care ) ) + { + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_and( b, c ) ) : ntk.create_or( a, ntk.create_and( b, c ) ); + } + } + } + + /* check negative unate divisors */ + for ( const auto& s0 : udivs.negative_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.negative_divisors0.size(); ++j ) + { + auto const s1 = bdivs.negative_divisors0.at( j ); + auto const s2 = bdivs.negative_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( binary_and( ( tt_s0 | ( tt_s1 & tt_s2 ) ), care ) == binary_and( tt, care ) ) + { + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_or( b, c ) ) : ntk.create_and( a, ntk.create_or( b, c ) ); + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; + binate_divisors bdivs; +}; /* xag_resub_functor */ + +template +void resubstitution_minmc_withDC( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + using truthtable_t = kitty::dynamic_truth_table; + using mffc_result_t = std::pair; + using resub_impl_t = detail::resubstitution_impl, truthtable_t>, mffc_result_t>, typename detail::default_divisor_collector, mffc_result_t>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xmg_algebraic_rewriting.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xmg_algebraic_rewriting.hpp new file mode 100644 index 00000000000..7b8640ed022 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xmg_algebraic_rewriting.hpp @@ -0,0 +1,518 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xmg_algebraic_rewriting.hpp + \brief xmg algebraric rewriting + + \author Heinz Riener + \author Mathias Soeken + \author Zhufei Chu +*/ + +#pragma once + +#include +#include + +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for xmg_algebraic_depth_rewriting. + * + * The data structure `xmg_algebraic_depth_rewriting_params` holds configurable + * parameters with default arguments for `xmg_algebraic_depth_rewriting`. + */ +struct xmg_algebraic_depth_rewriting_params +{ + /*! \brief Rewriting strategy. */ + enum strategy_t + { + /*! \brief DFS rewriting strategy. + * + * Applies depth rewriting once to all output cones whose drivers have + * maximum levels + */ + dfs, + /*! \brief Aggressive rewriting strategy. + * + * Applies depth reduction multiple times until the number of nodes, which + * cannot be rewritten, matches the number of nodes, in the current + * network; or the new network size is larger than the initial size w.r.t. + * to an `overhead`. + */ + aggressive, + /*! \brief Selective rewriting strategy. + * + * Like `aggressive`, but only applies rewriting to nodes on critical paths + * and without `overhead`. + */ + selective + } strategy = dfs; + + /*! \brief Overhead factor in aggressive rewriting strategy. + * + * When comparing to the initial size in aggressive depth rewriting, also the + * number of dangling nodes are taken into account. + */ + float overhead{ 2.0f }; + + /*! \brief Allow area increase while optimizing depth. */ + bool allow_area_increase{ true }; +}; + +namespace detail +{ + +template +class xmg_algebraic_depth_rewriting_impl +{ +public: + xmg_algebraic_depth_rewriting_impl( Ntk& ntk, xmg_algebraic_depth_rewriting_params const& ps ) + : ntk( ntk ), ps( ps ) + { + } + + void run() + { + switch ( ps.strategy ) + { + case xmg_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case xmg_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case xmg_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [this]( auto n ) { + reduce_depth( n ); + reduce_depth_xor_associativity( n ); + reduce_depth_xor_complementary_associativity( n ); + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + + if ( reduce_depth( n ) ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + if ( !reduce_depth( n ) ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + bool reduce_depth( node const& n ) + { + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_maj( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + ocs2[0] = !ocs2[0]; + ocs2[1] = !ocs2[1]; + ocs2[2] = !ocs2[2]; + } + + if ( auto cand = associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + auto opt = ntk.create_maj( z, assoc ? u : x, ntk.create_maj( x, y, u ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* distributivity */ + if ( ps.allow_area_increase ) + { + auto opt = ntk.create_maj( ocs2[2], + ntk.create_maj( ocs[0], ocs[1], ocs2[0] ), + ntk.create_maj( ocs[0], ocs[1], ocs2[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + } + return true; + } + + using candidate_t = std::tuple, signal, signal, signal, bool>; + std::optional associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + + return std::nullopt; + } + + /* XOR associativity */ + bool reduce_depth_xor_associativity( node const& n ) + { + if ( !ntk.is_xor3( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor3( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + if ( ntk.is_complemented( ocs2[0] ) ) + { + ocs2[0] = !ocs2[0]; + } + else if ( ntk.is_complemented( ocs2[1] ) ) + { + ocs2[1] = !ocs2[1]; + } + else if ( ntk.is_complemented( ocs2[2] ) ) + { + ocs2[2] = !ocs2[2]; + } + else + { + ocs2[0] = !ocs2[0]; + } + } + + auto opt = ntk.create_xor3( ocs[0], ocs2[2], + ntk.create_xor3( ocs2[0], ocs2[1], ocs[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* XOR complementary associativity = */ + bool reduce_depth_xor_complementary_associativity( node const& n ) + { + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor3( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + /* depth of last child must be higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) < ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* multiple child fanout is allowable */ + // if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + // return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + if ( ntk.is_complemented( ocs2[0] ) ) + { + ocs2[0] = !ocs2[0]; + } + else if ( ntk.is_complemented( ocs2[1] ) ) + { + ocs2[1] = !ocs2[1]; + } + else if ( ntk.is_complemented( ocs2[2] ) ) + { + ocs2[2] = !ocs2[2]; + } + else + { + ocs2[0] = !ocs2[0]; + } + } + + if ( auto cand = xor_compl_associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + auto opt = ntk.create_maj( x, u, ntk.create_xor3( assoc ? !x : x, y, z ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + return true; + } + + std::optional xor_compl_associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( v.index == z.index ) + { + return candidate_t{ w, x, y, v, v.complement == z.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + if ( w.index == z.index ) + { + return candidate_t{ v, x, y, w, w.complement == z.complement }; + } + + return std::nullopt; + } + + std::array, 3> ordered_children( node const& n ) const + { + std::array, 3> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } ); + std::stable_sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) { + return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) ); + } ); + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + xmg_algebraic_depth_rewriting_params const& ps; +}; + +} // namespace detail + +/*! \brief XMG algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with majority gates for depth + * optimization using the associativity and distributivity rule in + * majority-of-3 logic. It can be applied to networks other than XMGs, but + * only considers pairs of nodes which both implement the majority-of-3 + * function and the XOR function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_maj` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_maj` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired by an + implementation from Luca Amarù. + \endverbatim + */ +template +void xmg_algebraic_depth_rewriting( Ntk& ntk, xmg_algebraic_depth_rewriting_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_maj method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + detail::xmg_algebraic_depth_rewriting_impl p( ntk, ps ); + p.run(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xmg_optimization.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xmg_optimization.hpp new file mode 100644 index 00000000000..b19c73d53e5 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xmg_optimization.hpp @@ -0,0 +1,101 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xmg_optimization.hpp + \brief Rewriting MAJ to XNORs. + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../networks/xmg.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "dont_cares.hpp" + +namespace mockturtle +{ + +/*! \brief Optimizes some MAJ gates using satisfiability don't cares + * + * The function is based on `xag_dont_cares_optimization` in `xag_optimization.hpp`. + * + * If a MAJ gate is satisfiability don't care for assignments 000 and 111, it can be + * replaced by an XNOR gate. + */ +inline xmg_network xmg_dont_cares_optimization( xmg_network const& xmg ) +{ + node_map old_to_new( xmg ); + + xmg_network dest; + old_to_new[xmg.get_constant( false )] = dest.get_constant( false ); + + xmg.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = dest.create_pi(); + } ); + + satisfiability_dont_cares_checker checker( xmg ); + + topo_view{ xmg }.foreach_node( [&]( auto const& n ) { + if ( xmg.is_constant( n ) || xmg.is_pi( n ) ) + return; + + std::array fanin; + xmg.foreach_fanin( n, [&]( auto const& f, auto i ) { + fanin[i] = old_to_new[f] ^ xmg.is_complemented( f ); + } ); + + if ( xmg.is_maj( n ) ) + { + if ( checker.is_dont_care( n, { false, false, false } ) && checker.is_dont_care( n, { true, true, true } ) ) + { + old_to_new[n] = dest.create_xor3( !fanin[0], fanin[1], fanin[2] ); + } + else + { + old_to_new[n] = dest.create_maj( fanin[0], fanin[1], fanin[2] ); + } + } + else /* is XOR */ + { + old_to_new[n] = dest.create_xor3( fanin[0], fanin[1], fanin[2] ); + } + } ); + + xmg.foreach_po( [&]( auto const& f ) { + dest.create_po( old_to_new[f] ^ xmg.is_complemented( f ) ); + } ); + + return dest; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/xmg_resub.hpp b/third-party/mockturtle/include/mockturtle/algorithms/xmg_resub.hpp new file mode 100644 index 00000000000..44690e0325f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/xmg_resub.hpp @@ -0,0 +1,346 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xmg_resub.hpp + \brief Resubstitution + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once +#include "../networks/xmg.hpp" +#include "dont_cares.hpp" +#include "resubstitution.hpp" +#include + +namespace mockturtle +{ + +namespace detail +{ + +/*! \brief Ternary XOR of three truth tables */ +template +inline TT ternary_xor( const TT& first, const TT& second, const TT& third ) +{ + return kitty::ternary_operation( first, second, third, []( auto a, auto b, auto c ) { return ( ( a ^ b ) ^ c ); } ); +} + +} // namespace detail + +struct xmg_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint32_t num_div1_accepts{ 0 }; + + uint32_t num_div1_xor3_accepts{ 0 }; + uint32_t num_div1_xnor3_accepts{ 0 }; + uint32_t num_div1_maj3_accepts{ 0 }; + uint32_t num_div1_not_maj3_accepts{ 0 }; + + uint32_t num_filtered0{ 0 }; + uint32_t num_filtered1{ 0 }; + + void report() const + { + fmt::print( "[i] kernel: xmg_resub_functor\n" ); + fmt::print( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + fmt::print( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + fmt::print( "[i] 1-resub {:6d} = {:6d} XOR3 + {:6d} XNOR3 + {:6d} MAJ3 + {:6d} NOT-MAJ3 ({:>5.2f} secs)\n", + num_div1_accepts, num_div1_xor3_accepts, num_div1_xnor3_accepts, num_div1_maj3_accepts, num_div1_not_maj3_accepts, + to_seconds( time_resub1 ) ); + fmt::print( "[i] filtering candidates: {:6d} candidates in first loop + {:6d} candidates in second loop\n", num_filtered0, num_filtered1 ); + } +}; /* xmg_resub_stats */ + +template +struct xmg_resub_functor +{ +public: + using node = xmg_network::node; + using signal = xmg_network::signal; + using stats = xmg_resub_stats; + +public: + explicit xmg_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT& care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, care, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, care, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* consider adding one gate */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, care, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root, TT& care, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + + if ( binary_and( tt, care ) == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, TT& care, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( binary_and( tt, care ) != binary_and( sim.get_tt( ntk.make_signal( d ) ), care ) ) + continue; /* next */ + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + struct divisor + { + explicit divisor( uint32_t node, int32_t entropy ) + : node( node ), entropy( entropy ) + { + } + + uint32_t node; + int32_t entropy; + }; + + std::optional resub_div1( node const& root, TT& care, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + const auto root_rdb = static_cast( absolute_distinguishing_power( tt ) ); + + std::vector sorted_divs; + for ( auto it = std::begin( divs ), ie = std::begin( divs ) + num_divs; it != ie; ++it ) + { + auto const s = ntk.make_signal( *it ); + auto const& tt_s = sim.get_tt( s ); + sorted_divs.emplace_back( static_cast( *it ), static_cast( relative_distinguishing_power( tt_s, tt ) ) ); + } + std::stable_sort( std::rbegin( sorted_divs ), std::rend( sorted_divs ), + [&]( auto const& u, auto const& v ) { + if ( u.entropy == v.entropy ) + return u.node < v.node; + return u.entropy < v.entropy; + } ); + + for ( auto i = 0u; i < sorted_divs.size(); ++i ) + { + auto const s0 = ntk.make_signal( sorted_divs.at( i ).node ); + auto const& tt0 = sim.get_tt( s0 ); + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + + int64_t const db_s0 = sorted_divs.at( i ).entropy; + int64_t const bound0 = root_rdb - db_s0; + for ( auto j = i + 1; j < sorted_divs.size(); ++j ) + { + auto const s1 = ntk.make_signal( sorted_divs.at( j ).node ); + auto const& tt1 = sim.get_tt( s1 ); + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + + int64_t const db_s1 = sorted_divs.at( j ).entropy; + if ( ( 2u * db_s1 ) < bound0 ) + { + st.num_filtered0 += static_cast( ( sorted_divs.size() - j - 1 ) * ( sorted_divs.size() - j ) ); + break; + } + + int64_t const bound1 = bound0 - db_s1; + for ( auto k = j + 1; k < sorted_divs.size(); ++k ) + { + auto const s2 = ntk.make_signal( sorted_divs.at( k ).node ); + auto const& tt2 = sim.get_tt( s2 ); + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + int64_t const db_s2 = sorted_divs.at( k ).entropy; + if ( db_s2 < bound1 ) + { + st.num_filtered1 += static_cast( sorted_divs.size() - k - 1 ); + break; + } + + if ( binary_and( tt, care ) == binary_and( detail::ternary_xor( tt0, tt1, tt2 ), care ) ) + { + /* XOR3 */ + ++st.num_div1_xor3_accepts; + return sim.get_phase( root ) ? !ntk.create_xor3( a, b, c ) : ntk.create_xor3( a, b, c ); + } + else if ( binary_and( tt, care ) == binary_and( detail::ternary_xor( ~tt0, tt1, tt2 ), care ) ) + { + /* XNOR3 */ + ++st.num_div1_xnor3_accepts; + return sim.get_phase( root ) ? !ntk.create_xor3( !a, b, c ) : ntk.create_xor3( !a, b, c ); + } + else if ( binary_and( tt, care ) == binary_and( kitty::ternary_majority( tt0, tt1, tt2 ), care ) ) + { + /* MAJ3 */ + ++st.num_div1_maj3_accepts; + return sim.get_phase( root ) ? !ntk.create_maj( a, b, c ) : ntk.create_maj( a, b, c ); + } + else if ( binary_and( tt, care ) == binary_and( kitty::ternary_majority( ~tt0, tt1, tt2 ), care ) ) + { + /* NOT-MAJ3 */ + ++st.num_div1_not_maj3_accepts; + return sim.get_phase( root ) ? !ntk.create_maj( !a, b, c ) : ntk.create_maj( !a, b, c ); + } + } + } + } + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; +}; /* xmg_resub_functor */ + +template +void xmg_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using resub_impl_t = detail::resubstitution_impl, truthtable_dc_t>>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/generators/arithmetic.hpp b/third-party/mockturtle/include/mockturtle/generators/arithmetic.hpp new file mode 100644 index 00000000000..f99df24b92e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/arithmetic.hpp @@ -0,0 +1,411 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file arithmetic.hpp + \brief Generate arithmetic logic networks + + \author Heinz Riener + \author Jovan Blanuša + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include +#include + +#include "../networks/aig.hpp" +#include "../traits.hpp" +#include "control.hpp" + +namespace mockturtle +{ + +/*! \brief Inserts a full adder into a network. + * + * Inserts a full adder for three inputs (two 1-bit operands and one carry) + * into the network and returns a pair of sum and carry bit. + * + * By default creates a seven 2-input gate network composed of AND, NOR, and OR + * gates. If network has `create_node` function, creates two 3-input gate + * network. If the network has ternary `create_maj` and `create_xor3` + * functions, it will use them (except for AIGs). + * + * \param ntk Network + * \param a First input operand + * \param b Second input operand + * \param c Carry + * \return Pair of sum (`first`) and carry (`second`) + */ +template +inline std::pair, signal> full_adder( Ntk& ntk, const signal& a, const signal& b, const signal& c ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + /* specialization for LUT-ish networks */ + if constexpr ( has_create_node_v ) + { + kitty::dynamic_truth_table tt_maj( 3u ), tt_xor( 3u ); + kitty::create_from_hex_string( tt_maj, "e8" ); + kitty::create_from_hex_string( tt_xor, "96" ); + + const auto sum = ntk.create_node( { a, b, c }, tt_xor ); + const auto carry = ntk.create_node( { a, b, c }, tt_maj ); + + return { sum, carry }; + } + /* use MAJ and XOR3 if available by network, unless network is AIG */ + else if constexpr ( !std::is_same_v && has_create_maj_v && has_create_xor3_v ) + { + const auto carry = ntk.create_maj( a, b, c ); + const auto sum = ntk.create_xor3( a, b, c ); + return { sum, carry }; + } + else + { + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_nor_v, "Ntk does not implement the create_nor method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + + const auto w1 = ntk.create_and( a, b ); + const auto w2 = ntk.create_nor( a, b ); + const auto w3 = ntk.create_nor( w1, w2 ); + const auto w4 = ntk.create_and( c, w3 ); + const auto w5 = ntk.create_nor( c, w3 ); + const auto sum = ntk.create_nor( w4, w5 ); + const auto carry = ntk.create_or( w1, w4 ); + + return { sum, carry }; + } +} + +/*! \brief Inserts a half adder into a network. + * + * Inserts a half adder for two inputs (two 1-bit operands) + * into the network and returns a pair of sum and carry bit. + * + * It creates three 2-input gate network composed of AND and NOR gates. + * + * \param ntk Network + * \param a First input operand + * \param b Second input operand + * \return Pair of sum (`first`) and carry (`second`) + */ +template +inline std::pair, signal> half_adder( Ntk& ntk, const signal& a, const signal& b ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + /* specialization for LUT-ish networks */ + if constexpr ( has_create_node_v ) + { + kitty::dynamic_truth_table tt_and( 2u ), tt_xor( 2u ); + kitty::create_from_hex_string( tt_and, "8" ); + kitty::create_from_hex_string( tt_xor, "6" ); + + const auto sum = ntk.create_node( { a, b }, tt_xor ); + const auto carry = ntk.create_node( { a, b }, tt_and ); + + return { sum, carry }; + } + else + { + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_nor_v, "Ntk does not implement the create_nor method" ); + + const auto carry = ntk.create_and( a, b ); + const auto w2 = ntk.create_nor( a, b ); + const auto sum = ntk.create_nor( carry, w2 ); + + return { sum, carry }; + } +} + +/*! \brief Creates carry ripple adder structure. + * + * Creates a carry ripple structure composed of full adders. The vectors `a` + * and `b` must have the same size. The resulting sum bits are eventually + * stored in `a` and the carry bit will be overridden to store the output carry + * bit. + * + * \param a First input operand, will also have the output after the call + * \param b Second input operand + * \param carry Carry bit, will also have the output carry after the call + */ +template +inline void carry_ripple_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, signal& carry ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + assert( a.size() == b.size() ); + + auto pa = a.begin(); + for ( auto pb = b.begin(); pa != a.end(); ++pa, ++pb ) + { + std::tie( *pa, carry ) = full_adder( ntk, *pa, *pb, carry ); + } +} + +/*! \brief Creates carry ripple subtractor structure. + * + * Creates a carry ripple structure composed of full adders. The vectors `a` + * and `b` must have the same size. The resulting sum bits are eventually + * stored in `a` and the carry bit will be overridden to store the output carry + * bit. The inputs in `b` are inverted to realize subtraction with full + * adders. The carry bit must be passed in inverted state to the subtractor. + * + * \param a First input operand, will also have the output after the call + * \param b Second input operand + * \param carry Carry bit, will also have the output carry after the call + */ +template +inline void carry_ripple_subtractor_inplace( Ntk& ntk, std::vector>& a, const std::vector>& b, signal& carry ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + assert( a.size() == b.size() ); + + auto pa = a.begin(); + for ( auto pb = b.begin(); pa != a.end(); ++pa, ++pb ) + { + std::tie( *pa, carry ) = full_adder( ntk, *pa, ntk.create_not( *pb ), carry ); + } +} + +/*! \brief Creates a classical multiplier using full adders. + * + * The vectors `a` and `b` must not have the same size. The function creates + * the multiplier in `ntk` and returns output signals, whose size is the summed + * sizes of `a` and `b`. + * + * \param ntk Network + * \param a First input operand + * \param b Second input operand + */ +template +inline std::vector> carry_ripple_multiplier( Ntk& ntk, std::vector> const& a, std::vector> const& b ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + auto res = constant_word( ntk, 0, static_cast( a.size() + b.size() ) ); + auto tmp = constant_word( ntk, 0, static_cast( a.size() * 2 ) ); + + for ( auto j = 0u; j < b.size(); ++j ) + { + for ( auto i = 0u; i < a.size(); ++i ) + { + std::tie( i ? tmp[a.size() + i - 1] : res[j], tmp[i] ) = full_adder( ntk, ntk.create_and( a[i], b[j] ), tmp[a.size() + i], tmp[i] ); + } + } + + auto carry = tmp.back() = ntk.get_constant( false ); + for ( auto i = 0u; i < a.size(); ++i ) + { + std::tie( res[b.size() + i], carry ) = full_adder( ntk, tmp[i], tmp[a.size() + i], carry ); + } + + return res; +} + +// CLA implementation based on Alan Mishchenko's implementation in +// https://github.com/berkeley-abc/abc/blob/master/src/base/wlc/wlcBlast.c +namespace detail +{ + +template +inline std::pair, signal> carry_lookahead_adder_inplace_rec( Ntk& ntk, + typename std::vector>::iterator genBegin, + typename std::vector>::iterator genEnd, + typename std::vector>::iterator proBegin, + typename std::vector>::iterator carBegin ) +{ + auto const term_case = [&]( signal const& gen0, signal const& gen1, signal const& pro0, signal const& pro1, signal const& car ) -> std::tuple, signal, signal> { + auto tmp = ntk.create_and( gen0, pro1 ); + auto rPro = ntk.create_and( pro0, pro1 ); + auto rGen = ntk.create_or( ntk.create_or( gen1, tmp ), ntk.create_and( rPro, car ) ); + auto rCar = ntk.create_or( gen0, ntk.create_and( pro0, car ) ); + + return { rGen, rPro, rCar }; + }; + + auto m = std::distance( genBegin, genEnd ); + + if ( m == 2 ) + { + const auto [gen, pro, car] = term_case( *genBegin, *( genBegin + 1 ), *proBegin, *( proBegin + 1 ), *carBegin ); + *( carBegin + 1 ) = car; + return { gen, pro }; + } + else + { + m >>= 1; + const auto [gen0, pro0] = carry_lookahead_adder_inplace_rec( ntk, genBegin, genBegin + m, proBegin, carBegin ); + *( carBegin + m ) = gen0; + const auto [gen1, pro1] = carry_lookahead_adder_inplace_rec( ntk, genBegin + m, genEnd, proBegin + m, carBegin + m ); + + const auto [gen, pro, car] = term_case( gen0, gen1, pro0, pro1, *carBegin ); + *( carBegin + m ) = car; + return { gen, pro }; + } +} + +template +inline void carry_lookahead_adder_inplace_pow2( Ntk& ntk, std::vector>& a, std::vector> const& b, signal& carry ) +{ + if ( a.size() == 1u ) + { + a[0] = full_adder( ntk, a[0], b[0], carry ).first; + return; + } + + std::vector> gen( a.size() ), pro( a.size() ), car( a.size() + 1 ); + car[0] = carry; + std::transform( a.begin(), a.end(), b.begin(), gen.begin(), [&]( auto const& f, auto const& g ) { return ntk.create_and( f, g ); } ); + std::transform( a.begin(), a.end(), b.begin(), pro.begin(), [&]( auto const& f, auto const& g ) { return ntk.create_xor( f, g ); } ); + + carry_lookahead_adder_inplace_rec( ntk, gen.begin(), gen.end(), pro.begin(), car.begin() ); + std::transform( pro.begin(), pro.end(), car.begin(), a.begin(), [&]( auto const& f, auto const& g ) { return ntk.create_xor( f, g ); } ); +} + +} // namespace detail + +/*! \brief Creates carry lookahead adder structure. + * + * Creates a carry lookahead structure composed of full adders. The vectors `a` + * and `b` must have the same size. The resulting sum bits are eventually + * stored in `a` and the carry bit will be overridden to store the output carry + * bit. + * + * \param a First input operand, will also have the output after the call + * \param b Second input operand + * \param carry Carry bit, will also have the output carry after the call + */ +template +inline void carry_lookahead_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, signal& carry ) +{ + /* extend bitsize to next power of two */ + const auto log2 = static_cast( std::ceil( std::log2( static_cast( a.size() + 1 ) ) ) ); + + std::vector> a_ext( a.begin(), a.end() ); + a_ext.resize( static_cast( 1 ) << log2, ntk.get_constant( false ) ); + std::vector> b_ext( b.begin(), b.end() ); + b_ext.resize( static_cast( 1 ) << log2, ntk.get_constant( false ) ); + + detail::carry_lookahead_adder_inplace_pow2( ntk, a_ext, b_ext, carry ); + + std::copy_n( a_ext.begin(), a.size(), a.begin() ); + carry = a_ext[a.size()]; +} + +/*! \brief Creates a sideways sum adder using half and full adders + * + * The function creates the adder in `ntk` and returns output signals, + * whose size is floor(log2(a.size()))+1. + * + * \param ntk Network + * \param a Input operand + */ +template +inline std::vector> sideways_sum_adder( Ntk& ntk, std::vector> const& a ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + int n = static_cast( a.size() ); + + int out_n = 1; // floor(log2(n) + 1) + int tmpn = n; + while ( tmpn >>= 1 ) + out_n++; + + std::list> sum_bits, carry_bits; + + auto* first_level = &sum_bits; + auto* second_level = &carry_bits; + + auto res = constant_word( ntk, 0, out_n ); + int output_ind = 0; + + for ( int i = 0; i < n; i++ ) + first_level->push_back( a[i] ); + + while ( 1 ) + { + while ( !first_level->empty() ) + { + if ( first_level->size() == 1 ) + { + auto in_sig = first_level->front(); + first_level->pop_front(); + res[output_ind++] = in_sig; + } + else if ( first_level->size() == 2 ) + { + auto in_sig1 = first_level->front(); + first_level->pop_front(); + auto in_sig2 = first_level->front(); + first_level->pop_front(); + signal tmp_sum; + signal tmp_carry; + std::tie( tmp_sum, tmp_carry ) = half_adder( ntk, in_sig1, in_sig2 ); + first_level->push_back( tmp_sum ); + second_level->push_back( tmp_carry ); + } + else // first_level->size() >=3 + { + auto in_sig1 = first_level->front(); + first_level->pop_front(); + auto in_sig2 = first_level->front(); + first_level->pop_front(); + auto in_sig3 = first_level->front(); + first_level->pop_front(); + signal tmp_sum; + signal tmp_carry; + std::tie( tmp_sum, tmp_carry ) = full_adder( ntk, in_sig1, in_sig2, in_sig3 ); + first_level->push_back( tmp_sum ); + second_level->push_back( tmp_carry ); + } + } + + if ( second_level->empty() ) + break; + + // swapping buffers + auto tmp = first_level; + first_level = second_level; + second_level = tmp; + } + + return res; +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/generators/control.hpp b/third-party/mockturtle/include/mockturtle/generators/control.hpp new file mode 100644 index 00000000000..8c2fe52f2af --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/control.hpp @@ -0,0 +1,217 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file control.hpp + \brief Generate control logic networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Creates a word from a constant + * + * Creates a vector of `bitwidth` constants that represent the positive number + * `value`. + */ +template +inline std::vector> constant_word( Ntk& ntk, uint64_t value, uint32_t bitwidth ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + std::vector> word( bitwidth ); + for ( auto i = 0u; i < bitwidth; ++i ) + { + bool bit = false; + if ( i < 64 ) + { + bit = static_cast( ( value >> i ) & 1 ); + } + word[i] = ntk.get_constant( bit ); + } + return word; +} + +/*! \brief Extends a word by leading zeros + * + * Adds leading zeros as most-significant bits to word `a`. The size of `a` + * must be smaller or equal to `bitwidth`, which is the width of the resulting + * word. + */ +template +inline std::vector> zero_extend( Ntk& ntk, std::vector> const& a, uint32_t bitwidth ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + assert( bitwidth >= a.size() ); + + auto ret{ a }; + for ( auto i = a.size(); i < bitwidth; ++i ) + { + ret.emplace_back( ntk.get_constant( false ) ); + } + return ret; +} + +/*! \brief Creates a 2k-k MUX (array of k 2-1 MUXes). + * + * This creates *k* MUXes using `cond` as condition signal and `t` for the then + * signals and `e` for the else signals. The method works in-place and writes + * the outputs of the networ into `t`. + */ +template +inline void mux_inplace( Ntk& ntk, signal const& cond, std::vector>& t, std::vector> const& e ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite method" ); + + std::transform( t.begin(), t.end(), e.begin(), t.begin(), [&]( auto const& a, auto const& b ) { return ntk.create_ite( cond, a, b ); } ); +} + +template +inline std::vector> mux( Ntk& ntk, signal const& cond, std::vector> const& t, std::vector> const& e ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite method" ); + + std::vector> ret; + std::transform( t.begin(), t.end(), e.begin(), std::back_inserter( ret ), [&]( auto const& a, auto const& b ) { return ntk.create_ite( cond, a, b ); } ); + return ret; +} + +/*! \brief Creates k-to-2^k binary decoder + * + * Given k signals `xs`, this function creates 2^k signals of which exactly one + * input is 1, for each of the 2^k input assignments to `xs`. + */ +template +std::vector> binary_decoder( Ntk& ntk, std::vector> const& xs ) +{ + if ( xs.empty() ) + { + return {}; + } + + if ( xs.size() == 1u ) + { + return { ntk.create_not( xs[0] ), xs[0] }; + } + + // recursion + const auto m = ( xs.size() + 1 ) / 2; + + const auto d1 = binary_decoder( ntk, std::vector>( xs.begin(), xs.begin() + m ) ); + const auto d2 = binary_decoder( ntk, std::vector>( xs.begin() + m, xs.end() ) ); + + std::vector> d( 1 << xs.size() ); + auto it = d.begin(); + + for ( auto const& s2 : d2 ) + { + for ( auto const& s1 : d1 ) + { + *it++ = ntk.create_and( s1, s2 ); + } + } + + return d; +} + +/*! \brief Creates 2^k MUX + * + * Given k select signals `sel` and 2^k data signals `data`, this function + * creates a logic network that outputs `data[i]` when `i` is the encoded + * assignment of `sel`. + * + * This is an iterative construction based on MUX gates. A more efficient + * method may be provided by the Klein-Paterson variant + * `binary_mux_klein_paterson`. + */ +template +signal binary_mux( Ntk& ntk, std::vector> const& sel, std::vector> data ) +{ + for ( auto i = 0u; i < sel.size(); ++i ) + { + for ( auto j = 0u; j < ( 1u << ( sel.size() - i - 1u ) ); ++j ) + { + data[j] = ntk.create_ite( sel[i], data[2 * j + 1], data[2 * j] ); + } + } + + return data[0u]; +} + +/*! \brief Creates 2^k MUX + * + * Given k select signals `sel` and 2^k data signals `data`, this function + * creates a logic network that outputs `data[i]` when `i` is the encoded + * assignment of `sel`. + * + * This Klein-Paterson variant uses fewer gates than the direct method + * `binary_mux` (see Klein, & Paterson. (1980). Asymptotically Optimal Circuit + * for a Storage Access Function. IEEE Transactions on Computers, C-29(8), + * 737–738. doi:10.1109/tc.1980.1675657 ) + */ +template +signal binary_mux_klein_paterson( Ntk& ntk, std::vector> const& sel, std::vector> const& data ) +{ + if ( sel.size() == 1u ) + { + return ntk.create_ite( sel[0u], data[1u], data[0u] ); + } + + // recursion + const auto s = sel.size() / 2u; + const auto r = sel.size() - s; + + const auto ds = binary_decoder( ntk, std::vector>( sel.begin(), sel.begin() + s ) ); + std::vector> s_data( 1u << r ); + for ( auto j = 0u; j < s_data.size(); ++j ) + { + std::vector> and_terms( 1u << s ); + std::transform( ds.begin(), ds.end(), + data.begin() + ( j * ( 1u << s ) ), + and_terms.begin(), + [&]( auto const& f1, auto const& f2 ) { return ntk.create_and( f1, f2 ); } ); + s_data[j] = ntk.create_nary_or( and_terms ); + } + + return binary_mux_klein_paterson( ntk, std::vector>( sel.begin() + s, sel.end() ), s_data ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/generators/legacy.hpp b/third-party/mockturtle/include/mockturtle/generators/legacy.hpp new file mode 100644 index 00000000000..f278d64832a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/legacy.hpp @@ -0,0 +1,209 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file legacy.hpp + \brief Some older not used routines + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include + +#include "../traits.hpp" +#include "arithmetic.hpp" +#include "control.hpp" + +namespace mockturtle +{ + +namespace legacy +{ + +namespace detail +{ + +template +inline std::pair compute_montgomery_parameters( IntType c, IntType k = 0 ) +{ + if ( k == 0 ) + { + k = 1 << ( static_cast( std::ceil( std::log2( c ) ) ) + 1 ); + } + + // egcd + IntType y = k % c; + IntType x = c; + IntType a{ 0 }, b{ 1 }; + + while ( y ) + { + std::tie( a, b ) = std::pair{ b, a - ( x / y ) * b }; + std::tie( x, y ) = std::pair{ y, x % y }; + } + + const IntType ki = ( a > 0 ) ? ( a % c ) : ( c + ( a % c ) % c ); + const IntType factor = ( k * ki - 1 ) / c; + + return { k, factor }; +} + +template +std::vector> to_montgomery_form( Ntk& ntk, std::vector> const& t, int32_t mod, uint32_t rbits, int64_t np ) +{ + /* bit-width of original mod */ + uint32_t nbits = t.size() - rbits; + + std::vector> t_rpart( t.begin(), t.begin() + rbits ); + auto m = carry_ripple_multiplier( ntk, t_rpart, constant_word( ntk, np, rbits ) ); + assert( m.size() == 2 * rbits ); + m.resize( rbits ); + assert( m.size() == rbits ); + + m = carry_ripple_multiplier( ntk, m, constant_word( ntk, mod, nbits ) ); + assert( m.size() == t.size() ); + + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, m, t, carry ); + + m.erase( m.begin(), m.begin() + rbits ); + assert( m.size() == nbits ); + + std::vector> sum( m.begin(), m.end() ); + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, sum, constant_word( ntk, mod, nbits ), carry_inv ); + + mux_inplace( ntk, !carry, m, sum ); + return m; +} + +} /* namespace detail */ + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod (2^k - + * c)\f$. The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t c ) +{ + /* c must be smaller than 2^k */ + assert( c < ( UINT64_C( 1 ) << a.size() ) ); + + /* refer to simpler case */ + if ( c == 0 ) + { + modular_adder_inplace( ntk, a, b ); + return; + } + + const auto word = constant_word( ntk, c, static_cast( a.size() ) ); + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a, word, carry ); + + carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a, b, carry ); + + std::vector> sum( a.begin(), a.end() ); + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a, word, carry_inv ); + + mux_inplace( ntk, !carry, a, sum ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod (2^k - + * c)\f$. The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t c ) +{ + /* c must be smaller than 2^k */ + assert( c < ( UINT64_C( 1 ) << a.size() ) ); + + /* refer to simpler case */ + if ( c == 0 ) + { + modular_subtractor_inplace( ntk, a, b ); + return; + } + + auto carry = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a, b, carry ); + + const auto word = constant_word( ntk, c, static_cast( a.size() ) ); + std::vector> sum( a.begin(), a.end() ); + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, sum, word, carry_inv ); + + mux_inplace( ntk, carry, a, sum ); +} + +/*! \brief Creates modular multiplication based on Montgomery multiplication + * + * Given two inputs words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(ab) \bmod (2^k - c)\f$. + * The first input word `a` is overridden and stores the output signals. + * + * The implementation is based on Montgomery multiplication and includes the + * encoding and decoding in and from the Montgomery number representation. + * Correct functionality is only ensured if both `a` and `b` are smaller than + * \f$2^k - c\f$. + */ +template +inline void modular_multiplication_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t c ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + const auto n = ( 1 << a.size() ) - c; + const auto nbits = static_cast( std::ceil( std::log2( n ) ) ); + + auto [r, np] = detail::compute_montgomery_parameters( n ); + const auto rbits = static_cast( std::log2( r ) ); + + const auto f2 = constant_word( ntk, ( r * r ) % n, rbits ); + + const auto ma = detail::to_montgomery_form( ntk, carry_ripple_multiplier( ntk, a, f2 ), n, rbits, np ); + const auto mb = detail::to_montgomery_form( ntk, carry_ripple_multiplier( ntk, b, f2 ), n, rbits, np ); + + assert( ma.size() == nbits ); + assert( mb.size() == nbits ); + + a = detail::to_montgomery_form( ntk, zero_extend( ntk, carry_ripple_multiplier( ntk, ma, mb ), nbits + rbits ), n, rbits, np ); + a = detail::to_montgomery_form( ntk, zero_extend( ntk, a, nbits + rbits ), n, rbits, np ); +} + +} // namespace legacy + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/generators/majority.hpp b/third-party/mockturtle/include/mockturtle/generators/majority.hpp new file mode 100644 index 00000000000..3aa22c21924 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/majority.hpp @@ -0,0 +1,136 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file majority.hpp + \brief Generate majority-n networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +template +signal fake_majority9( Ntk& ntk, std::array, 9> const& xs ) +{ + return ntk.create_maj( + ntk.create_maj( xs[0], xs[1], xs[2] ), + ntk.create_maj( xs[3], xs[4], xs[5] ), + ntk.create_maj( xs[6], xs[7], xs[8] ) ); +} + +template +signal general_associativity( Ntk& ntk, signal const& y, std::vector> const& xs ) +{ + assert( xs.size() >= 2u ); + + return std::accumulate( xs.rbegin() + 1, xs.rend(), xs.back(), + [&]( auto const& f, auto const& x ) { return ntk.create_maj( x, y, f ); } ); +} + +} // namespace detail + +/*! \brief Implements Majority-5 using 4 MAJ operations. + * + * All majority operations require no inverters and are leafy. + */ +template +signal majority5( Ntk& ntk, std::array, 5> const& xs ) +{ + const auto lhs = ntk.create_maj( xs[0], xs[1], xs[2] ); + const auto rhs = detail::general_associativity( ntk, xs[3], { xs[0], xs[1], xs[2] } ); + return ntk.create_maj( lhs, xs[4], rhs ); +} + +/*! \brief Implements Majority-7 using 7 MAJ operations. + * + * All majority operations require no inverters and are leafy. + */ +template +signal majority7( Ntk& ntk, std::array, 7> const& xs ) +{ + const auto side = [&]( std::array, 6> const& ws ) { + const auto l1 = ntk.create_maj( ws[0], ws[1], ws[2] ); + return detail::general_associativity( ntk, l1, { ws[3], ws[4], ws[5] } ); + }; + + return ntk.create_maj( + side( { xs[1], xs[2], xs[3], xs[4], xs[5], xs[6] } ), + xs[0], + side( { xs[4], xs[5], xs[6], xs[1], xs[2], xs[3] } ) ); +} + +/*! \brief Implements Majority-9 using 13 MAJ operations. + * + * All majority operations require no inverters. + */ +template +signal majority9_13( Ntk& ntk, std::array, 9> const& xs ) +{ + const auto side = [&]( std::array, 9> const& ws ) { + const auto l1 = ntk.create_maj( ws[3], ws[4], ws[5] ); + const auto l2 = detail::general_associativity( ntk, l1, { ws[0], ws[1], ws[2] } ); + return detail::general_associativity( ntk, l2, { ws[6], ws[7], ws[8] } ); + }; + + return ntk.create_maj( + side( xs ), + detail::fake_majority9( ntk, xs ), + side( { xs[0], xs[1], xs[2], xs[6], xs[7], xs[8], xs[3], xs[4], xs[5] } ) ); +} + +/*! \brief Implements Majority-9 using 12 MAJ operations. + * + * This construction requires one inverter. + */ +template +signal majority9_12( Ntk& ntk, std::array, 9> const& xs ) +{ + const auto side = [&]( std::array, 9> const& ws ) { + const auto bottom = ntk.create_maj( ntk.create_not( ws[0] ), ws[1], ws[2] ); + const auto l1 = ntk.create_maj( ws[3], ws[4], ws[5] ); + const auto l2 = ntk.create_maj( ws[0], l1, bottom ); + return detail::general_associativity( ntk, l2, { ws[6], ws[7], ws[8] } ); + }; + + return ntk.create_maj( + side( xs ), + detail::fake_majority9( ntk, xs ), + side( { xs[0], xs[1], xs[2], xs[6], xs[7], xs[8], xs[3], xs[4], xs[5] } ) ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/generators/majority_n.hpp b/third-party/mockturtle/include/mockturtle/generators/majority_n.hpp new file mode 100644 index 00000000000..590f781dc98 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/majority_n.hpp @@ -0,0 +1,90 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/*! + \file majority_n.hpp + \brief Generate majority-n networks using BDD and sorter network based methods + + \author Dewmini Sudara + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "sorting.hpp" + +#include +#include + +namespace mockturtle +{ +/**! \brief Generates majority-n network on given input signals using the BDD based method + * + * All majority operations are leafy. + */ +template +signal majority_n_bdd( Ntk& ntk, std::array, N> const& xs ) +{ + const auto logic1 = ntk.get_constant( true ); + const auto logic0 = ntk.get_constant( false ); + std::array>, N> dp; + dp[0].push_back( xs[0] ); + for ( auto r = 1u; r <= xs.size() / 2; r++ ) + { + dp[r].push_back( ntk.create_maj( logic0, dp[r - 1][0], xs[r] ) ); + for ( auto c = 1u; c < r; c++ ) + { + dp[r].push_back( ntk.create_maj( dp[r - 1][c - 1], dp[r - 1][c], xs[r] ) ); + } + dp[r].push_back( ntk.create_maj( logic1, dp[r - 1][r - 1], xs[r] ) ); + } + for ( auto r = xs.size() / 2 + 1; r < xs.size(); r++ ) + { + for ( auto c = 0u; c < xs.size() - r; c++ ) + { + dp[r].push_back( ntk.create_maj( dp[r - 1][c], dp[r - 1][c + 1], xs[r] ) ); + } + } + return dp[xs.size() - 1][0]; +} + +/**! \brief Generates majority-n network on given input signals using bubble sort. + * + * All majority operations require no inverters and are leafy. + */ +template +signal majority_n_bubble_sort( Ntk& ntk, std::array, N> const& xs ) +{ + std::vector> sigs( xs.begin(), xs.end() ); + bubble_sorting_network( static_cast( sigs.size() ), [&sigs, &ntk]( auto i, auto j ) { + signal lhs = ntk.create_and( sigs[i], sigs[j] ); + signal rhs = ntk.create_or( sigs[i], sigs[j] ); + sigs[i] = lhs; + sigs[j] = rhs; + } ); + return sigs[sigs.size() / 2]; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/generators/modular_arithmetic.hpp b/third-party/mockturtle/include/mockturtle/generators/modular_arithmetic.hpp new file mode 100644 index 00000000000..9018a28f47d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/modular_arithmetic.hpp @@ -0,0 +1,796 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file modular_arithmetic.hpp + \brief Generate modular arithmetic logic networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include + +#include "../traits.hpp" +#include "arithmetic.hpp" +#include "control.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +inline void invert_modulus( std::vector& m ) +{ + m.flip(); + + for ( auto i = 0u; i < m.size(); ++i ) + { + m[i] = !m[i]; + if ( m[i] ) + break; + } +} + +inline void increment_inplace( std::vector& word ) +{ + auto it = word.begin(); + + while ( it != word.end() ) + { + if ( ( *it++ = !*it ) ) + { + return; + } + } +} + +inline std::vector increment( std::vector const& word ) +{ + auto copy = word; + increment_inplace( copy ); + return copy; +} + +inline void decrement_inplace( std::vector& word ) +{ + auto it = word.begin(); + + while ( it != word.end() ) + { + if ( !( *it++ = !*it ) ) + { + return; + } + } +} + +inline std::vector decrement( std::vector const& word ) +{ + auto copy = word; + decrement_inplace( copy ); + return copy; +} + +} /* namespace detail */ + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod 2^k\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b ) +{ + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a, b, carry ); +} + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod m\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, std::vector const& m ) +{ + // bit-size for corrected addition + const uint32_t bitsize = static_cast( m.size() ); + assert( bitsize <= a.size() ); + + // corrected registers + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + std::vector> b_trim( b.begin(), b.begin() + bitsize ); + + // 1. Compute (a + b) on bitsize bits + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a_trim, b_trim, carry ); /* a_trim <- a + b */ + + // store result in sum (and extend it to bitsize + 1 bits) + auto sum = a_trim; /* sum <- a + b */ + sum.emplace_back( ntk.get_constant( false ) ); + + // 2. Compute (a + b) - m (m is represented as word) on (bitsize + 1) bits + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + auto carry_inv = ntk.get_constant( true ); + a_trim.emplace_back( carry ); + carry_ripple_subtractor_inplace( ntk, a_trim, word, carry_inv ); /* a_trim <- a + b - c */ + + // if overflow occurred in step 2, return result from step 2, otherwise, result from step 1. + mux_inplace( ntk, carry_inv, a_trim, sum ); + + // copy corrected register back into input register + std::copy_n( a_trim.begin(), bitsize, a.begin() ); +} + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod m\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + // simpler case + if ( m == ( UINT64_C( 1 ) << a.size() ) ) + { + modular_adder_inplace( ntk, a, b ); + return; + } + + // bit-size for corrected addition + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_adder_inplace( ntk, a, b, mvec ); +} + +template +inline std::vector> modular_adder( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& m ) +{ + auto w = a; + modular_adder_inplace( ntk, w, b, m ); + return w; +} + +template +inline void modular_adder_hiasat_inplace( Ntk& ntk, std::vector>& x, std::vector> const& y, std::vector const& m ) +{ + assert( m.size() <= x.size() ); + assert( x.size() == y.size() ); + + const uint32_t bitsize = static_cast( m.size() ); + + // corrected registers + std::vector> x_trim( x.begin(), x.begin() + bitsize ); + std::vector> y_trim( y.begin(), y.begin() + bitsize ); + + // compute Z-vector from m-vector (Z = 2^bitsize - m) + auto z = m; + detail::invert_modulus( z ); + + /* SAC unit */ + std::vector> A( bitsize ), B( bitsize + 1 ), a( bitsize ), b( bitsize + 1 ); + + B[0] = b[0] = ntk.get_constant( false ); + for ( auto i = 0u; i < bitsize; ++i ) + { + A[i] = ntk.create_xor( x_trim[i], y_trim[i] ); + B[i + 1] = ntk.create_and( x_trim[i], y_trim[i] ); + a[i] = z[i] ? ntk.create_xnor( x_trim[i], y_trim[i] ) : A[i]; + b[i + 1] = z[i] ? ntk.create_or( x_trim[i], y_trim[i] ) : B[i + 1]; + } + + /* CPG unit */ + std::vector> G( bitsize ), P( bitsize + 1 ), g( bitsize ), p( bitsize + 1 ); + for ( auto i = 0u; i < bitsize; ++i ) + { + G[i] = ntk.create_and( A[i], B[i] ); + P[i] = ntk.create_xor( A[i], B[i] ); + g[i] = ntk.create_and( a[i], b[i] ); + p[i] = ntk.create_xor( a[i], b[i] ); + } + P[bitsize] = B[bitsize]; + p[bitsize] = b[bitsize]; + + /* CLA for C_out */ + std::vector> C( bitsize ); + C[0] = p[bitsize]; + for ( auto i = 1u; i < bitsize; ++i ) + { + std::vector> cube; + cube.push_back( g[i] ); + for ( auto j = i + 1u; j < bitsize; ++j ) + { + cube.push_back( p[j] ); + } + C[i] = ntk.create_nary_and( cube ); + } + const auto Cout = ntk.create_nary_or( C ); + // ntk.create_po( Cout ); + + /* MUX store result in p and g */ + p.pop_back(); + P.pop_back(); + mux_inplace( ntk, Cout, g, G ); + mux_inplace( ntk, Cout, p, P ); + + /* CLAS */ + C[0] = ntk.get_constant( false ); + for ( auto i = 1u; i < bitsize; ++i ) + { + C[i] = ntk.create_or( g[i - 1], ntk.create_and( p[i - 1], C[i - 1] ) ); + } + + for ( auto i = 0u; i < bitsize; ++i ) + { + x[i] = ntk.create_xor( p[i], C[i] ); + } +} + +template +inline void modular_adder_hiasat_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + // simpler case + if ( m == ( UINT64_C( 1 ) << a.size() ) ) + { + modular_adder_inplace( ntk, a, b ); + return; + } + + // bit-size for corrected addition + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_adder_hiasat_inplace( ntk, a, b, mvec ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod 2^k\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b ) +{ + auto carry = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a, b, carry ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod m\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, std::vector const& m ) +{ + // bit-size for corrected addition + const uint32_t bitsize = static_cast( m.size() ); + assert( bitsize <= a.size() ); + + // corrected registers + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + std::vector> b_trim( b.begin(), b.begin() + bitsize ); + + // 1. Compute (a - b) on bitsize bits + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a_trim, b_trim, carry_inv ); /* a_trim <- a - b */ + + // store result in sum (and extend it to bitsize + 1 bits) + auto sum = a_trim; /* sum <- a - b */ + + sum.emplace_back( ntk.get_constant( false ) ); + + // 2. Compute (a - b) + m (m is represented as word) on (bitsize + 1) bits + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + auto carry = ntk.get_constant( false ); + a_trim.emplace_back( ntk.create_not( carry_inv ) ); + carry_ripple_adder_inplace( ntk, a_trim, word, carry ); /* a_trim <- (a - b) + c */ + + // if overflow occurred in step 2, return result from step 2, otherwise, result from step 1. + mux_inplace( ntk, carry, a_trim, sum ); + + // copy corrected register back into input register + std::copy_n( a_trim.begin(), bitsize, a.begin() ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod m\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + // simpler case + if ( m == ( UINT64_C( 1 ) << a.size() ) ) + { + modular_subtractor_inplace( ntk, a, b ); + return; + } + + // bit-size for corrected subtraction + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_subtractor_inplace( ntk, a, b, mvec ); +} + +template +inline std::vector> modular_subtractor( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& m ) +{ + auto w = a; + modular_subtractor_inplace( ntk, w, b, m ); + return w; +} + +/*! \brief Creates modular doubling (multiplication by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(2 * a) \bmod m\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_doubling_inplace( Ntk& ntk, std::vector>& a, std::vector const& m ) +{ + assert( a.size() >= m.size() ); + const auto bitsize = m.size(); + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + + std::vector> shifted( bitsize + 1u, ntk.get_constant( false ) ); + std::copy( a_trim.begin(), a_trim.end(), shifted.begin() + 1u ); + std::copy_n( shifted.begin(), bitsize, a_trim.begin() ); + + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, shifted, word, carry_inv ); + + mux_inplace( ntk, ntk.create_not( carry_inv ), a_trim, std::vector>( shifted.begin(), shifted.begin() + bitsize ) ); + std::copy( a_trim.begin(), a_trim.end(), a.begin() ); +} + +/*! \brief Creates modular doubling (multiplication by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(2 * a) \bmod m\f$. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_doubling_inplace( Ntk& ntk, std::vector>& a, uint64_t m ) +{ + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_doubling_inplace( ntk, a, mvec ); +} + +/*! \brief Creates modular halving (corrected division by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a / 2) \bmod m\f$. The + * modulus must be odd, and the function is evaluated to \f$a / 2\f$, if `a` is + * even and to \f$(a + m) / 2\f$, if `a` is odd. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_halving_inplace( Ntk& ntk, std::vector>& a, std::vector const& m ) +{ + assert( a.size() >= m.size() ); + assert( m.size() > 0u ); + assert( m[0] ); + const auto bitsize = m.size(); + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + + std::vector> extended( bitsize + 1u, ntk.get_constant( false ) ), a_extended( bitsize + 1u, ntk.get_constant( false ) ); + std::copy( a_trim.begin(), a_trim.end(), extended.begin() ); + std::copy( a_trim.begin(), a_trim.end(), a_extended.begin() ); + + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, extended, word, carry ); + + mux_inplace( ntk, a_trim[0], extended, a_extended ); + + std::copy_n( extended.begin() + 1, bitsize, a.begin() ); +} + +/*! \brief Creates modular halving (corrected division by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a / 2) \bmod m\f$. The + * modulus must be odd, and the function is evaluated to \f$a / 2\f$, if `a` is + * even and to \f$(a + m) / 2\f$, if `a` is odd. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_halving_inplace( Ntk& ntk, std::vector>& a, uint64_t m ) +{ + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_halving_inplace( ntk, a, mvec ); +} + +/*! \brief Creates modular multiplication + * + * Given two inputs words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(ab) \bmod c\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_multiplication_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, std::vector const& m ) +{ + assert( a.size() >= m.size() ); + assert( a.size() == b.size() ); + + const auto bitsize = m.size(); + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + std::vector> b_trim( b.begin(), b.begin() + bitsize ); + + std::vector> accu( bitsize ); + auto itA = a_trim.rbegin(); + std::transform( b_trim.begin(), b_trim.end(), accu.begin(), [&]( auto const& f ) { return ntk.create_and( *itA, f ); } ); + + while ( ++itA != a_trim.rend() ) + { + modular_doubling_inplace( ntk, accu, m ); + std::vector> summand( bitsize ); + std::transform( b_trim.begin(), b_trim.end(), summand.begin(), [&]( auto const& f ) { return ntk.create_and( *itA, f ); } ); + modular_adder_inplace( ntk, accu, summand, m ); + } + + std::copy( accu.begin(), accu.end(), a.begin() ); +} + +/*! \brief Creates modular multiplier + * + * Given two inputs words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(ab) \bmod c\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_multiplication_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_multiplication_inplace( ntk, a, b, mvec ); +} + +template +inline std::vector> modular_multiplication( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& m ) +{ + auto w = a; + modular_multiplication_inplace( ntk, w, b, m ); + return w; +} + +template +inline std::vector> modular_constant_multiplier_one_bits( Ntk& ntk, std::vector> const& a, std::vector const& constant ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + std::vector> sum( a.size(), ntk.get_constant( false ) ); + + auto it = std::find( constant.begin(), constant.end(), true ); + if ( it != constant.end() ) + { + auto shift = std::distance( constant.begin(), it ); + std::copy_n( a.begin(), a.size() - shift, sum.begin() + shift ); + it = std::find( it + 1, constant.end(), true ); + + while ( it != constant.end() ) + { + shift = std::distance( constant.begin(), it ); + std::vector> summand( a.size(), ntk.get_constant( false ) ); + std::copy_n( a.begin(), a.size() - shift, summand.begin() + shift ); + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, sum, summand, carry ); + it = std::find( it + 1, constant.end(), true ); + } + } + + return sum; +} + +template +inline std::vector> modular_constant_multiplier_csd( Ntk& ntk, std::vector> const& a, std::vector const& constant ) +{ + // constant == 0 + if ( std::find( constant.begin(), constant.end(), true ) == constant.end() ) + { + return std::vector>( a.size(), ntk.get_constant( false ) ); + } + // constant == 1 + else if ( constant.front() && std::find( constant.begin() + 1, constant.end(), true ) == constant.end() ) + { + return a; + } + // constant % 2 == 0 + else if ( !constant.front() ) + { + // constant / 2 + std::vector new_constant( a.size(), false ); + std::copy( constant.begin() + 1, constant.end(), new_constant.begin() ); + auto res = modular_constant_multiplier( ntk, a, new_constant ); + res.insert( res.begin(), ntk.get_constant( false ) ); + res.pop_back(); + return res; + } + // constant % 4 == 1u + else if ( !constant[1] ) + { + auto res = modular_constant_multiplier( ntk, a, detail::decrement( constant ) ); + modular_adder_inplace( ntk, res, a ); + return res; + } + else /* ( constant % 4 == 3u ) */ + { + auto res = modular_constant_multiplier( ntk, a, detail::increment( constant ) ); + modular_subtractor_inplace( ntk, res, a ); + return res; + } +} + +/*! \brief Creates modular constant-multiplier + * + * Given an input word of size *k* and a constant with the same bit-width, + * this function creates a circuit that computes \f$(a\cdot\mathrm{constant}) \bmod 2^k\f$. + */ +template +inline std::vector> modular_constant_multiplier( Ntk& ntk, std::vector> const& a, std::vector const& constant ) +{ + return modular_constant_multiplier_csd( ntk, a, constant ); +} + +/*! \brief Creates vector of Booleans from hex string + * + * This function can be used to create moduli for very large numbers that cannot + * be represented using any of the integer built-in data types. If the vector + * `res` is too small for the value `hex` most-significant digits will be + * ignored. + */ +inline void bool_vector_from_hex( std::vector& res, std::string_view hex, bool shrink_to_fit = true ) +{ + auto itR = res.begin(); + auto itS = hex.rbegin(); + + while ( itR != res.end() && itS != hex.rend() ) + { + uint32_t number{ 0 }; + if ( *itS >= '0' && *itS <= '9' ) + { + number = *itS - '0'; + } + else if ( *itS >= 'a' && *itS <= 'f' ) + { + number = *itS - 'a' + 10; + } + else if ( *itS >= 'A' && *itS <= 'F' ) + { + number = *itS - 'A' + 10; + } + else + { + assert( false && "invalid hex number" ); + } + + for ( auto i = 0u; i < 4u; ++i ) + { + *itR++ = ( number >> i ) & 1; + if ( itR == res.end() ) + { + break; + } + } + + ++itS; + } + + if ( shrink_to_fit ) + { + auto find_last = []( std::vector::const_iterator itFirst, + std::vector::const_iterator itLast, + bool value ) -> std::vector::const_iterator { + auto cur = itLast; + while ( itFirst != itLast ) + { + if ( *itFirst == value ) + { + cur = itFirst; + } + ++itFirst; + } + return cur; + }; + + const auto itLast = find_last( res.begin(), res.end(), true ); + if ( itLast == res.end() ) + { + res.clear(); + } + else + { + res.erase( find_last( res.begin(), res.end(), true ) + 1u, res.end() ); + } + } + else + { + /* in case the hex string was short, fill remaining values with false */ + std::fill( itR, res.end(), false ); + } +} + +inline void bool_vector_from_dec( std::vector& res, uint64_t value ) +{ + auto it = res.begin(); + while ( value && it != res.end() ) + { + *it++ = value % 2; + value >>= 1; + } +} + +inline uint64_t bool_vector_to_long( std::vector const& vec ) +{ + return std::accumulate( vec.begin(), vec.end(), std::make_pair( 0u, 0ul ), + []( auto accu, auto bit ) { + return std::make_pair( accu.first + 1u, accu.second + ( bit ? 1ul << accu.first : 0ul ) ); + } ) + .second; +} + +/*! \brief Creates a multiplier assuming Montgomery numbers as inputs. + * + * This modular multiplication assumes the two inputs *a* and *b* to be + * Montgomery numbers representing \f$a \cdot 2^k \bmod N\f$, where \f$N\f$ is + * the modulus as bit-string, and \f$k\f$ is the bit-width of *a* and *b*. It + * returns a signal of length *b*. The last paramaeter *NN* must be computed + * such that \f$R \cdot 2^k = N \cdot NN\f$ using the extended GCD. + */ +template +inline std::vector> montgomery_multiplication( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& N, std::vector const& NN ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + assert( a.size() == b.size() ); + + const auto logR = a.size(); + + std::vector> Nbits( logR, ntk.get_constant( false ) ); + std::transform( N.begin(), N.end(), Nbits.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + + /* multiply a and b and truncate to least-significant logR bits */ + auto mult1 = carry_ripple_multiplier( ntk, a, b ); + std::vector> mult1_truncated( mult1.begin(), mult1.begin() + logR ); + + /* compute (((a * b) % R) * NN) % R */ + auto mult2 = modular_constant_multiplier( ntk, mult1_truncated, NN ); + + mult2.resize( 2 * logR, ntk.get_constant( false ) ); + auto Ncopy = N; + Ncopy.resize( 2 * logR, false ); + auto summand = modular_constant_multiplier( ntk, mult2, Ncopy ); + + assert( mult1.size() == 2 * logR ); + assert( summand.size() == 2 * logR ); + + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, mult1, summand, carry ); + mult1.erase( mult1.begin(), mult1.begin() + logR ); + + auto tcopy = mult1; + carry = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, tcopy, Nbits, carry ); + mux_inplace( ntk, carry, tcopy, mult1 ); + + return tcopy; +} + +/*! \brief Creates a multiplier assuming Montgomery numbers as inputs. + * + * This modular multiplication assumes the two inputs *a* and *b* to be + * Montgomery numbers representing \f$a \cdot 2^k \bmod N\f$, where \f$N\f$ is + * the modulus, and \f$k\f$ is the bit-width of *a* and *b*. It returns a + * signal of length *b*. + */ +template +inline std::vector> montgomery_multiplication( Ntk& ntk, std::vector> const& a, std::vector> const& b, uint64_t N ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + const auto logR = a.size(); + const auto R = 1 << logR; + + // egcd + int64_t s = 0, old_s = 1, r = R, old_r = N; + while ( r ) + { + const auto q = old_r / r; + std::tie( old_r, r ) = std::pair{ r, old_r - q * r }; + std::tie( old_s, s ) = std::pair{ s, old_s - q * s }; + } + const auto NN = std::abs( old_s ); + + std::vector Nvec( logR ), NNvec( logR ); + for ( auto i = 0u; i < logR; ++i ) + { + Nvec[i] = static_cast( ( N >> i ) & 1 ); + NNvec[i] = static_cast( ( NN >> i ) & 1 ); + } + + // std::cout << fmt::format( "[i] R = {}, NN = {}, N = {}\n", R, NN, N ); + + return montgomery_multiplication( ntk, a, b, Nvec, NNvec ); +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/generators/random_network.hpp b/third-party/mockturtle/include/mockturtle/generators/random_network.hpp new file mode 100644 index 00000000000..a0c8ddf5624 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/random_network.hpp @@ -0,0 +1,672 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file random_network.hpp + \brief Generate random logic networks + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/mig.hpp" +#include "../networks/xag.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for random_network_generator. + * + * When this parameter object is used, random_network_generator + * generates networks according to the specified number of PIs + * and number of gates. After generating primary inputs and gates, + * all nodes with no fanout become primary outputs. After generating + * `num_networks_per_configuration` networks (i.e. `generate` being + * called this number of times), the configuration (numbers of PIs + * and gates) will be incremented by `num_pis_increment` and + * `num_gates_increment`, respectively. + */ +struct random_network_generator_params_size +{ + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; + + /*! \brief Number of networks of each configuration to generate + * before increasing size. */ + uint32_t num_networks_per_configuration{ 100u }; + + /*! \brief Number of PIs to start with. */ + uint32_t num_pis{ 4u }; + + /*! \brief Number of gates to start with. */ + uint32_t num_gates{ 10u }; + + /*! \brief Number of PIs to increment at each step. */ + uint32_t num_pis_increment{ 0u }; + + /*! \brief Number of gates to increment at each step. */ + uint32_t num_gates_increment{ 0u }; +}; /* random_network_generator_params_size */ + +/*! \brief Parameters for random_network_generator. + * + * When this parameter object is used, random_network_generator + * first enumerates all non-isomorphic connected partial DAGs of + * `num_gates` vertices, randomly shuffles them, and then generates + * `num_networks_per_configuration` random networks of each topology. + * It is guaranteed that all possible topologies are generated for + * the same number of times, provided that `generate` is called for + * enough times. After all topologies have been generated, `num_gates` + * is increased by 1 and the above steps are repeated. + * + * This method currently only supports generating 2-regular (i.e., + * each gate has two fanins), single-output DAGs. + * + * Guideline on how many iterations are needed to visit all topologies: + * - `num_gates` = 2: 1 topology + * - `num_gates` = 3: 3 topologies + * - `num_gates` = 4: 10 topologies + * - `num_gates` = 5: 49 topologies + * - `num_gates` = 6: 302 topologies + * - `num_gates` = 7: 2312 topologies + * - `num_gates` = 8: 21218 topologies + * - `num_gates` = 9: 228249 topologies + * + * For example, starting from `num_gates` = 3 and using + * `num_networks_per_configuration` = 100, to generate networks + * of all topologies with no more than 5 vertices, `generate` + * should be called (3 + 10 + 49) * 100 = 6200 times. + * + * For each topology, a random network is concretized from the partial + * DAG by randomly choosing a (#PIs/#inputs of DAG) ratio and allocating + * the corresponding number of PIs, randomly choosing a PI to be connected + * to each input of the partial DAG, and randomly deciding if each edge + * should be complemented. + */ +struct random_network_generator_params_topology +{ + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; + + /*! \brief Number of networks to generate for each topology. */ + uint32_t num_networks_per_configuration{ 100u }; + + /*! \brief Number of gates to start with. */ + uint32_t num_gates{ 3u }; + + /*! \brief Minimum ratio of (#PIs/#inputs of DAG). + * Lower ratio makes more reconvergences. */ + float min_PI_ratio{ 0.5 }; + + /*! \brief Maximum ratio of (#PIs/#inputs of DAG). + * Higher ratio makes it more likely to sample a full tree. */ + float max_PI_ratio{ 1.0 }; +}; /* random_network_generator_params_topology */ + +/*! \brief Parameters for random_network_generator. + * + * When this parameter object is used, random_network_generator + * first enumerates all non-isomorphic connected partial DAGs with + * numbers of vertices between `min_num_gates_component` and + * `max_num_gates_component`. Random networks are generated by + * randomly generating `num_components` DAGs of randomly-sampled + * topologies (repeated topologies are allowed). The first topology + * is concretized by connecting inputs to randomly-chosen PIs, and + * the remaining ones are concretized by connecting inputs to PIs + * or nodes in the previous components. + * + * After generating `num_networks_per_configuration` networks, + * `num_components` is increased by `num_components_increment` + * and `num_pis` is increased by `num_pis_increment`. + */ +struct random_network_generator_params_composed +{ + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; + + /*! \brief Number of networks to generate for each topology. */ + uint32_t num_networks_per_configuration{ 1000u }; + + /*! \brief Minimum number of gates of the components. */ + uint32_t min_num_gates_component{ 3u }; + + /*! \brief Maximum number of gates of the components. */ + uint32_t max_num_gates_component{ 5u }; + + /*! \brief Number of components to start with. */ + uint32_t num_components{ 2u }; + + /*! \brief Number of PIs to start with. */ + uint32_t num_pis{ 4u }; + + /*! \brief Number of components to increment at each step. */ + uint32_t num_components_increment{ 1u }; + + /*! \brief Number of PIs to increment at each step. */ + uint32_t num_pis_increment{ 2u }; +}; /* random_network_generator_params_composed */ + +namespace detail +{ + +template +struct create_gate_rule +{ + using signal = typename Ntk::signal; + + std::function const& )> func; + uint32_t num_args; +}; + +} /* namespace detail */ + +/*! \brief Generates random logic networks + * + * Generate random logic networks with certain parameters. + * + * The constructor takes a vector of construction rules, which are + * used in the algorithm to build the logic network. The constructor + * also takes a parameter object, which can be of various types and + * influences the way networks are generated. + * + * The function `generate` returns a random network and can be called + * repeatedly, each time generating a different network. + * + */ +template +class random_network_generator +{ +}; + +template +class random_network_generator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using rand_engine_t = std::mt19937; + using rules_t = std::vector>; + using params_t = random_network_generator_params_size; + +private: /* common data members */ + rules_t const _gens; + params_t const _ps; + rand_engine_t _rng; + +public: + explicit random_network_generator( rules_t const& gens, params_t ps = {} ) + : _gens( gens ), _ps( ps ), _rng( static_cast( ps.seed ) ), + _counter( 0u ), _num_pis( ps.num_pis ), _num_gates( ps.num_gates ) + { + } + + Ntk generate() + { + assert( _num_pis > 0 ); + assert( _num_gates > 0 ); + + std::vector fs; + Ntk ntk; + + /* generate constant */ + fs.emplace_back( ntk.get_constant( false ) ); + + /* generate pis */ + for ( auto i = 0u; i < _num_pis; ++i ) + { + fs.emplace_back( ntk.create_pi() ); + } + + /* generate gates */ + std::uniform_int_distribution rule_dist( 0, static_cast( _gens.size() - 1u ) ); + + auto gate_counter = ntk.num_gates(); + while ( gate_counter < _num_gates ) + { + auto const r = _gens.at( rule_dist( _rng ) ); + + std::uniform_int_distribution dist( 0, static_cast( fs.size() - 1 ) ); + std::vector args; + for ( auto i = 0u; i < r.num_args; ++i ) + { + auto const a_compl = dist( _rng ) & 1; + auto const a = fs.at( dist( _rng ) ); + args.emplace_back( a_compl ? !a : a ); + } + + auto const g = r.func( ntk, args ); + if ( ntk.num_gates() > gate_counter ) + { + fs.emplace_back( g ); + ++gate_counter; + } + + assert( ntk.num_gates() == gate_counter ); + } + + /* generate pos */ + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.fanout_size( n ) == 0u ) + { + ntk.create_po( ntk.make_signal( n ) ); + } + } ); + + assert( ntk.num_pis() == _num_pis ); + assert( ntk.num_gates() == _num_gates ); + + if ( ++_counter >= _ps.num_networks_per_configuration ) + { + _counter = 0; + _num_gates += _ps.num_gates_increment; + _num_pis += _ps.num_pis_increment; + } + + return ntk; + } + +private: + uint32_t _counter; + uint32_t _num_pis; + uint32_t _num_gates; +}; /* random_network_generator */ + +#ifdef ENABLE_NAUTY +template +class random_network_generator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using rand_engine_t = std::mt19937; + using rules_t = std::vector>; + using params_t = random_network_generator_params_topology; + +private: /* common data members */ + rules_t const _gens; + params_t const _ps; + rand_engine_t _rng; + +public: + explicit random_network_generator( rules_t const& gens, params_t ps = {} ) + : _gens( gens ), _ps( ps ), _rng( static_cast( ps.seed ) ), + _counter( 0u ), _num_gates( ps.num_gates ), _ith_dag( 0u ), + _rule_dist( 0, _gens.size() - 1u ) + { + } + + Ntk generate() + { + if ( _counter == 0 ) + { + if ( _ith_dag == 0 ) + { + prepare_partial_dags(); + } + auto num_inputs = _dags.at( _ith_dag ).nr_pi_fanins(); + uint32_t min_num_pis = std::ceil( _ps.min_PI_ratio * num_inputs ); + uint32_t max_num_pis = std::ceil( _ps.max_PI_ratio * num_inputs ); + _num_pis_dist = std::uniform_int_distribution( min_num_pis, max_num_pis ); + } + + percy::partial_dag const& pd = _dags.at( _ith_dag ); + uint32_t num_pis = _num_pis_dist( _rng ); + std::uniform_int_distribution pis_dist( 1u, num_pis ); + + std::vector fs; + Ntk ntk; + + /* generate constant */ + fs.emplace_back( ntk.get_constant( false ) ); + + /* generate pis */ + for ( auto i = 0u; i < num_pis; ++i ) + { + fs.emplace_back( ntk.create_pi() ); + } + + /* generate gates */ + pd.foreach_vertex( [&]( auto const& v, auto i ) { + uint32_t size_before = ntk.num_gates(); + signal g; + + do + { + auto const r = _gens.at( _rule_dist( _rng ) ); + std::vector args; + + for ( auto fi : v ) + { + bool const inv = _rng() & 1; + if ( fi == percy::FANIN_PI ) + { + auto const& a = fs.at( pis_dist( _rng ) ); + args.emplace_back( inv ? !a : a ); + } + else + { + assert( fi >= 1 && num_pis + fi < fs.size() ); + auto const& a = fs.at( num_pis + fi ); + args.emplace_back( inv ? !a : a ); + } + } + + g = r.func( ntk, args ); + } while ( ntk.num_gates() == size_before ); + + fs.emplace_back( g ); + } ); + + /* generate pos */ + ntk.foreach_gate( [&]( auto const& n ) { + if ( ntk.fanout_size( n ) == 0u ) + { + ntk.create_po( ntk.make_signal( n ) ); + } + } ); + + if ( ++_counter >= _ps.num_networks_per_configuration ) + { + _counter = 0; + if ( ++_ith_dag >= _dags.size() ) + { + _ith_dag = 0; + ++_num_gates; + } + } + + return ntk; + } + +private: + void prepare_partial_dags() + { + using namespace percy; + + _dags.clear(); + partial_dag g; + partial_dag_generator gen( _num_gates ); + std::set> can_reprs; + pd_iso_checker checker( _num_gates ); + + gen.set_callback( [&]( partial_dag_generator* gen ) { + for ( int i = 0; i < gen->nr_vertices(); i++ ) + { + g.set_vertex( i, gen->_js[i], gen->_ks[i] ); + } + const auto can_repr = checker.crepr( g ); + const auto res = can_reprs.insert( can_repr ); + if ( res.second ) + _dags.push_back( g ); + } ); + gen.gen_type( partial_gen_type::GEN_COLEX ); + g.reset( 2, _num_gates ); + gen.count_dags(); + + std::shuffle( _dags.begin(), _dags.end(), _rng ); + } + +private: + uint32_t _counter; + uint32_t _num_gates; + std::vector _dags; + uint32_t _ith_dag; + std::uniform_int_distribution _num_pis_dist, _rule_dist; +}; /* random_network_generator */ + +template +class random_network_generator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using rand_engine_t = std::mt19937; + using rules_t = std::vector>; + using params_t = random_network_generator_params_composed; + +private: /* common data members */ + rules_t const _gens; + params_t const _ps; + rand_engine_t _rng; + +public: + explicit random_network_generator( rules_t const& gens, params_t ps = {} ) + : _gens( gens ), _ps( ps ), _rng( static_cast( ps.seed ) ), + _counter( 0u ), _num_components( ps.num_components ), _num_pis( ps.num_pis ), + _rule_dist( 0, _gens.size() - 1u ) + { + prepare_partial_dags(); + _dag_dist = std::uniform_int_distribution( 0, _dags.size() - 1u ); + } + + Ntk generate() + { + std::vector fs; + Ntk ntk; + + /* generate constant */ + fs.emplace_back( ntk.get_constant( false ) ); + + /* generate pis */ + for ( auto i = 0u; i < _num_pis; ++i ) + { + fs.emplace_back( ntk.create_pi() ); + } + + /* generate gates */ + for ( auto i = 0u; i < _num_components; ++i ) + { + concretize( ntk, _dags.at( _dag_dist( _rng ) ), fs ); + // concretize( ntk, _dags.at( 3 ), fs ); // [3] in 4-gate topologies: diamond + } + + /* generate pos */ + ntk.foreach_gate( [&]( auto const& n ) { + if ( ntk.fanout_size( n ) == 0u ) + { + ntk.create_po( ntk.make_signal( n ) ); + } + } ); + + if ( ++_counter >= _ps.num_networks_per_configuration ) + { + _counter = 0; + _num_components += _ps.num_components_increment; + _num_pis += _ps.num_pis_increment; + } + + return ntk; + } + +private: + void concretize( Ntk& ntk, percy::partial_dag& pd, std::vector& fs ) + { + uint32_t num_existing = ntk.size() - 1u; + std::uniform_int_distribution dist( 1u, num_existing ); + // std::uniform_int_distribution dist( 1u, _num_pis ); + + pd.foreach_vertex( [&]( auto const& v, auto i ) { + uint32_t size_before = ntk.num_gates(); + signal g; + + do + { + auto const r = _gens.at( _rule_dist( _rng ) ); + std::vector args; + + for ( auto fi : v ) + { + bool const inv = _rng() & 1; + if ( fi == percy::FANIN_PI ) + { + auto const& a = fs.at( dist( _rng ) ); + args.emplace_back( inv ? !a : a ); + } + else + { + assert( fi >= 1 && num_existing + fi < fs.size() ); + auto const& a = fs.at( num_existing + fi ); + args.emplace_back( inv ? !a : a ); + } + } + + g = r.func( ntk, args ); + } while ( ntk.num_gates() == size_before ); + + fs.emplace_back( g ); + assert( ntk.size() == fs.size() ); + } ); + } + + void prepare_partial_dags() + { + for ( auto num = _ps.min_num_gates_component; num <= _ps.max_num_gates_component; ++num ) + { + prepare_partial_dags( num ); + } + } + + void prepare_partial_dags( uint32_t num_gates ) + { + using namespace percy; + + partial_dag g; + partial_dag_generator gen( num_gates ); + std::set> can_reprs; + pd_iso_checker checker( num_gates ); + + gen.set_callback( [&]( partial_dag_generator* gen ) { + for ( int i = 0; i < gen->nr_vertices(); i++ ) + { + g.set_vertex( i, gen->_js[i], gen->_ks[i] ); + } + const auto can_repr = checker.crepr( g ); + const auto res = can_reprs.insert( can_repr ); + if ( res.second ) + _dags.push_back( g ); + } ); + gen.gen_type( partial_gen_type::GEN_COLEX ); + g.reset( 2, num_gates ); + gen.count_dags(); + } + +private: + uint32_t _counter; + uint32_t _num_components, _num_pis; + std::vector _dags; + std::uniform_int_distribution _rule_dist, _dag_dist; +}; /* random_network_generator */ +#endif + +/*! \brief Generates a random AIG network */ +template +auto random_aig_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( aig_network& aig, std::vector const& vs ) -> aig_network::signal { + assert( vs.size() == 2u ); + return aig.create_and( vs[0], vs[1] ); + }, + 2u } ); + + return gen_t( rules, ps ); +} + +/*! \brief Generates a random XAG network */ +template +auto random_xag_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( xag_network& xag, std::vector const& vs ) -> xag_network::signal { + assert( vs.size() == 2u ); + return xag.create_and( vs[0], vs[1] ); + }, + 2u } ); + rules.emplace_back( rule_t{ []( xag_network& xag, std::vector const& vs ) -> xag_network::signal { + assert( vs.size() == 2u ); + return xag.create_xor( vs[0], vs[1] ); + }, + 2u } ); + + return gen_t( rules, ps ); +} + +/*! \brief Generates a random MIG network */ +template +auto random_mig_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 3u ); + return mig.create_maj( vs[0], vs[1], vs[2] ); + }, + 3u } ); + + return gen_t( rules, ps ); +} + +/*! \brief Generates a random MIG network MAJ-, AND-, and OR-gates */ +template +auto mixed_random_mig_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 3u ); + return mig.create_maj( vs[0], vs[1], vs[2] ); + }, + 3u } ); + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 2u ); + return mig.create_and( vs[0], vs[1] ); + }, + 2u } ); + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 2u ); + return mig.create_or( vs[0], vs[1] ); + }, + 2u } ); + + return gen_t( rules, ps ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/generators/self_dualize.hpp b/third-party/mockturtle/include/mockturtle/generators/self_dualize.hpp new file mode 100644 index 00000000000..837df18a983 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/self_dualize.hpp @@ -0,0 +1,129 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file self_dualize.hpp + \brief Self-dualize a logic network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#include "../algorithms/reconv_cut.hpp" +#include "../networks/aig.hpp" +#include "../views/cut_view.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Generates a self-dual AIG + * + * Generates the self-dualization of a multi-output logic network N. + * The algorithm iterates over the output functions f0, ..., fm of N + * and computes self-dualized function gi for 0 <= i <= m defined by + * the formula + * + * gi(x0, x1, ..., xn) + * = (x0 * fi(x1, ..., xn)) + (!x0 * !fi(!x1, ..., !xn)). + * + */ +inline aig_network self_dualize_aig( aig_network const& src_aig ) +{ + using node = node; + using signal = signal; + + aig_network dest_aig; + std::unordered_map node_to_signal_one; + std::unordered_map node_to_signal_two; + + /* copy inputs */ + node_to_signal_one[0] = dest_aig.get_constant( false ); + node_to_signal_two[0] = dest_aig.get_constant( false ); + src_aig.foreach_pi( [&]( const auto& n ) { + auto const pi = dest_aig.create_pi(); + node_to_signal_one[n] = pi; + node_to_signal_two[n] = !pi; + } ); + + reconvergence_driven_cut_parameters ps; + ps.max_leaves = 99999999u; + reconvergence_driven_cut_statistics st; + detail::reconvergence_driven_cut_impl cut_generator( src_aig, ps, st ); + + src_aig.foreach_po( [&]( const auto& f ) { + auto leaves = cut_generator.run( { src_aig.get_node( f ) } ).first; + std::stable_sort( std::begin( leaves ), std::end( leaves ) ); + + /* check if all leaves are pis */ + for ( const auto& l : leaves ) + { + (void)l; + assert( src_aig.is_pi( l ) ); + } + + cut_view view( src_aig, leaves, f ); + topo_view topo_view( view ); + + /* create cone once */ + topo_view.foreach_gate( [&]( const auto& g ) { + std::vector new_fanins; + topo_view.foreach_fanin( g, [&]( const auto& fi ) { + auto const n = topo_view.get_node( fi ); + new_fanins.emplace_back( topo_view.is_complemented( fi ) ? !node_to_signal_one[n] : node_to_signal_one[n] ); + } ); + + assert( new_fanins.size() == 2u ); + node_to_signal_one[g] = dest_aig.create_and( new_fanins[0u], new_fanins[1u] ); + } ); + + /* create cone once */ + topo_view.foreach_gate( [&]( const auto& g ) { + std::vector new_fanins; + topo_view.foreach_fanin( g, [&]( const auto& fi ) { + auto const n = topo_view.get_node( fi ); + new_fanins.emplace_back( topo_view.is_complemented( fi ) ? !node_to_signal_two[n] : node_to_signal_two[n] ); + } ); + + assert( new_fanins.size() == 2u ); + node_to_signal_two[g] = dest_aig.create_and( new_fanins[0u], new_fanins[1u] ); + } ); + + auto const output_signal_one = topo_view.is_complemented( f ) ? !node_to_signal_one[topo_view.get_node( f )] : node_to_signal_one[topo_view.get_node( f )]; + auto const output_signal_two = topo_view.is_complemented( f ) ? !node_to_signal_two[topo_view.get_node( f )] : node_to_signal_two[topo_view.get_node( f )]; + + auto const new_pi = dest_aig.create_pi(); + auto const output = dest_aig.create_or( dest_aig.create_and( new_pi, output_signal_one ), dest_aig.create_and( !new_pi, !output_signal_two ) ); + dest_aig.create_po( output ); + } ); + + return dest_aig; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/generators/sorting.hpp b/third-party/mockturtle/include/mockturtle/generators/sorting.hpp new file mode 100644 index 00000000000..cf68be94993 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/generators/sorting.hpp @@ -0,0 +1,148 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sorting.hpp + \brief Generate sorting networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle +{ + +/*! \brief Generates sorting network based on bubble sort. + * + * The functor is called for every comparator in the network. The arguments + * to the functor are two integers that define on which lines the comparator + * acts. + * + * \param n Number of elements to sort + * \param compare_fn Functor + */ +template +void bubble_sorting_network( uint32_t n, Fn&& compare_fn ) +{ + if ( n <= 1 ) + { + return; + } + for ( auto c = n - 1; c >= 1; --c ) + { + for ( auto j = 0u; j < c; ++j ) + { + compare_fn( j, j + 1 ); + } + } +} + +/*! \brief Generates sorting network based on insertion sort. + * + * The functor is called for every comparator in the network. The arguments + * to the functor are two integers that define on which lines the comparator + * acts. + * + * \param n Number of elements to sort + * \param compare_fn Functor + */ +template +void insertion_sorting_network( uint32_t n, Fn&& compare_fn ) +{ + if ( n <= 1 ) + { + return; + } + for ( auto c = 1u; c < n; ++c ) + { + for ( int j = c - 1; j >= 0; --j ) + { + compare_fn( j, j + 1 ); + } + } +} + +namespace detail +{ + +template +void batcher_merge( std::vector const& list, Fn&& compare_fn ) +{ + if ( list.size() == 2u ) + { + compare_fn( list[0], list[1] ); + return; + } + + std::vector even, odd; + for ( auto i = 0u; i < list.size(); i += 2 ) + { + even.push_back( list[i] ); + odd.push_back( list[i + 1] ); + } + + batcher_merge( even, compare_fn ); + batcher_merge( odd, compare_fn ); + + for ( auto i = 1u; i < list.size() - 2; i += 2 ) + { + compare_fn( list[i], list[i + 1] ); + } +} + +template +void batcher_sort( uint32_t begin, uint32_t end, Fn&& compare_fn ) +{ + const auto size = end - begin; + if ( size == 2u ) + { + compare_fn( begin, begin + 1 ); + return; + } + + batcher_sort( begin, begin + size / 2, compare_fn ); + batcher_sort( begin + size / 2, end, compare_fn ); + + std::vector list( size ); + std::iota( list.begin(), list.end(), begin ); + batcher_merge( list, compare_fn ); +} + +} // namespace detail + +template +void batcher_sorting_network( uint32_t n, Fn&& compare_fn ) +{ + if ( n < 2 ) + return; + detail::batcher_sort( 0u, n, compare_fn ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/interface.hpp b/third-party/mockturtle/include/mockturtle/interface.hpp new file mode 100644 index 00000000000..ec012fde689 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/interface.hpp @@ -0,0 +1,768 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file interface.hpp + \brief Documentation of network interfaces + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "networks/events.hpp" +#include "traits.hpp" + +namespace mockturtle +{ + +static_assert( false, "file interface.hpp cannot be included, it's only used for documentation" ); + +class network final +{ +public: + /*! \brief Type referring to itself. + * + * The ``base_type`` is the network type itself. It is required, because + * views may extend networks, and this type provides a way to determine the + * underlying network type. + */ + using base_type = network; + + /*! \brief Type representing a node. + * + * A ``node`` is a node in the logic network. It could be a constant, a + * primary input or a logic gate. + */ + struct node + { + }; + + /*! \brief Type representing a signal. + * + * A ``signal`` can be seen as a pointer to a node, or an outgoing edge of + * a node towards its fanout. Depending on the kind of logic network, it + * may carry additional information such as a complement attribute. + */ + struct signal + { + }; + + /*! \brief Type representing the storage. + * + * A ``storage`` is some container that can contain all data necessary to + * store the logic network. It can be constructed outside of the logic network + * and passed as a reference to the constructor. It may be shared among + * several logic networks. A `std::shared_ptr` is a convenient data + * structure to hold a storage in a logic network. + */ + struct storage + { + }; + + /*! \brief Default constructor. + * + * Constructs an empty network. + */ + network(); + + /*! \brief Constructor taking a storage. */ + explicit network( storage s ); + + /*! \brief Default copy assignment operator. + * + * Currently, most network implementations in mockturtle use `std::shared_ptr` + * to hold and share the storage. Thus, the default behavior of copy-assigning + * a network only copies the pointer, but not really duplicating the contents + * in the storage data structure. In other words, it makes a shallow copy + * by default. + */ + network& operator=( const network& other ) = default; + + /*! \brief Explicitly duplicate a network. + * + * Deep copy a network by duplicating the storage. Note that this method + * does not duplicate the network events. + */ + network clone(); + +#pragma region Primary I / O and constants + /*! \brief Gets constant value represented by network. + * + * A constant node is the only node that must be created when initializing + * the network. For this reason, this method has constant access and is not + * called `create_constant`. + * + * \param value Constant value + */ + signal get_constant( bool value ) const; + + /*! \brief Creates a primary input in the network. + * + * Each created primary input is stored in a node and contributes to the size + * of the network. + */ + signal create_pi(); + + /*! \brief Creates a primary output in the network. + * + * A primary output is not stored in terms of a node, and it also does not + * contribute to the size of the network. A primary output is created for a + * signal in the network and it is possible that multiple primary outputs + * point to the same signal. + * + * \param s Signal that drives the created primary output + */ + void create_po( signal const& s ); + + /*! \brief Checks whether a node is a constant node. */ + bool is_constant( node const& n ) const; + + /*! \brief Checks whether a node is a primary input. */ + bool is_pi( node const& n ) const; + + /*! \brief Checks whether a node is a combinational input. + * + * This method should be effectively the same as ``is_pi`` in a + * combinational network. + */ + bool is_ci( node const& n ) const; + + /*! \brief Gets the Boolean value of the constant node. + * + * The method expects that `n` is a constant node. + */ + bool constant_value( node const& n ) const; +#pragma endregion + +#pragma region Create unary functions + /*! \brief Creates signal that computes ``f``. + * + * This method is not required to create a gate in the network. A network + * implementation can also just return ``f``. + * + * \param f Child signal + */ + signal create_buf( signal const& f ); + + /*! \brief Creates a signal that inverts ``f``. + * + * This method is not required to create a gate in the network. If a network + * supports complemented attributes on signals, it can just return the + * complemented signal ``f``. + * + * \param f Child signal + */ + signal create_not( signal const& f ); +#pragma endregion + +#pragma region Create binary functions + /*! \brief Creates a signal that computes the binary AND. */ + signal create_and( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary NAND. */ + signal create_nand( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary OR. */ + signal create_or( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary NOR. */ + signal create_nor( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary less-than. + * + * The signal is true if and only if ``f`` is 0 and ``g`` is 1. + */ + signal create_lt( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary less-than-or-equal. + * + * The signal is true if and only if ``f`` is 0 or ``g`` is 1. + */ + signal create_le( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary greater-than. + * + * The signal is true if and only if ``f`` is 1 and ``g`` is 0. + */ + signal create_gt( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary greater-than-or-equal. + * + * The signal is true if and only if ``f`` is 1 or ``g`` is 0. + */ + signal create_ge( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary XOR. */ + signal create_xor( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary XNOR. */ + signal create_xnor( signal const& f, signal const& g ); +#pragma endregion + +#pragma region Create ternary functions + /*! \brief Creates a signal that computes the majority-of-3. */ + signal create_maj( signal const& f, signal const& g, signal const& h ); + + /*! \brief Creates a signal that computes the if-then-else operation. + * + * \param cond Condition for ITE operator + * \param f_then Then-case for ITE operator + * \param f_else Else-case for ITE operator + */ + signal create_ite( signal const& cond, signal const& f_then, signal const& f_else ); + + /*! \brief Creates a signal that computes the ternary XOR operation. */ + signal create_xor3( signal const& a, signal const& b, signal const& c ); +#pragma endregion + +#pragma region Create nary functions + /*! \brief Creates a signal that computes the n-ary AND. + * + * If `fs` is empty, it returns constant-1. + */ + signal create_nary_and( std::vector const& fs ); + + /*! \brief Creates a signal that computes the n-ary OR. + * + * If `fs` is empty, it returns constant-0. + */ + signal create_nary_or( std::vector const& fs ); + + /*! \brief Creates a signal that computes the n-ary XOR. + * + * If `fs` is empty, it returns constant-0. + */ + signal create_nary_xor( std::vector const& fs ); +#pragma endregion + +#pragma region Create arbitrary functions + /*! \brief Creates node with arbitrary function. + * + * The number of variables in ``function`` must match the number of fanin + * signals in ``fanin``. ``fanin[0]`` will correspond to the + * least-significant variable in ``function``. + * + * \param fanin Fan-in signals + * \param function Truth table for node function + */ + signal create_node( std::vector const& fanin, kitty::dynamic_truth_table const& function ); + + /*! \brief Clones a node from another network of same type. + * + * This method can clone a node from a different network ``other``, which is + * from the same type. The node ``source`` is a node in the source network + * ``other``, but the signals in ``fanin`` refer to signals in the target + * network, which are assumed to be in the same order as in the source + * network. + * + * \param other Other network of same type + * \param source Node in ``other`` + * \param children Fan-in signals from the current network + * \return New signal representing node in current network + */ + signal clone_node( network const& other, node const& source, std::vector const& fanin ); +#pragma endregion + +#pragma region Restructuring + /*! \brief Replaces one node in a network by another signal. + * + * This method causes all nodes that have ``old_node`` as fanin to have + * `new_signal` as fanin instead. In doing so, a possible polarity of + * `new_signal` is taken into account. Afterwards, the fan-out count of + * ``old_node`` is guaranteed to be 0. + * + * It does not update custom values or visited flags of a node. + * + * \param old_node Node to replace + * \param new_signal Signal to replace ``old_node`` with + */ + void substitute_node( node const& old_node, signal const& new_signal ); + + /*! \brief Perform multiple node-signal replacements in a network. + * + * This method replaces all occurrences of a node with a signal for + * all pairs (node, signal) in the substitution list. + * + * \param substitutions A list of (node, signal) replacement pairs + */ + void substitute_nodes( std::list> substitutions ); + + /*! \brief Replaces a child node by a new signal in a node. + * + * If ``n`` has a child pointing to ``old_node``, then it will be replaced by + * ``new_signal``. If the replacement catches a trivial case, e.g., ``n`` + * becomes a constant, then this will be returned as an optional replacement + * candidate by the function. + * + * The function updates the hash table. If no trivial case was found, it + * updates the hash table according to the new structure of ``n``. + * + * \param n Node which may have ``old_node`` as a child + * \param old_node Child to be replaced + * \param new_signal Signel to replace ``old_node`` with + * \return May return new recursive replacement candidate + */ + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ); + + /*! \brief Replaces a output driver by a new signal. + * + * If ``old_node`` is drive to some output, then it will be replaced by + * ``new_signal``. + * + * \param old_node Driver to be replaced + * \param new_signal Signal replace ``old_node`` with + */ + void replace_in_outputs( node const& old_node, signal const& new_signal ); + + /*! \brief Removes a node (and potentially its fanins) from the hash table. + * + * The node will be marked dead. This status can be checked with + * ``is_dead``. Taking out a node does not change the indexes of + * other nodes. The node will be removed from the hash table. + * The reference counters of all fanin will be decremented and + * ``take_out_node`` will be recursively invoked on all fanins + * if their fanout count reach 0. + * + * \param n Node to be removed + */ + void take_out_node( node const& n ); + + /*! \brief Check if a node is dead. + * + * A dead node is no longer visited in the ``foreach_node`` and + * ``foreach_gate`` methods. It still contributes to the overall + * ``size`` of the network, but ``num_gates`` does not take dead + * nodes into account. + * + * \param n Node to check + * \return Whether ``n`` is dead + */ + bool is_dead( node const& n ) const; +#pragma endregion + +#pragma region Structural properties + /*! \brief Checks whether the network is combinational. */ + bool is_combinational() const; + + /*! \brief Returns the number of nodes (incl. constants and PIs and dead nodes). */ + uint32_t size() const; + + /*! \brief Returns the number of primary inputs. */ + uint32_t num_pis() const; + + /*! \brief Returns the number of primary outputs. */ + uint32_t num_pos() const; + + /*! \brief Returns the number of combinational inputs. + * + * This method should be effectively the same as `num_pis`` in a + * combinational network. + */ + uint32_t num_cis() const; + + /*! \brief Returns the number of combinational outputs. + * + * This method should be effectively the same as `num_pos`` in a + * combinational network. + */ + uint32_t num_cos() const; + + /*! \brief Returns the number of gates (without dead nodes) */ + uint32_t num_gates() const; + + /*! \brief Returns the fanin size of a node. */ + uint32_t fanin_size( node const& n ) const; + + /*! \brief Returns the fanout size of a node. */ + uint32_t fanout_size( node const& n ) const; + + /*! \brief Increments fanout size and returns old value. + * + * This is useful for ref-counting based algorithm. The user of this function + * should make sure to bring the value back to a consistent state. + */ + uint32_t incr_fanout_size( node const& n ) const; + + /*! \brief Decrements fanout size and returns new value. + * + * This is useful for ref-counting based algorithm. The user of this function + * should make sure to bring the value back to a consistent state. + */ + uint32_t decr_fanout_size( node const& n ) const; + + /*! \brief Returns the length of the critical path. + * + * For efficiency reasons, this interface is often not provided in the + * network implementations, but has to be extended by wrapping with `depth_view`. + */ + uint32_t depth() const; + + /*! \brief Returns the level of a node. + * + * For efficiency reasons, this interface is often not provided in the + * network implementations, but has to be extended by wrapping with `depth_view`. + */ + uint32_t level( node const& n ) const; + + /*! \brief Returns true if node is a 2-input AND gate. */ + bool is_and( node const& n ) const; + + /*! \brief Returns true if node is a 2-input OR gate. */ + bool is_or( node const& n ) const; + + /*! \brief Returns true if node is a 2-input XOR gate. */ + bool is_xor( node const& n ) const; + + /*! \brief Returns true if node is a majority-of-3 gate. */ + bool is_maj( node const& n ) const; + + /*! \brief Returns true if node is a if-then-else gate. */ + bool is_ite( node const& n ) const; + + /*! \brief Returns true if node is a 3-input XOR gate. */ + bool is_xor3( node const& n ) const; + + /*! \brief Returns true if node is a primitive n-ary AND gate. */ + bool is_nary_and( node const& n ) const; + + /*! \brief Returns true if node is a primitive n-ary OR gate. */ + bool is_nary_or( node const& n ) const; + + /*! \brief Returns true if node is a primitive n-ary XOR gate. */ + bool is_nary_xor( node const& n ) const; + + /*! \brief Returns true if node is a general function node. */ + bool is_function( node const& n ) const; +#pragma endregion + +#pragma region Functional properties + /*! \brief Returns the gate function of a node. + * + * Note that this function returns the gate function represented by a node + * in terms of the *intended* gate. For example, in an AIG, all gate + * functions are AND, complemented edges are not taken into account. Also, + * in an MIG, all gate functions are MAJ, independently of complemented edges + * and possible constant inputs. + * + * In order to retrieve a function with respect to complemented edges one can + * use the `compute` function with a truth table as simulation value. + */ + kitty::dynamic_truth_table node_function( node const& n ) const; +#pragma endregion + +#pragma region Nodes and signals + /*! \brief Get the node a signal is pointing to. */ + node get_node( signal const& f ) const; + + /*! \brief Create a signal from a node (without edge attributes). */ + signal make_signal( node const& n ) const; + + /*! \brief Check whether a signal is complemented. + * + * This method may also be provided by network implementations that do not + * have complemented edges. In this case, the method simply returns + * ``false`` for each node. + */ + bool is_complemented( signal const& f ) const; + + /*! \brief Returns the index of a node. + * + * The index of a node must be a unique for each node and must be between 0 + * (inclusive) and the size of a network (exclusive, value returned by + * ``size()``). + */ + uint32_t node_to_index( node const& n ) const; + + /*! \brief Returns the node for an index. + * + * This is the inverse function to ``node_to_index``. + * + * \param index A value between 0 (inclusive) and the size of the network + * (exclusive) + */ + node index_to_node( uint32_t index ) const; + + /*! \brief Returns the primary input node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * primary inputs (exclusive). + */ + node pi_at( uint32_t index ) const; + + /*! \brief Returns the primary output signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * primary outputs (exclusive). + */ + signal po_at( uint32_t index ) const; + + /*! \brief Returns the combinational input node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational inputs (exclusive). + */ + node ci_at( uint32_t index ) const; + + /*! \brief Returns the combinational output signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational outputs (exclusive). + */ + signal co_at( uint32_t index ) const; + + /*! \brief Returns the index of a primary input node. + * + * \param n A primary input node. + * \return A value between 0 and num_pis()-1. + */ + uint32_t pi_index( node const& n ) const; + + /*! \brief Returns the index of a primary output signal. + * + * \param n A primary output signal. + * \return A value between 0 and num_pos()-1. + */ + uint32_t po_index( signal const& n ) const; + + /*! \brief Returns the index of a combinational input node. + * + * \param n A combinational input node. + * \return A value between 0 and num_cis()-1. + */ + uint32_t ci_index( node const& n ) const; + + /*! \brief Returns the index of a combinational output signal. + * + * \param n A combinational output signal. + * \return A value between 0 and num_cos()-1. + */ + uint32_t co_index( signal const& n ) const; +#pragma endregion + +#pragma region Node and signal iterators + /*! \brief Calls ``fn`` on every node in network. + * + * The order of nodes depends on the implementation and must not guarantee + * topological order. The parameter ``fn`` is any callable that must have + * one of the following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_node( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every gate node in the network. + * + * Calls each node that is not constant and not a combinational input. The + * parameter ``fn`` is any callable that must have one of the following four + * signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_gate( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every primary input node in the network. + * + * The order is in the same order as primary inputs have been created with + * ``create_pi``. The parameter ``fn`` is any callable that must have one of + * the following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_pi( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every primary output signal in the network. + * + * The order is in the same order as primary outputs have been created with + * ``create_po``. The function is called on the signal that is driving the + * output and may occur more than once in the iteration, if it drives more + * than one output. The parameter ``fn`` is any callable that must have one + * of the following four signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_po( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every combinational input node in the network. + * + * This method should be effectively the same as ``foreach_pi`` in a + * combinational network. + */ + template + void foreach_ci( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every combinational output signal in the network. + * + * This method should be effectively the same as ``foreach_po`` in a + * combinational network. + */ + template + void foreach_co( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every fanin of a node. + * + * The order of the fanins is in the same order that was used to create the + * node. The parameter ``fn`` is any callable that must have one of the + * following four signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_fanin( node const& n, Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every fanout of a node. + * + * The method gives no guarantee on the order of the fanout. The parameter + * ``fn`` is any callable that must have one of the following signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + * + * For efficiency reasons, this interface is often not provided in the + * network implementations, but has to be extended by wrapping with `fanout_view`. + */ + template + void foreach_fanout( node const& n, Fn&& fn ) const; +#pragma endregion + +#pragma region Simulate values + /*! \brief Simulates arbitrary value on a node. + * + * This is a generic simulation method that can be implemented multiple times + * for a network interface for different types. One only needs to change the + * implementation and change the value for the type parameter ``T``, which + * indicates the element type of the iterators. + * + * Examples for simulation types are ``bool``, + * ``kitty::dynamic_truth_table``, bit masks, or BDDs. + * + * The ``begin`` and ``end`` iterator point to values which are assumed to be + * assigned to the fanin of the node. Consequently, the distance from + * ``begin`` to ``end`` must equal the fanin size of the node. + * + * \param n Node to simulate (used to retrieve the node function) + * \param begin Begin iterator to simulation values of fanin + * \param end End iterator to simulation values of fanin + * \return Returns computed simulation value of type ``T`` + */ + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const; +#pragma endregion + +#pragma region Custom node values + /*! \brief Reset all values to 0. */ + void clear_values() const; + + /*! \brief Returns value of a node. */ + uint32_t value( node const& n ) const; + + /*! \brief Sets value of a node. */ + void set_value( node const& n, uint32_t value ) const; + + /*! \brief Increments value of a node and returns *previous* value. */ + uint32_t incr_value( node const& n ) const; + + /*! \brief Decrements value of a node and returns *new* value. */ + uint32_t decr_value( node const& n ) const; +#pragma endregion + +#pragma region Visited flags + /*! \brief Reset all visited values to 0. */ + void clear_visited() const; + + /*! \brief Returns the visited value of a node. */ + uint32_t visited( node const& n ) const; + + /*! \brief Sets the visited value of a node. */ + void set_visited( node const& n, uint32_t v ) const; + + /*! \brief Returns the current traversal id. */ + uint32_t trav_id() const; + + /*! \brief Increment the current traversal id. */ + void incr_trav_id() const; +#pragma endregion + +#pragma region General methods + /*! \brief Returns network events object. + * + * Clients can register callbacks for network events to this object. Events + * include adding nodes, modifying nodes, and deleting nodes. + */ + network_events& events() const; +#pragma endregion +}; + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/io/aiger_reader.hpp b/third-party/mockturtle/include/mockturtle/io/aiger_reader.hpp new file mode 100644 index 00000000000..3a633f15f23 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/aiger_reader.hpp @@ -0,0 +1,233 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aiger_reader.hpp + \brief Lorina reader for AIGER files + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/sequential.hpp" +#include "../traits.hpp" +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for Aiger files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `get_constant` + * - `create_not` + * - `create_and` + * + * **Optional network functions to support sequential networks:** + * - `create_ri` + * - `create_ro` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig; + lorina::read_aiger( "file.aig", aiger_reader( aig ) ); + + mig_network mig; + lorina::read_aiger( "file.aig", aiger_reader( mig ) ); + \endverbatim + */ +template +class aiger_reader : public lorina::aiger_reader +{ +public: + explicit aiger_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and function" ); + } + + ~aiger_reader() + { + uint32_t output_id{ 0 }; + for ( auto out : outputs ) + { + auto const lit = std::get<0>( out ); + auto signal = signals[lit >> 1]; + if ( lit & 1 ) + { + signal = _ntk.create_not( signal ); + } + if constexpr ( has_set_output_name_v ) + { + if ( !std::get<1>( out ).empty() ) + { + _ntk.set_output_name( output_id++, std::get<1>( out ) ); + } + } + _ntk.create_po( signal ); + } + + if constexpr ( has_create_ri_v ) + { + for ( auto i = 0u; i < latches.size(); ++i ) + { + auto& latch = latches[i]; + auto const lit = std::get<0>( latch ); + auto const reset = std::get<1>( latch ); + + auto signal = signals[lit >> 1]; + if ( lit & 1 ) + { + signal = _ntk.create_not( signal ); + } + + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signal, std::get<2>( latch ) + "_next" ); + } + + _ntk.create_ri( signal ); + register_t reg; + reg.init = reset; + _ntk.set_register( i, reg ); + } + } + } + + void on_header( uint64_t, uint64_t num_inputs, uint64_t num_latches, uint64_t, uint64_t ) const override + { + (void)num_latches; + if constexpr ( !has_create_ri_v || !has_create_ro_v ) + { + assert( num_latches == 0 && "network type does not support the creation of latches" ); + } + + _num_inputs = static_cast( num_inputs ); + + /* constant */ + signals.push_back( _ntk.get_constant( false ) ); + + /* create primary inputs (pi) */ + for ( auto i = 0u; i < num_inputs; ++i ) + { + signals.push_back( _ntk.create_pi() ); + } + + if constexpr ( has_create_ro_v ) + { + /* create latch outputs (ro) */ + for ( auto i = 0u; i < num_latches; ++i ) + { + signals.push_back( _ntk.create_ro() ); + } + } + } + + void on_input_name( unsigned index, const std::string& name ) const override + { + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signals[1 + index], name ); + } + } + + void on_output_name( unsigned index, const std::string& name ) const override + { + std::get<1>( outputs[index] ) = name; + } + + void on_latch_name( unsigned index, const std::string& name ) const override + { + if constexpr ( has_create_ri_v && has_create_ro_v ) + { + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signals[1 + _num_inputs + index], name ); + } + std::get<2>( latches[index] ) = name; + } + } + + void on_and( unsigned index, unsigned left_lit, unsigned right_lit ) const override + { + (void)index; + assert( signals.size() == index ); + + auto left = signals[left_lit >> 1]; + if ( left_lit & 1 ) + { + left = _ntk.create_not( left ); + } + + auto right = signals[right_lit >> 1]; + if ( right_lit & 1 ) + { + right = _ntk.create_not( right ); + } + + signals.push_back( _ntk.create_and( left, right ) ); + } + + void on_latch( unsigned index, unsigned next, latch_init_value reset ) const override + { + if constexpr ( has_create_ri_v && has_create_ro_v ) + { + (void)index; + int8_t r = reset == latch_init_value::NONDETERMINISTIC ? -1 : ( reset == latch_init_value::ONE ? 1 : 0 ); + latches.push_back( std::make_tuple( next, r, "" ) ); + } + } + + void on_output( unsigned index, unsigned lit ) const override + { + (void)index; + assert( index == outputs.size() ); + outputs.emplace_back( lit, "" ); + } + +private: + Ntk& _ntk; + + mutable uint32_t _num_inputs{ 0 }; + mutable std::vector> outputs; + mutable std::vector signals; + mutable std::vector> latches; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/bench_reader.hpp b/third-party/mockturtle/include/mockturtle/io/bench_reader.hpp new file mode 100644 index 00000000000..a3cf95a73f6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/bench_reader.hpp @@ -0,0 +1,194 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bench_reader.hpp + \brief Lorina reader for BENCH files + + \author Heinz Riener + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for BENCH files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `get_constant` + * - `create_node` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + klut_network klut; + lorina::read_bench( "file.bench", bench_reader( klut ) ); + \endverbatim + */ +template +class bench_reader : public lorina::bench_reader +{ +public: + explicit bench_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_create_node_v, "Ntk does not implement the create_node function" ); + signals["gnd"] = _ntk.get_constant( false ); + signals["vdd"] = _ntk.get_constant( true ); + } + + ~bench_reader() + { + for ( auto const& o : outputs ) + { + _ntk.create_po( signals[o] ); + } + } + + void on_input( const std::string& name ) const override + { + signals[name] = _ntk.create_pi(); + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signals[name], name ); + } + } + + void on_output( const std::string& name ) const override + { + if constexpr ( has_set_output_name_v ) + { + _ntk.set_output_name( outputs.size(), name ); + } + outputs.emplace_back( name ); + } + + void on_assign( const std::string& input, const std::string& output ) const override + { + signals[output] = signals.at( input ); + } + + void on_gate( const std::vector& inputs, const std::string& output, const std::string& type ) const override + { + if ( type.size() > 2 && std::string_view( type ).substr( 0, 2 ) == "0x" && inputs.size() <= 6u ) + { + /* modern-style gate definition */ + kitty::dynamic_truth_table tt( static_cast( inputs.size() ) ); + kitty::create_from_hex_string( tt, type.substr( 2 ) ); + + std::vector> input_signals; + for ( const auto& i : inputs ) + input_signals.push_back( signals[i] ); + + signals[output] = _ntk.create_node( input_signals, tt ); + } + else + { + /* old-style gate definition */ + std::vector> input_signals; + for ( const auto& i : inputs ) + input_signals.push_back( signals[i] ); + + kitty::dynamic_truth_table tt( static_cast( inputs.size() ) ); + + std::vector vs( inputs.size(), tt ); + for ( auto i = 0u; i < inputs.size(); ++i ) + kitty::create_nth_var( vs[i], i ); + + if ( type == "NOT" ) + { + assert( inputs.size() == 1u ); + tt = ~vs.at( 0u ); + } + else if ( type == "BUFF" ) + { + assert( inputs.size() == 1u ); + tt = vs.at( 0u ); + } + else if ( type == "AND" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt &= vs.at( i ); + } + else if ( type == "NAND" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt &= vs.at( i ); + tt = ~tt; + } + else if ( type == "OR" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt |= vs.at( i ); + } + else if ( type == "NOR" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt |= vs.at( i ); + tt = ~tt; + } + else + { + assert( false && "unsupported gate type" ); + } + signals[output] = _ntk.create_node( input_signals, tt ); + } + } + +private: + Ntk& _ntk; + + mutable std::map> signals; + mutable std::vector outputs; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/blif_reader.hpp b/third-party/mockturtle/include/mockturtle/io/blif_reader.hpp new file mode 100644 index 00000000000..7aed715450c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/blif_reader.hpp @@ -0,0 +1,335 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file blif_reader.hpp + \brief Lorina reader for BLIF files + + \author Andrea Costamagna + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/cover.hpp" +#include "../networks/sequential.hpp" +#include "../traits.hpp" + +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for BLIF files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_node` or `create_cover_node` + * - `get_constant` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + klut_network klut; + lorina::read_blif( "file.blif", blif_reader( klut ) ); + + cover_network cover; + lorina::read_blif( "file.blif", blif_reader( cover ) ); + \endverbatim + */ +template +class blif_reader : public lorina::blif_reader +{ +public: + explicit blif_reader( Ntk& ntk ) + : ntk_( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_node_v || has_create_cover_node_v, "Ntk does not implement the create_node function or the create_cover_node function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + } + + ~blif_reader() + { + for ( auto const& o : outputs ) + { + ntk_.create_po( signals[o] ); + } + + if constexpr ( has_create_ri_v ) + { + for ( auto const& latch : latches ) + { + auto signal = signals[latch]; + ntk_.create_ri( signal ); + } + } + } + + virtual void on_model( const std::string& model_name ) const override + { + if constexpr ( has_set_network_name_v ) + { + ntk_.set_network_name( model_name ); + } + + (void)model_name; + } + + virtual void on_input( const std::string& name ) const override + { + signals[name] = ntk_.create_pi(); + if constexpr ( has_set_name_v ) + { + ntk_.set_name( signals[name], name ); + } + } + + virtual void on_output( const std::string& name ) const override + { + if constexpr ( has_set_output_name_v ) + { + ntk_.set_output_name( outputs.size(), name ); + } + outputs.emplace_back( name ); + } + + virtual void on_latch( const std::string& input, const std::string& output, const std::optional& l_type, const std::optional& control, const std::optional& reset ) const override + { + if constexpr ( has_create_ro_v ) + { + std::string type = "re"; + if ( l_type ) + { + switch ( *l_type ) + { + case latch_type::FALLING: + { + type = "fe"; + } + break; + case latch_type::RISING: + { + type = "re"; + } + break; + case latch_type::ACTIVE_HIGH: + { + type = "ah"; + } + break; + case latch_type::ACTIVE_LOW: + { + type = "al"; + } + break; + case latch_type::ASYNC: + { + type = "as"; + } + break; + default: + { + type = ""; + } + break; + } + } + + uint32_t r = 3; + if ( reset ) + { + switch ( *reset ) + { + case latch_init_value::NONDETERMINISTIC: + { + r = 2; + } + break; + case latch_init_value::ONE: + { + r = 1; + } + break; + case latch_init_value::ZERO: + { + r = 0; + } + break; + default: + break; + } + } + + std::string ctrl = control.has_value() ? control.value() : "clock"; + + register_t reg; + reg.control = ctrl; + reg.init = r; + reg.type = type; + + signals[output] = ntk_.create_ro(); + ntk_.set_register( latches.size(), reg ); + + if constexpr ( has_set_name_v && has_set_output_name_v ) + { + ntk_.set_name( signals[output], output ); + ntk_.set_output_name( outputs.size() + latches.size(), input ); + } + + latches.emplace_back( input ); + } + } + + virtual void on_gate( const std::vector& inputs, const std::string& output, const output_cover_t& cover ) const override + { + if ( inputs.size() == 0u ) + { + if ( cover.size() == 0u ) + { + signals[output] = ntk_.get_constant( false ); + return; + } + + assert( cover.size() == 1u ); + assert( cover.at( 0u ).first.size() == 0u ); + assert( cover.at( 0u ).second.size() == 1u ); + + auto const assigned_value = cover.at( 0u ).second.at( 0u ); + auto const const_value = ntk_.get_constant( assigned_value == '1' ? true : false ); + signals[output] = const_value; + return; + } + + assert( cover.size() > 0u ); + assert( cover.at( 0u ).second.size() == 1 ); + auto const first_output_value = cover.at( 0u ).second.at( 0u ); + + if constexpr ( std::is_same::value ) + { + std::vector cubes; + bool is_sop = ( first_output_value == '1' ); + for ( const auto& c : cover ) + { + assert( c.second.size() == 1 ); + + auto const output = c.second[0u]; + assert( output == '0' || output == '1' ); + assert( output == first_output_value ); + (void)first_output_value; + + cubes.emplace_back( kitty::cube( c.first ) ); + } + + std::vector> input_signals; + for ( const auto& i : inputs ) + { + assert( signals.find( i ) != signals.end() ); + input_signals.push_back( signals.at( i ) ); + } + + if ( cubes.size() != 0 ) + { + signals[output] = ntk_.create_cover_node( input_signals, std::make_pair( cubes, is_sop ) ); + } + } + else + { + + std::vector minterms; + std::vector maxterms; + + for ( const auto& c : cover ) + { + assert( c.second.size() == 1 ); + + auto const output = c.second[0u]; + assert( output == '0' || output == '1' ); + assert( output == first_output_value ); + (void)first_output_value; + + if ( output == '1' ) + { + minterms.emplace_back( kitty::cube( c.first ) ); + } + else if ( output == '0' ) + { + maxterms.emplace_back( ~kitty::cube( c.first ) ); + } + } + + assert( minterms.size() == 0u || maxterms.size() == 0u ); + + kitty::dynamic_truth_table tt( int( inputs.size() ) ); + if ( minterms.size() != 0 ) + { + kitty::create_from_cubes( tt, minterms, false ); + } + else if ( maxterms.size() != 0 ) + { + kitty::create_from_clauses( tt, maxterms, false ); + } + std::vector> input_signals; + for ( const auto& i : inputs ) + { + assert( signals.find( i ) != signals.end() ); + input_signals.push_back( signals.at( i ) ); + } + signals[output] = ntk_.create_node( input_signals, tt ); + } + } + + virtual void on_end() const override {} + + virtual void on_comment( const std::string& comment ) const override + { + (void)comment; + } + +private: + Ntk& ntk_; + + mutable std::map> signals; + mutable std::vector outputs; + mutable std::vector latches; +}; /* blif_reader */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/bristol_reader.hpp b/third-party/mockturtle/include/mockturtle/io/bristol_reader.hpp new file mode 100644 index 00000000000..49e0610e651 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/bristol_reader.hpp @@ -0,0 +1,149 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bristol_reader.hpp + \brief Lorina reader for Bristol files + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for Bristol files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_and` + * - `create_xor` + * - `create_not` + * - `get_constant` + * + \verbatim embed:rst + Example + .. code-block:: c++ + xag_network xag; + lorina::read_bristol( "file.txt", bristol_reader( xag ) ); + \endverbatim + */ +template +class bristol_reader : public lorina::bristol_reader +{ +public: + explicit bristol_reader( Ntk& ntk ) + : ntk_( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and function" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + } + + ~bristol_reader() + { + } + + virtual void on_header( uint32_t num_gates, uint32_t num_wires, uint32_t num_inputs, std::vector const& num_wires_per_input, uint32_t num_outputs, std::vector const& num_wires_per_output ) const override + { + (void)num_inputs; + (void)num_outputs; + + const auto num_pis = std::accumulate( num_wires_per_input.begin(), num_wires_per_input.end(), 0u ); + num_pos_ = std::accumulate( num_wires_per_output.begin(), num_wires_per_output.end(), 0u ); + num_gates_ = num_gates; + gate_ctr_ = 0u; + signal_.resize( num_wires ); + + for ( auto i = 0u; i < num_pis; ++i ) + { + signal_[i] = ntk_.create_pi(); + } + } + + virtual void on_gate( std::vector const& in, uint32_t out, std::string const& gate ) const override + { + if ( gate == "XOR" ) + { + signal_[out] = ntk_.create_xor( signal_[in[0]], signal_[in[1]] ); + } + else if ( gate == "AND" ) + { + signal_[out] = ntk_.create_and( signal_[in[0]], signal_[in[1]] ); + } + else if ( gate == "INV" ) + { + signal_[out] = ntk_.create_not( signal_[in[0]] ); + } + else if ( gate == "EQW" ) + { + signal_[out] = signal_[in[0]]; + } + else + { + fmt::print( "[e] unknown {} gate {} with {} inputs!", gate, out, in.size() ); + std::abort(); + } + + ++gate_ctr_; + + if ( gate_ctr_ == num_gates_ ) + { + for ( auto i = signal_.size() - num_pos_; i < signal_.size(); ++i ) + { + ntk_.create_po( signal_[i] ); + } + } + else if ( gate_ctr_ > num_gates_ ) + { + fmt::print( "[w] adding dangling {} gate with inputs {} and output {}\n", gate, fmt::join( in, ", " ), out ); + } + } + +private: + Ntk& ntk_; + + mutable uint32_t num_pos_; + mutable uint32_t num_gates_; + mutable uint32_t gate_ctr_; + mutable std::vector> signal_; +}; /* bristol_reader */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/dimacs_reader.hpp b/third-party/mockturtle/include/mockturtle/io/dimacs_reader.hpp new file mode 100644 index 00000000000..13784725cb6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/dimacs_reader.hpp @@ -0,0 +1,113 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dimacs_reader.hpp + \brief Lorina reader for DIMACS files + + \author Bruno Schmitt +*/ + +#pragma once + +#include "../traits.hpp" +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for DIMACS files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_not` + * - `create_nary_and` + * - `create_nary_or` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + xag_network xag; + lorina::read_dimacs( "file.cnf", dimacs_reader( xag ) ); + \endverbatim + */ +template +class dimacs_reader : public lorina::dimacs_reader +{ +public: + explicit dimacs_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_nary_and_v, "Ntk does not implement the create_nary_and function" ); + static_assert( has_create_nary_or_v, "Ntk does not implement the create_nary_or function" ); + } + + void on_number_of_variables( uint64_t number_of_inputs ) const override + { + _pis.resize( number_of_inputs ); + std::generate( _pis.begin(), _pis.end(), [this]() { return _ntk.create_pi(); } ); + } + + void on_clause( const std::vector& clause ) const override + { + std::vector> literals; + + for ( int lit : clause ) + { + uint32_t var = std::abs( lit ) - 1; + if ( lit < 0 ) + { + literals.push_back( !_pis.at( var ) ); + } + else + { + literals.push_back( _pis.at( var ) ); + } + } + + const auto sum = _ntk.create_nary_or( literals ); + _sums.push_back( sum ); + } + + void on_end() const override + { + const auto output = _ntk.create_nary_and( _sums ); + _ntk.create_po( output ); + } + +private: + Ntk& _ntk; + mutable std::vector> _pis; + mutable std::vector> _sums; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/genlib_reader.hpp b/third-party/mockturtle/include/mockturtle/io/genlib_reader.hpp new file mode 100644 index 00000000000..1a3faaea142 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/genlib_reader.hpp @@ -0,0 +1,169 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file genlib_reader.hpp + \brief Reader visitor for GENLIB files + + \author Alessandro Tempia Calvino + \author Heinz Riener +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +enum class phase_type : uint8_t +{ + INV = 0, + NONINV = 1, + UNKNOWN = 2, +}; + +struct pin +{ + std::string name; + phase_type phase; + double input_load; + double max_load; + double rise_block_delay; + double rise_fanout_delay; + double fall_block_delay; + double fall_fanout_delay; +}; /* pin */ + +struct gate +{ + unsigned int id; + std::string name; + std::string expression; + uint32_t num_vars; + kitty::dynamic_truth_table function; + double area; + std::vector pins; + std::string output_name; +}; /* gate */ + +/*! \brief lorina callbacks for GENLIB files. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + \endverbatim + */ +class genlib_reader : public lorina::genlib_reader +{ +public: + explicit genlib_reader( std::vector& gates ) + : gates( gates ) + {} + + virtual void on_gate( std::string const& name, std::string const& expression, double area, std::vector const& ps, std::string const& output_pin ) const override + { + std::vector pp; + std::vector pin_names; + + if ( ps.size() == 1 && ps[0].name == "*" ) + { + /* pins not defined, use names and appearance order of the expression */ + std::vector tokens; + std::string const delimitators{ " ()!\'*&+|^\t\r\n" }; + std::size_t prev = 0, pos; + while ( ( pos = expression.find_first_of( delimitators, prev ) ) != std::string::npos ) + { + if ( pos > prev ) + { + tokens.emplace_back( expression.substr( prev, pos - prev ) ); + } + prev = pos + 1; + } + if ( prev < expression.length() ) + { + tokens.emplace_back( expression.substr( prev, std::string::npos ) ); + } + for ( auto const& pin_name : tokens ) + { + if ( std::find( pin_names.begin(), pin_names.end(), pin_name ) == pin_names.end() ) + { + pp.emplace_back( pin{ pin_name, + phase_type( static_cast( ps[0].phase ) ), + ps[0].input_load, ps[0].max_load, + ps[0].rise_block_delay, ps[0].rise_fanout_delay, ps[0].fall_block_delay, ps[0].fall_fanout_delay } ); + pin_names.push_back( pin_name ); + } + } + } + else + { + for ( const auto& p : ps ) + { + pp.emplace_back( pin{ p.name, + phase_type( static_cast( p.phase ) ), + p.input_load, p.max_load, + p.rise_block_delay, p.rise_fanout_delay, p.fall_block_delay, p.fall_fanout_delay } ); + pin_names.push_back( p.name ); + } + } + + /* replace possible CONST0 or CONST1 by 0 and 1 */ + std::string formula( expression ); + std::size_t found = formula.find( "CONST" ); + if ( found != std::string::npos ) + { + formula.erase( found, found + 5 ); + } + + uint32_t num_vars = pin_names.size(); + + kitty::dynamic_truth_table tt{ num_vars }; + + if ( !kitty::create_from_formula( tt, formula, pin_names ) ) + { + /* formula error, skip gate */ + return; + } + + gates.emplace_back( gate{ static_cast( gates.size() ), name, + expression, num_vars, tt, area, pp, output_pin } ); + } + +protected: + std::vector& gates; +}; /* genlib_reader */ + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/io/pla_reader.hpp b/third-party/mockturtle/include/mockturtle/io/pla_reader.hpp new file mode 100644 index 00000000000..558ed44e27e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/pla_reader.hpp @@ -0,0 +1,154 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file pla_reader.hpp + \brief Lorina reader for PLA files + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../traits.hpp" +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for PLA files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_not` + * - `create_nary_and` + * - `create_nary_or` + * - `create_nary_xor` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig; + lorina::read_pla( "file.pla", pla_reader( aig ) ); + + mig_network mig; + lorina::read_pla( "file.pla", pla_reader( mig ) ); + \endverbatim + */ +template +class pla_reader : public lorina::pla_reader +{ +public: + explicit pla_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_nary_and_v, "Ntk does not implement the create_nary_and function" ); + static_assert( has_create_nary_or_v, "Ntk does not implement the create_nary_or function" ); + static_assert( has_create_nary_xor_v, "Ntk does not implement the create_nary_xor function" ); + } + + void on_number_of_inputs( uint64_t number_of_inputs ) const override + { + _pis.resize( number_of_inputs ); + std::generate( _pis.begin(), _pis.end(), [this]() { return _ntk.create_pi(); } ); + } + + void on_number_of_outputs( uint64_t number_of_outputs ) const override + { + _products.resize( number_of_outputs ); + } + + bool on_keyword( const std::string& keyword, const std::string& value ) const override + { + if ( keyword == "type" && value == "esop" ) + { + _is_xor = true; + return true; + } + return false; + } + + void on_end() const override + { + for ( auto const& v : _products ) + { + _ntk.create_po( _is_xor ? _ntk.create_nary_xor( v ) : _ntk.create_nary_or( v ) ); + } + } + + void on_term( const std::string& term, const std::string& out ) const override + { + std::vector> literals; + for ( auto i = 0u; i < term.size(); ++i ) + { + switch ( term[i] ) + { + default: + std::cerr << "[w] unknown character '" << term[i] << "' in PLA input term, treat as don't care\n"; + case '-': + break; + + case '0': + literals.push_back( _ntk.create_not( _pis[i] ) ); + break; + case '1': + literals.push_back( _pis[i] ); + break; + } + } + + const auto product = _ntk.create_nary_and( literals ); + for ( auto i = 0u; i < out.size(); ++i ) + { + switch ( out[i] ) + { + default: + std::cerr << "[w] unknown character '" << out[i] << "' in PLA output term, treat is 0\n"; + case '0': + break; + + case '1': + _products[i].push_back( product ); + break; + } + } + } + +private: + Ntk& _ntk; + mutable std::vector> _pis; + mutable std::vector>> _products; + mutable bool _is_xor{ false }; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/serialize.hpp b/third-party/mockturtle/include/mockturtle/io/serialize.hpp new file mode 100644 index 00000000000..b02a1a5b724 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/serialize.hpp @@ -0,0 +1,377 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file serialize.hpp + \brief Serialize network into a file + + \author Bruno Schmitt + \author Heinz Riener + \author Siang-Yun (Sonia) Lee + + This file implements functions to serialize a (combinational) + `aig_network` into a file. The serializer should be used for + debugging-purpose only. It allows to store the current state of the + network (including dangling and dead nodes), but does not guarantee + platform-independence (use, e.g., `write_verilog` instead). +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +struct serializer +{ +public: + using node_type = typename aig_network::storage::element_type::node_type; + using pointer_type = typename node_type::pointer_type; + +public: + bool operator()( phmap::BinaryOutputArchive& os, uint64_t const& data ) const + { + return os.dump( (char*)&data, sizeof( uint64_t ) ); + } + + bool operator()( phmap::BinaryInputArchive& ar_input, uint64_t* data ) const + { + return ar_input.load( (char*)data, sizeof( uint64_t ) ); + } + + template + bool operator()( phmap::BinaryOutputArchive& os, node_pointer const& ptr ) const + { + return os.dump( (char*)&ptr.data, sizeof( ptr.data ) ); + } + + template + bool operator()( phmap::BinaryInputArchive& ar_input, node_pointer* ptr ) const + { + return ar_input.load( (char*)&ptr->data, sizeof( ptr->data ) ); + } + + bool operator()( phmap::BinaryOutputArchive& os, cauint64_t const& data ) const + { + return os.dump( (char*)&data.n, sizeof( data.n ) ); + } + + bool operator()( phmap::BinaryInputArchive& ar_input, cauint64_t* data ) const + { + return ar_input.load( (char*)&data->n, sizeof( data->n ) ); + } + + template + bool operator()( phmap::BinaryOutputArchive& os, regular_node const& n ) const + { + uint64_t size = n.children.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + + for ( const auto& c : n.children ) + { + bool result = this->operator()( os, c ); + if ( !result ) + { + return false; + } + } + + size = n.data.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& d : n.data ) + { + bool result = this->operator()( os, d ); + if ( !result ) + { + return false; + } + } + + return true; + } + + template + bool operator()( phmap::BinaryInputArchive& ar_input, const regular_node* n ) const + { + uint64_t size; + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + + for ( uint64_t i = 0; i < size; ++i ) + { + pointer_type ptr; + bool result = this->operator()( ar_input, &ptr ); + if ( !result ) + { + return false; + } + const_cast*>( n )->children[i] = ptr; + } + + ar_input.load( (char*)&size, sizeof( uint64_t ) ); + for ( uint64_t i = 0; i < size; ++i ) + { + cauint64_t data; + bool result = this->operator()( ar_input, &data ); + if ( !result ) + { + return false; + } + const_cast*>( n )->data[i] = data; + } + + return true; + } + + bool operator()( phmap::BinaryOutputArchive& os, std::pair const& value ) const + { + return this->operator()( os, value.first ) && this->operator()( os, value.second ); + } + + bool operator()( phmap::BinaryInputArchive& ar_input, std::pair* value ) const + { + return this->operator()( ar_input, &value->first ) && this->operator()( ar_input, &value->second ); + } + + bool operator()( phmap::BinaryOutputArchive& os, aig_storage const& storage ) const + { + /* nodes */ + uint64_t size = storage.nodes.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& n : storage.nodes ) + { + if ( !this->operator()( os, n ) ) + { + return false; + } + } + + /* inputs */ + size = storage.inputs.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& i : storage.inputs ) + { + if ( !this->operator()( os, i ) ) + { + return false; + } + } + + /* outputs */ + size = storage.outputs.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& o : storage.outputs ) + { + if ( !this->operator()( os, o ) ) + { + return false; + } + } + + /* hash */ + if ( !const_cast( storage ).hash.dump( os ) ) + { + return false; + } + + if ( !os.dump( (char*)&storage.trav_id, sizeof( uint32_t ) ) ) + { + return false; + } + + return true; + } + + bool operator()( phmap::BinaryInputArchive& ar_input, aig_storage* storage ) const + { + /* nodes */ + uint64_t size; + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( uint64_t i = 0; i < size; ++i ) + { + node_type n; + if ( !this->operator()( ar_input, &n ) ) + { + return false; + } + storage->nodes.push_back( n ); + } + + /* inputs */ + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( uint64_t i = 0; i < size; ++i ) + { + uint64_t value; + if ( !ar_input.load( (char*)&value, sizeof( uint64_t ) ) ) + { + return false; + } + storage->inputs.push_back( value ); + } + + /* outputs */ + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( uint64_t i = 0; i < size; ++i ) + { + pointer_type ptr; + if ( !this->operator()( ar_input, &ptr ) ) + { + return false; + } + storage->outputs.push_back( ptr ); + } + + /* hash */ + if ( !storage->hash.load( ar_input ) ) + { + return false; + } + + if ( !ar_input.load( (char*)&storage->trav_id, sizeof( uint32_t ) ) ) + { + return false; + } + + return true; + } +}; /* struct serializer */ + +} /* namespace detail */ + +/*! \brief Serializes a combinational AIG network to a archive, returning false on failure + * + * \param aig Combinational AIG network + * \param os Output archive + */ +inline bool serialize_network_fallible( aig_network const& aig, phmap::BinaryOutputArchive& os ) +{ + detail::serializer _serializer; + return _serializer( os, *aig._storage ); +} + +/*! \brief Serializes a combinational AIG network to a archive + * + * \param aig Combinational AIG network + * \param os Output archive + */ +inline void serialize_network( aig_network const& aig, phmap::BinaryOutputArchive& os ) +{ + bool const okay = serialize_network_fallible( aig, os ); + (void)okay; + assert( okay && "failed to serialize the network onto stream" ); +} + +/*! \brief Serializes a combinational AIG network in a file + * + * \param aig Combinational AIG network + * \param filename Filename + */ +inline void serialize_network( aig_network const& aig, std::string const& filename ) +{ + phmap::BinaryOutputArchive ar_out( filename.c_str() ); + serialize_network( aig, ar_out ); +} + +/*! \brief Deserializes a combinational AIG network from a input archive, returning nullopt on failure + * + * \param ar_input Input archive + * \return Deserialized AIG network + */ +inline std::optional deserialize_network_fallible( phmap::BinaryInputArchive& ar_input ) +{ + detail::serializer _serializer; + auto storage = std::make_shared(); + storage->nodes.clear(); + storage->inputs.clear(); + storage->outputs.clear(); + storage->hash.clear(); + + if ( _serializer( ar_input, storage.get() ) ) + { + return aig_network{ storage }; + } + + return std::nullopt; +} + +/*! \brief Deserializes a combinational AIG network from a input archive + * + * \param ar_input Input archive + * \return Deserialized AIG network + */ +inline aig_network deserialize_network( phmap::BinaryInputArchive& ar_input ) +{ + auto result = deserialize_network_fallible( ar_input ); + (void)result.has_value(); + assert( result.has_value() && "failed to deserialize the network onto stream" ); + return *result; +} + +/*! \brief Deserializes a combinational AIG network from a file + * + * \param filename Filename + * \return Deserialized AIG network + */ +inline aig_network deserialize_network( std::string const& filename ) +{ + phmap::BinaryInputArchive ar_input( filename.c_str() ); + auto aig = deserialize_network( ar_input ); + return aig; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/super_reader.hpp b/third-party/mockturtle/include/mockturtle/io/super_reader.hpp new file mode 100644 index 00000000000..335dbf6a0aa --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/super_reader.hpp @@ -0,0 +1,103 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file super_reader.hpp + \brief Reader visitor for SUPER files generated by ABC + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +struct supergate_spec +{ + unsigned int id; + std::string name{}; + bool is_super{ false }; + std::vector fanin_id; +}; + +struct super_lib +{ + std::string genlib_name{}; + uint32_t max_num_vars{ 0u }; + uint32_t num_supergates{ 0u }; + uint32_t num_lines{ 0 }; + std::vector supergates{}; +}; + +/*! \brief lorina callbacks for SUPER files. + * + * SUPER files can be generated by ABC with the command `super`. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + super_lib supergates_spec; + lorina::read_super( "file.super", super_reader( supergates_spec ) ); + \endverbatim + */ +class super_reader : public lorina::super_reader +{ +public: + explicit super_reader( super_lib& lib ) + : lib( lib ) + { + } + + virtual void on_super_info( std::string const& genlib_name, uint32_t max_num_vars, uint32_t max_superGates, uint32_t num_lines ) const override + { + lib.genlib_name = genlib_name; + lib.max_num_vars = max_num_vars; + lib.num_supergates = max_superGates; + lib.num_lines = num_lines; + } + + virtual void on_supergate( std::string const& name, bool const& is_super, std::vector const& fanins_id ) const override + { + + lib.supergates.emplace_back( supergate_spec{ static_cast( lib.supergates.size() ), + name, + is_super, + fanins_id } ); + } + +protected: + super_lib& lib; +}; /* super_reader */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/verilog_reader.hpp b/third-party/mockturtle/include/mockturtle/io/verilog_reader.hpp new file mode 100644 index 00000000000..1f8c851fbb3 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/verilog_reader.hpp @@ -0,0 +1,636 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file verilog_reader.hpp + \brief Lorina reader for VERILOG files + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../generators/arithmetic.hpp" +#include "../generators/modular_arithmetic.hpp" +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for VERILOG files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `get_constant` + * - `create_not` + * - `create_and` + * - `create_or` + * - `create_xor` + * - `create_maj` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + mig_network mig; + lorina::read_verilog( "file.v", verilog_reader( mig ) ); + \endverbatim + */ +template +class verilog_reader : public lorina::verilog_reader +{ +public: + explicit verilog_reader( Ntk& ntk, std::string const& top_module_name = "top" ) : ntk_( ntk ), top_module_name_( top_module_name ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and function" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or function" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor function" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite function" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj function" ); + + signals_["0"] = ntk_.get_constant( false ); + signals_["1"] = ntk_.get_constant( true ); + signals_["1'b0"] = ntk_.get_constant( false ); + signals_["1'b1"] = ntk_.get_constant( true ); + } + + void on_module_header( const std::string& module_name, const std::vector& inouts ) const override + { + (void)inouts; + if constexpr ( has_set_network_name_v ) + { + ntk_.set_network_name( module_name ); + } + + name_ = module_name; + } + + void on_inputs( const std::vector& names, std::string const& size = "" ) const override + { + (void)size; + if ( name_ != top_module_name_ ) + return; + + for ( const auto& name : names ) + { + if ( size.empty() ) + { + signals_[name] = ntk_.create_pi(); + input_names_.emplace_back( name, 1u ); + if constexpr ( has_set_name_v ) + { + ntk_.set_name( signals_[name], name ); + } + } + else + { + std::vector> word; + const auto length = parse_size( size ); + for ( auto i = 0u; i < length; ++i ) + { + const auto sname = fmt::format( "{}[{}]", name, i ); + word.push_back( ntk_.create_pi() ); + signals_[sname] = word.back(); + if constexpr ( has_set_name_v ) + { + ntk_.set_name( signals_[sname], sname ); + } + } + registers_[name] = word; + input_names_.emplace_back( name, length ); + } + } + } + + void on_outputs( const std::vector& names, std::string const& size = "" ) const override + { + (void)size; + if ( name_ != top_module_name_ ) + return; + + for ( const auto& name : names ) + { + if ( size.empty() ) + { + outputs_.emplace_back( name ); + output_names_.emplace_back( name, 1u ); + } + else + { + const auto length = parse_size( size ); + for ( auto i = 0u; i < length; ++i ) + { + outputs_.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + output_names_.emplace_back( name, length ); + } + } + } + + void on_assign( const std::string& lhs, const std::pair& rhs ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( rhs.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", rhs.first ); + + auto r = signals_[rhs.first]; + signals_[lhs] = rhs.second ? ntk_.create_not( r ) : r; + } + + void on_nand( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + signals_[lhs] = ntk_.create_nand( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + + void on_and( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + if constexpr ( is_crossed_network_type_v ) + { + if ( !op1.second && !op2.second ) + signals_[lhs] = ntk_.create_and( a, b ); + else if ( !op1.second && op2.second ) + signals_[lhs] = ntk_.create_gt( a, b ); // a & !b + else if ( op1.second && !op2.second ) + signals_[lhs] = ntk_.create_lt( a, b ); // !a & b + else + signals_[lhs] = ntk_.create_nor( a, b ); // !a & !b = !(a | b) + } + else + { + signals_[lhs] = ntk_.create_and( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + } + + void on_or( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + if constexpr ( is_crossed_network_type_v ) + { + if ( !op1.second && !op2.second ) + signals_[lhs] = ntk_.create_or( a, b ); + else if ( !op1.second && op2.second ) + signals_[lhs] = ntk_.create_ge( a, b ); // a | !b + else if ( op1.second && !op2.second ) + signals_[lhs] = ntk_.create_le( a, b ); // !a | b + else + signals_[lhs] = ntk_.create_nand( a, b ); // !a | !b = !(a & b) + } + else + { + signals_[lhs] = ntk_.create_or( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + } + + void on_xor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + if constexpr ( is_crossed_network_type_v ) + { + if ( !op1.second == !op2.second ) + signals_[lhs] = ntk_.create_xor( a, b ); + else + signals_[lhs] = ntk_.create_xnor( a, b ); + } + else + { + signals_[lhs] = ntk_.create_xor( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + } + + void on_xor3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if constexpr ( is_crossed_network_type_v ) + { + assert( false && "3-input gates in crossed_network are not supported (to be implemented)" ); + } + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + if ( signals_.find( op3.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op3.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + auto c = signals_[op3.first]; + + if constexpr ( has_create_xor3_v ) + { + signals_[lhs] = ntk_.create_xor3( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b, op3.second ? ntk_.create_not( c ) : c ); + } + else + { + signals_[lhs] = ntk_.create_xor( ntk_.create_xor( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ), op3.second ? ntk_.create_not( c ) : c ); + } + } + + void on_maj3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if constexpr ( is_crossed_network_type_v ) + { + assert( false && "3-input gates in crossed_network are not supported (to be implemented)" ); + } + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + if ( signals_.find( op3.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op3.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + auto c = signals_[op3.first]; + signals_[lhs] = ntk_.create_maj( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b, op3.second ? ntk_.create_not( c ) : c ); + } + + void on_mux21( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if constexpr ( is_crossed_network_type_v ) + { + assert( false && "3-input gates in crossed_network are not supported (to be implemented)" ); + } + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + if ( signals_.find( op3.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op3.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + auto c = signals_[op3.first]; + signals_[lhs] = ntk_.create_ite( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b, op3.second ? ntk_.create_not( c ) : c ); + } + + void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, + std::vector> const& args ) const override + { + (void)inst_name; + if ( name_ != top_module_name_ ) + return; + + /* check routines */ + const auto num_args_equals = [&]( uint32_t expected_count ) { + if ( args.size() != expected_count ) + { + fmt::print( stderr, "[e] {} module expects {} arguments\n", module_name, expected_count ); + return false; + } + return true; + }; + + const auto num_params_equals = [&]( uint32_t expected_count ) { + if ( params.size() != expected_count ) + { + fmt::print( stderr, "[e] {} module expects {} parameters\n", module_name, expected_count ); + return false; + } + return true; + }; + + const auto register_exists = [&]( std::string const& name ) { + if ( registers_.find( name ) == registers_.end() ) + { + fmt::print( stderr, "[e] register {} does not exist\n", name ); + return false; + } + return true; + }; + + const auto register_has_size = [&]( std::string const& name, uint32_t size ) { + if ( !register_exists( name ) || registers_[name].size() != size ) + { + fmt::print( stderr, "[e] register {} must have size {}\n", name, size ); + return false; + } + return true; + }; + + const auto add_register = [&]( std::string const& name, std::vector> const& fs ) { + for ( auto i = 0u; i < fs.size(); ++i ) + { + signals_[fmt::format( "{}[{}]", name, i )] = fs[i]; + } + registers_[name] = fs; + }; + + if ( module_name == "ripple_carry_adder" ) + { + if ( !num_args_equals( 3u ) ) + return; + if ( !num_params_equals( 1u ) ) + return; + const auto bitwidth = static_cast( parse_small_value( params[0u] ) ); + if ( !register_has_size( args[0].second, bitwidth ) ) + return; + if ( !register_has_size( args[1].second, bitwidth ) ) + return; + + auto a_copy = registers_[args[0].second]; + const auto& b = registers_[args[1].second]; + auto carry = ntk_.get_constant( false ); + carry_ripple_adder_inplace( ntk_, a_copy, b, carry ); + a_copy.push_back( carry ); + add_register( args[2].second, a_copy ); + } + else if ( module_name == "montgomery_multiplier" ) + { + if ( !num_args_equals( 3u ) ) + return; + if ( !num_params_equals( 3u ) ) + return; + const auto bitwidth = static_cast( parse_small_value( params[0u] ) ); + if ( !register_has_size( args[0].second, bitwidth ) ) + return; + if ( !register_has_size( args[1].second, bitwidth ) ) + return; + + auto N = parse_value( params[1u] ); + auto NN = parse_value( params[2u] ); + + N.resize( bitwidth ); + NN.resize( bitwidth ); + + add_register( args[2].second, montgomery_multiplication( ntk_, registers_[args[0].second], registers_[args[1].second], N, NN ) ); + } + else if ( module_name == "buffer" || module_name == "inverter" ) + { + if constexpr ( is_buffered_network_type_v ) + { + static_assert( has_create_buf_v, "Ntk does not implement the create_buf method" ); + if ( !num_args_equals( 2u ) ) + fmt::print( stderr, "[e] number of arguments of a `{}` instance is not 2\n", module_name ); + + signal fi = ntk_.get_constant( false ); + std::string lhs; + for ( auto const& arg : args ) + { + if ( arg.first == ".i" ) + { + if ( signals_.find( arg.second ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", arg.second ); + else + fi = signals_[arg.second]; + } + else if ( arg.first == ".o" ) + lhs = arg.second; + else + fmt::print( stderr, "[e] unknown argument {} to a `{}` instance\n", arg.first, module_name ); + } + signals_[lhs] = ntk_.create_buf( fi ); + if ( module_name == "inverter" ) + ntk_.invert( ntk_.get_node( signals_[lhs] ) ); + } + } + else if ( module_name == "crossing" ) + { + if constexpr ( is_crossed_network_type_v ) + { + if ( !num_args_equals( 4u ) ) + fmt::print( stderr, "[e] number of arguments of a `{}` instance is not 4\n", module_name ); + + signal fi1 = ntk_.get_constant( false ); + signal fi2 = ntk_.get_constant( false ); + std::string fo1, fo2; + for ( auto const& arg : args ) + { + if ( arg.first == ".i1" ) + { + if ( signals_.find( arg.second ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", arg.second ); + else + fi1 = signals_[arg.second]; + } + else if ( arg.first == ".i2" ) + { + if ( signals_.find( arg.second ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", arg.second ); + else + fi2 = signals_[arg.second]; + } + else if ( arg.first == ".o1" ) + fo1 = arg.second; + else if ( arg.first == ".o2" ) + fo2 = arg.second; + else + fmt::print( stderr, "[e] unknown argument {} to a `{}` instance\n", arg.first, module_name ); + } + + if ( signals_.find( fo1 ) == signals_.end() ) // due to lorina bug, each crossing will be instantiated twice... + { + auto p = ntk_.create_crossing( fi1, fi2 ); + signals_[fo1] = p.first; + signals_[fo2] = p.second; + } + } + } + else + { + fmt::print( stderr, "[e] unknown module name {}\n", module_name ); + } + } + + void on_endmodule() const override + { + if ( name_ != top_module_name_ ) + return; + + for ( auto const& o : outputs_ ) + { + ntk_.create_po( signals_[o] ); + } + + if constexpr ( has_set_output_name_v ) + { + uint32_t ctr{ 0u }; + for ( auto const& output_name : output_names_ ) + { + if ( output_name.second == 1u ) + { + ntk_.set_output_name( ctr++, output_name.first ); + } + else + { + for ( auto i = 0u; i < output_name.second; ++i ) + { + ntk_.set_output_name( ctr++, fmt::format( "{}[{}]", output_name.first, i ) ); + } + } + } + assert( ctr == ntk_.num_pos() ); + } + } + + const std::string& name() const + { + return name_; + } + + const std::vector> input_names() + { + return input_names_; + } + + const std::vector> output_names() + { + return output_names_; + } + +private: + std::vector parse_value( const std::string& value ) const + { + std::smatch match; + + if ( std::all_of( value.begin(), value.end(), isdigit ) ) + { + std::vector res( 64u ); + bool_vector_from_dec( res, static_cast( std::stoul( value ) ) ); + return res; + } + else if ( std::regex_match( value, match, hex_string ) ) + { + std::vector res( static_cast( std::stoul( match.str( 1 ) ) ) ); + bool_vector_from_hex( res, match.str( 2 ) ); + return res; + } + else + { + fmt::print( stderr, "[e] cannot parse number '{}'\n", value ); + } + assert( false ); + return {}; + } + + uint64_t parse_small_value( const std::string& value ) const + { + return bool_vector_to_long( parse_value( value ) ); + } + + uint32_t parse_size( const std::string& size ) const + { + if ( size.empty() ) + { + return 1u; + } + + if ( auto const l = size.size(); l > 2 && size[l - 2] == ':' && size[l - 1] == '0' ) + { + return static_cast( parse_small_value( size.substr( 0u, l - 2 ) ) + 1u ); + } + + assert( false ); + return 0u; + } + +private: + Ntk& ntk_; + + std::string const top_module_name_; + + mutable std::map> signals_; + mutable std::map>> registers_; + mutable std::vector outputs_; + mutable std::string name_; + mutable std::vector> input_names_; + mutable std::vector> output_names_; + + std::regex hex_string{ "(\\d+)'h([0-9a-fA-F]+)" }; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_aiger.hpp b/third-party/mockturtle/include/mockturtle/io/write_aiger.hpp new file mode 100644 index 00000000000..621b3645337 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_aiger.hpp @@ -0,0 +1,199 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_aiger.hpp + \brief Write networks to AIGER format + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee + + A detailed description of the (binary) AIGER format and its encoding is available at [1]. + + [1] http://fmv.jku.at/aiger/ +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +inline void encode( std::vector& buffer, uint32_t lit ) +{ + unsigned char ch; + while ( lit & ~0x7f ) + { + ch = ( lit & 0x7f ) | 0x80; + buffer.push_back( ch ); + lit >>= 7; + } + ch = lit; + buffer.push_back( ch ); +} + +} // namespace detail + +/*! \brief Writes a combinational AIG network in binary AIGER format into a file + * + * This function should be only called on "clean" aig_networks, e.g., + * immediately after `cleanup_dangling`. + * + * **Required network functions:** + * - `num_cis` + * - `num_cos` + * - `foreach_gate` + * - `foreach_fanin` + * - `foreach_po` + * - `get_node` + * - `is_complemented` + * - `node_to_index` + * + * \param aig Combinational AIG network + * \param os Output stream + */ +template +inline void write_aiger( Ntk const& aig, std::ostream& os ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_cis_v, "Ntk does not implement the num_cis method" ); + static_assert( has_num_cos_v, "Ntk does not implement the num_cos method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + + assert( aig.is_combinational() && "Network has to be combinational" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + uint32_t const M = aig.num_cis() + aig.num_gates(); + + /* HEADER */ + char string_buffer[1024]; + sprintf( string_buffer, "aig %u %u %u %u %u\n", M, aig.num_pis(), /*latches*/ 0, aig.num_pos(), aig.num_gates() ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + + /* POs */ + aig.foreach_po( [&]( signal const& f ) { + sprintf( string_buffer, "%u\n", uint32_t( 2 * aig.node_to_index( aig.get_node( f ) ) + aig.is_complemented( f ) ) ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + } ); + + /* GATES */ + std::vector buffer; + aig.foreach_gate( [&]( node const& n ) { + std::vector lits; + lits.push_back( 2 * aig.node_to_index( n ) ); + + aig.foreach_fanin( n, [&]( signal const& fi ) { + lits.push_back( 2 * aig.node_to_index( aig.get_node( fi ) ) + aig.is_complemented( fi ) ); + } ); + + if ( lits[1] > lits[2] ) + { + auto const tmp = lits[1]; + lits[1] = lits[2]; + lits[2] = tmp; + } + + assert( lits[2] < lits[0] ); + detail::encode( buffer, lits[0] - lits[2] ); + detail::encode( buffer, lits[2] - lits[1] ); + } ); + + for ( const auto& b : buffer ) + { + os.put( b ); + } + + /* symbol table */ + if constexpr ( has_has_name_v && has_get_name_v ) + { + aig.foreach_pi( [&]( node const& i, uint32_t index ) { + if ( !aig.has_name( aig.make_signal( i ) ) ) + return; + + sprintf( string_buffer, "i%u %s\n", + uint32_t( index ), + aig.get_name( aig.make_signal( i ) ).c_str() ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + } ); + } + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + aig.foreach_po( [&]( signal const& f, uint32_t index ) { + if ( !aig.has_output_name( index ) ) + return; + + sprintf( string_buffer, "o%u %s\n", + uint32_t( index ), + aig.get_output_name( index ).c_str() ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + } ); + } + + /* COMMENT */ + os.put( 'c' ); +} + +/*! \brief Writes a combinational AIG network in binary AIGER format into a file + * + * This function should be only called on "clean" aig_networks, e.g., + * immediately after `cleanup_dangling`. + * + * **Required network functions:** + * - `num_cis` + * - `num_cos` + * - `foreach_gate` + * - `foreach_fanin` + * - `foreach_po` + * - `get_node` + * - `is_complemented` + * - `node_to_index` + * + * \param aig Combinational AIG network + * \param filename Filename + */ +template +inline void write_aiger( Ntk const& aig, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_aiger( aig, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_bench.hpp b/third-party/mockturtle/include/mockturtle/io/write_bench.hpp new file mode 100644 index 00000000000..964d4c35101 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_bench.hpp @@ -0,0 +1,164 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_bench.hpp + \brief Write networks to BENCH format + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Writes network in BENCH format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `get_node` + * - `num_pos` + * - `node_to_index` + * - `node_function` + * + * \param ntk Network + * \param os Output stream + */ +template +void write_bench( Ntk const& ntk, std::ostream& os ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + ntk.foreach_pi( [&]( auto const& n ) { + os << fmt::format( "INPUT(n{})\n", ntk.node_to_index( n ) ); + } ); + + for ( auto i = 0u; i < ntk.num_pos(); ++i ) + { + os << fmt::format( "OUTPUT(po{})\n", i ); + } + + os << fmt::format( "n{} = gnd\n", ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + os << fmt::format( "n{} = vdd\n", ntk.node_to_index( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return; /* continue */ + + auto func = ntk.node_function( n ); + std::string children; + auto first = true; + ntk.foreach_fanin( n, [&]( auto const& c, auto i ) { + if ( ntk.is_complemented( c ) ) + { + kitty::flip_inplace( func, i ); + } + if ( first ) + { + first = false; + } + else + { + children += ", "; + } + + children += fmt::format( "n{}", ntk.node_to_index( ntk.get_node( c ) ) ); + } ); + + os << fmt::format( "n{} = LUT 0x{} ({})\n", + ntk.node_to_index( n ), + kitty::to_hex( func ), children ); + } ); + + /* outputs */ + ntk.foreach_po( [&]( auto const& s, auto i ) { + if ( ntk.is_constant( ntk.get_node( s ) ) ) + { + os << fmt::format( "po{} = {}\n", + i, + ( ntk.constant_value( ntk.get_node( s ) ) ^ ntk.is_complemented( s ) ) ? "vdd" : "gnd" ); + } + else + { + os << fmt::format( "po{} = LUT 0x{} (n{})\n", + i, + ntk.is_complemented( s ) ? 1 : 2, + ntk.node_to_index( ntk.get_node( s ) ) ); + } + } ); + + os << std::flush; +} + +/*! \brief Writes network in BENCH format into a file + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `get_node` + * - `num_pos` + * - `node_to_index` + * - `node_function` + * + * \param ntk Network + * \param filename Filename + */ +template +void write_bench( Ntk const& ntk, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_bench( ntk, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_blif.hpp b/third-party/mockturtle/include/mockturtle/io/write_blif.hpp new file mode 100644 index 00000000000..bf0de341eeb --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_blif.hpp @@ -0,0 +1,430 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_blif.hpp + \brief Write networks to BLIF format + + \author Heinz Riener + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/sequential.hpp" +#include "../traits.hpp" +#include "../views/topo_view.hpp" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace mockturtle +{ + +struct write_blif_params +{ + /** + * ## `ps.rename_ri_using_node` + * + * Rename registers using node name if `rename_ri_using_node` is set to 1 ( default: 0 ) + * + * A register is represented by a `ro_to_ri` mapping: + * + * ``` + * ri_node --> ro_signal + * ``` + * where `ri_node` is the register input (a combinational output node), and `ro_signal` is + * the register output (a combinational input signal). + * + * If `rename_ri_using_node` is set to 1, then `ri_node` will be renamed (from its default + * name) using the node name. We write the following to the BLIF file: + * + * ``` + * .latch ri_node ro_signal + * ``` + * + * Otherwise, if `rename_ri_using_node` is set to 1, `ri_node` will be named by its default + * name, `li_`, where `idx` is the index of the register (based on the definition order + * when calling `create_ro`). Then we write: + * + * ``` + * .latch li_ ro_signal + * .names ri_node li_ + * 1 1 + * ``` + */ + uint32_t rename_ri_using_node = 0u; +}; + +/*! \brief Writes network in BLIF format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - `fanin_size` + * - `foreach_fanin` + * - `foreach_pi` + * - `foreach_po` + * - `get_node` + * - `is_constant` + * - `is_pi` + * - `node_function` + * - `node_to_index` + * - `num_pis` + * - `num_pos` + * + * \param ntk Network + * \param os Output stream + */ +template +void write_blif( Ntk const& ntk, std::ostream& os, write_blif_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + uint32_t num_latches{ 0 }; + if constexpr ( has_num_registers_v ) + { + num_latches = ntk.num_registers(); + } + + topo_view topo_ntk{ ntk }; + std::unordered_set defined_names; + + /* write model */ + os << ".model top\n"; + + /* write inputs */ + if ( topo_ntk.num_pis() > 0u ) + { + os << ".inputs "; + topo_ntk.foreach_ci( [&]( auto const& n, auto index ) { + if ( ( ( index + 1 ) <= topo_ntk.num_cis() - num_latches ) ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + signal const s = topo_ntk.make_signal( topo_ntk.node_to_index( n ) ); + std::string const input_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : fmt::format( "pi{}", topo_ntk.get_node( s ) ); + os << input_name << ' '; + defined_names.insert( input_name ); /* we should not have collision here */ + } + else + { + std::string const input_name = fmt::format( "pi{}", topo_ntk.node_to_index( n ) ); + os << input_name << ' '; + defined_names.insert( input_name ); /* we should not have collision here */ + } + } + } ); + os << "\n"; + } + + /* write outputs */ + if ( topo_ntk.num_pos() > 0u ) + { + os << ".outputs "; + topo_ntk.foreach_co( [&]( auto const& f, auto index ) { + (void)f; + if ( index < topo_ntk.num_cos() - num_latches ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + std::string const output_name = topo_ntk.has_output_name( index ) ? topo_ntk.get_output_name( index ) : fmt::format( "po{}", index ); + os << output_name << ' '; + } + else + { + std::string const output_name = fmt::format( "po{}", index ); + os << output_name << ' '; + } + } + } ); + os << "\n"; + } + + if constexpr ( has_num_registers_v ) + { + if ( num_latches > 0u ) + { + uint32_t latch_idx = 0; + topo_ntk.foreach_co( [&]( auto const& f, auto index ) { + if ( index >= topo_ntk.num_cos() - num_latches ) + { + os << ".latch "; + auto const ro_signal = topo_ntk.make_signal( topo_ntk.ro_at( latch_idx ) ); + auto const ri_signal = topo_ntk.ri_at( latch_idx ); + register_t latch_info = topo_ntk.register_at( latch_idx ); + if constexpr ( has_has_name_v && has_get_name_v ) + { + std::string const node_name = topo_ntk.has_name( ri_signal ) ? + topo_ntk.get_name( ri_signal ) + : fmt::format( "new_n{}", topo_ntk.get_node( ri_signal ) ); + std::string const latch_name = ps.rename_ri_using_node ? + node_name + : fmt::format( "li{}", latch_idx ); + std::string const ri_name = topo_ntk.has_output_name( index ) ? + topo_ntk.get_output_name( index ) + : latch_name; + std::string const ro_name = topo_ntk.has_name( ro_signal ) ? topo_ntk.get_name( ro_signal ) : fmt::format( "new_n{}", topo_ntk.get_node( ro_signal ) ); + os << fmt::format( "{} {} {} {} {}\n", ri_name, ro_name, latch_info.type, latch_info.control, latch_info.init ); + defined_names.insert( ro_name ); /* we should not have collision here */ + } + else + { + std::string const ri_name = ps.rename_ri_using_node? + fmt::format( "new_n{}", topo_ntk.get_node( topo_ntk.ri_at( latch_idx ) ) ) + : fmt::format( "li{}", latch_idx ); + std::string const ro_name = fmt::format( "new_n{}", topo_ntk.get_node( ro_signal ) ); + os << fmt::format( "{} {} {} {} {}\n", ri_name, ro_name, latch_info.type, latch_info.control, latch_info.init ); + defined_names.insert( ro_name ); /* we should not have collision here */ + } + latch_idx++; + } + } ); + } + } + + /* write constants */ + os << ".names new_n0\n"; + os << "0\n"; + defined_names.insert( "new_n0" ); /* we should not have collision here */ + + if ( topo_ntk.get_constant( false ) != topo_ntk.get_constant( true ) ) + { + os << ".names new_n1\n"; + os << "1\n"; + defined_names.insert( "new_n1" ); /* we should not have collision here */ + } + + /* write nodes */ + topo_ntk.foreach_node( [&]( auto const& n ) { + if ( topo_ntk.is_constant( n ) || topo_ntk.is_ci( n ) ) + return; /* continue */ + + /* write truth table of node */ + auto func = topo_ntk.node_function( n ); + + if ( isop( func ).size() == 0 ) /* constants */ + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + auto const s = topo_ntk.make_signal( n ); + std::string const constant_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : fmt::format( "new_n{}", topo_ntk.get_node( s ) ); + os << fmt::format( ".names {}\n", constant_name ); + os << "0" << '\n'; + defined_names.insert( constant_name ); /* we should not have collision here */ + } + else + { + std::string const constant_name = fmt::format( "new_n{}", n ); + os << fmt::format( ".names {}\n", constant_name ); + os << "0" << '\n'; + defined_names.insert( constant_name ); /* we should not have collision here */ + } + return; + } + + os << fmt::format( ".names " ); + + /* write fanins of node */ + topo_ntk.foreach_fanin( n, [&]( auto const& f ) { + auto f_node = topo_ntk.get_node( f ); + if constexpr ( has_has_name_v && has_get_name_v ) + { + signal const s = topo_ntk.make_signal( f_node ); + std::string const fanin_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : topo_ntk.is_pi( f_node ) ? fmt::format( "pi{}", f_node ) : fmt::format( "new_n{}", f_node ); + os << fanin_name << ' '; + } + else + { + std::string const fanin_name = topo_ntk.is_pi( f_node ) ? fmt::format( "pi{} ", f_node ) : fmt::format( "new_n{} ", f_node ); + os << fanin_name; + } + } ); + + /* write fanout of node */ + if constexpr ( has_has_name_v && has_get_name_v ) + { + auto const s = topo_ntk.make_signal( n ); + std::string const fanout_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : fmt::format( "new_n{}", topo_ntk.get_node( s ) ); + os << fanout_name << '\n'; + defined_names.insert( fanout_name ); /* we should not have collision here */ + } + else + { + std::string const fanout_name = fmt::format( "new_n{}", n ); + os << fanout_name << '\n'; + defined_names.insert( fanout_name ); /* we should not have collision here */ + } + + int count = 0; + for ( auto cube : isop( func ) ) + { + topo_ntk.foreach_fanin( n, [&]( auto const& f, auto index ) { + if ( cube.get_mask( index ) && topo_ntk.is_complemented( f ) ) + cube.flip_bit( index ); + } ); + + cube.print( topo_ntk.fanin_size( n ), os ); + os << " 1\n"; + count++; + } + } ); + + auto latch_idx = 0; + topo_ntk.foreach_co( [&]( auto const& f, auto index ) { + auto f_node = topo_ntk.get_node( f ); + auto const minterm_string = topo_ntk.is_complemented( f ) ? "0" : "1"; + + if ( index < topo_ntk.num_cos() - num_latches ) /* the signal f is a PO */ + { + /* the default name depends on whether the signal is a PI or a regular signal */ + std::string const node_default_name = topo_ntk.is_pi( f_node ) ? fmt::format( "pi{}", f_node ) : fmt::format( "new_n{}", f_node ); + + if constexpr ( has_has_name_v && has_get_name_v && has_has_output_name_v && has_get_output_name_v ) /* with name view */ + { + signal const s = topo_ntk.make_signal( topo_ntk.get_node( f ) ); + + /* then we overwrite the default name if we assigned names from name_view */ + std::string const node_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : node_default_name; + + /* get the default name of PO */ + std::string default_output_name = fmt::format( "po{}", index ); + + /* over write the name if we have name view */ + std::string const output_name = topo_ntk.has_output_name( index ) ? topo_ntk.get_output_name( index ) : default_output_name; + + /* we need to bridge the nodes */ + if ( node_name != output_name && defined_names.find( output_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, output_name, minterm_string ); + defined_names.insert( output_name ); + } + } + else /* without name view */ + { + std::string const node_name = node_default_name; + std::string const output_name = fmt::format( "po{}", index ); + if ( node_name != output_name && defined_names.find( output_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, output_name, minterm_string ); + defined_names.insert( output_name ); + } + } + } + else /* the signal f is a RI */ + { + /* the default name depends on whether the signal is a PI or a regular signal */ + std::string const node_default_name = topo_ntk.is_pi( f_node ) ? fmt::format( "pi{}", f_node ) : fmt::format( "new_n{}", f_node ); + + if constexpr ( has_has_name_v && has_get_name_v && has_has_output_name_v && has_get_output_name_v ) /* with name view */ + { + signal const s = topo_ntk.make_signal( topo_ntk.get_node( f ) ); + + /* then we overwrite the default name if we assigned names from name_view */ + std::string const node_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : node_default_name; + + /* get the default name of RI */ + std::string default_ri_name = ps.rename_ri_using_node ? node_name : fmt::format( "li{}", latch_idx ); + + /* overwrite the name if we have name view */ + std::string const ri_name = topo_ntk.has_output_name( index ) ? topo_ntk.get_output_name( index ) : default_ri_name; + + /* we need to bridge the nodes */ + if ( node_name != ri_name && defined_names.find( ri_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, ri_name, minterm_string ); + defined_names.insert( ri_name ); + } + } + else /* without name view */ + { + std::string const node_name = node_default_name; + + /* get the default name of RI */ + std::string ri_name = ps.rename_ri_using_node ? node_name : fmt::format( "li{}", latch_idx ); + + if ( node_name != ri_name && defined_names.find( ri_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, ri_name, minterm_string ); + defined_names.insert( ri_name ); + } + } + + latch_idx++; + } + } ); + + os << ".end\n"; + os << std::flush; +} + +/*! \brief Writes network in BLIF format into a file + * + * **Required network functions:** + * - `fanin_size` + * - `foreach_fanin` + * - `foreach_pi` + * - `foreach_po` + * - `get_node` + * - `is_constant` + * - `is_pi` + * - `node_function` + * - `node_to_index` + * - `num_pis` + * - `num_pos` + * + * \param ntk Network + * \param filename Filename + */ +template +void write_blif( Ntk const& ntk, std::string const& filename, write_blif_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_blif( ntk, os, ps ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_dimacs.hpp b/third-party/mockturtle/include/mockturtle/io/write_dimacs.hpp new file mode 100644 index 00000000000..092c40c3d01 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_dimacs.hpp @@ -0,0 +1,126 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2019 EPFL EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_dimacs.hpp + \brief Write networks CNF encoding to DIMACS format + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../algorithms/cnf.hpp" +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Writes network into CNF DIMACS format + * + * It also adds unit clauses for the outputs. Therefore a satisfying solution + * is one that makes all outputs 1. + * + * \param ntk Logic network + * \param out Output stream + */ +template +void write_dimacs( Ntk const& ntk, std::ostream& out = std::cout ) +{ + std::stringstream clauses; + uint32_t num_clauses = 0u; + + const auto lits = generate_cnf( ntk, [&]( std::vector const& clause ) { + for ( auto lit : clause ) + { + const auto var = ( lit / 2 ) + 1; + const auto pol = lit % 2; + clauses << fmt::format( "{}{} ", pol ? "-" : "", var ); + } + clauses << fmt::format( "0\n" ); + ++num_clauses; + } ); + + for ( auto lit : lits ) + { + const auto var = ( lit / 2 ) + 1; + const auto pol = lit % 2; + clauses << fmt::format( "{}{} 0\n", pol ? "-" : "", var ); + ++num_clauses; + } + + out << fmt::format( "p cnf {} {}\n{}", ntk.size(), num_clauses, clauses.str() ); +} + +/*! \brief Writes network into CNF DIMACS format + * + * It also adds unit clauses for the outputs. Therefore a satisfying solution + * is one that makes all outputs 1. + * + * \param ntk Logic network + * \param filename Filename + */ +template +void write_dimacs( Ntk const& ntk, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_dimacs( ntk, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_dot.hpp b/third-party/mockturtle/include/mockturtle/io/write_dot.hpp new file mode 100644 index 00000000000..edbed29146c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_dot.hpp @@ -0,0 +1,456 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_dot.hpp + \brief Write graphical representation of networks to DOT format + + \author Heinz Riener + \author Mathias Soeken + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "../traits.hpp" +#include "../views/depth_view.hpp" + +namespace mockturtle +{ + +template +class default_dot_drawer +{ +public: + virtual ~default_dot_drawer() + { + } + +public: /* callbacks */ + virtual std::string node_label( Ntk const& ntk, node const& n ) const + { + return std::to_string( ntk.node_to_index( n ) ); + } + + virtual std::string node_shape( Ntk const& ntk, node const& n ) const + { + if ( ntk.is_constant( n ) ) + { + return "box"; + } + else if ( ntk.is_ci( n ) ) + { + return "triangle"; + } + else + { + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) ) + { + return "box"; + } + } + return "ellipse"; + } + } + + virtual uint32_t node_level( Ntk const& ntk, node const& n ) const + { + if ( !_depth_ntk ) + { + _depth_ntk = std::make_shared>( ntk ); + } + + return _depth_ntk->level( n ); + } + + virtual std::string po_shape( Ntk const& ntk, uint32_t i ) const + { + (void)ntk; + (void)i; + return "invtriangle"; + } + + virtual std::string node_fillcolor( Ntk const& ntk, node const& n ) const + { + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) ) + { + if ( ntk.fanout_size( n ) > 1 ) + return "lightcoral"; + else + return "lightskyblue"; + } + } + return ( ntk.is_constant( n ) || ntk.is_ci( n ) ) ? "snow2" : "white"; + } + + virtual std::string po_fillcolor( Ntk const& ntk, uint32_t i ) const + { + (void)ntk; + (void)i; + return "snow2"; + } + + virtual bool draw_signal( Ntk const& ntk, node const& n, signal const& f ) const + { + (void)ntk; + (void)n; + (void)f; + if constexpr ( is_buffered_network_type_v ) + { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + return false; + } + return true; + } + + virtual std::string signal_style( Ntk const& ntk, signal const& f ) const + { + return ntk.is_complemented( f ) ? "dashed" : "solid"; + } + +private: + mutable std::shared_ptr> _depth_ntk; +}; + +template +class gate_dot_drawer : public default_dot_drawer +{ +public: + virtual std::string node_label( Ntk const& ntk, node const& n ) const override + { + if constexpr ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + return "AND"; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + return "OR"; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + return "XOR"; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + std::string label{ "MAJ" }; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + const auto v = ntk.constant_value( ntk.get_node( f ) ) != ntk.is_complemented( f ); + label = v ? "OR" : "AND"; + return false; + } + return true; + } ); + return label; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + return "XOR"; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + return "AND"; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + return "OR"; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + return "XOR"; + } + } + + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) && !ntk.is_ci( n ) ) + { + return "BUF"; + } + } + + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return "CROSS"; + } + } + + return default_dot_drawer::node_label( ntk, n ); + } + + virtual std::string node_fillcolor( Ntk const& ntk, node const& n ) const override + { + if constexpr ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + return "lightcoral"; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + return "palegreen2"; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + return "lightskyblue"; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + std::string color{ "lightsalmon" }; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + const auto v = ntk.constant_value( ntk.get_node( f ) ) != ntk.is_complemented( f ); + color = v ? "palegreen2" : "lightcoral"; + return false; + } + return true; + } ); + return color; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + return "lightskyblue"; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + return "lightcoral"; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + return "palegreen2"; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + return "lightskyblue"; + } + } + + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) && !ntk.is_ci( n ) ) + { + return "palegoldenrod"; + } + } + + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return "palegoldenrod"; + } + } + + return default_dot_drawer::node_fillcolor( ntk, n ); + } + + virtual bool draw_signal( Ntk const& ntk, node const& n, signal const& f ) const override + { + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + return !ntk.is_constant( ntk.get_node( f ) ); + } + } + + return default_dot_drawer::draw_signal( ntk, n, f ); + } +}; + +/*! \brief Writes network in DOT format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - is_constant + * - is_ci + * - foreach_node + * - foreach_fanin + * - foreach_po + * + * \param ntk Network + * \param os Output stream + */ +template> +void write_dot( Ntk const& ntk, std::ostream& os, Drawer const& drawer = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + + std::stringstream nodes, edges, levels; + + std::vector> level_to_node_indexes; + + ntk.foreach_node( [&]( auto const& n ) { + nodes << fmt::format( "{} [label=\"{}\",shape={},style=filled,fillcolor={}]\n", + ntk.node_to_index( n ), + drawer.node_label( ntk, n ), + drawer.node_shape( ntk, n ), + drawer.node_fillcolor( ntk, n ) ); + if ( !ntk.is_constant( n ) && !ntk.is_ci( n ) ) + { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( !drawer.draw_signal( ntk, n, f ) ) + return true; + edges << fmt::format( "{} -> {} [style={}]\n", + ntk.node_to_index( ntk.get_node( f ) ), + ntk.node_to_index( n ), + drawer.signal_style( ntk, f ) ); + return true; + } ); + } + + const auto lvl = drawer.node_level( ntk, n ); + if ( level_to_node_indexes.size() <= lvl ) + { + level_to_node_indexes.resize( lvl + 1 ); + } + level_to_node_indexes[lvl].push_back( ntk.node_to_index( n ) ); + } ); + + for ( auto const& indexes : level_to_node_indexes ) + { + levels << "{rank = same; "; + std::copy( indexes.begin(), indexes.end(), std::ostream_iterator( levels, "; " ) ); + levels << "}\n"; + } + + levels << "{rank = same; "; + ntk.foreach_po( [&]( auto const& f, auto i ) { + nodes << fmt::format( "po{} [shape={},style=filled,fillcolor={}]\n", i, drawer.po_shape( ntk, i ), drawer.po_fillcolor( ntk, i ) ); + edges << fmt::format( "{} -> po{} [style={}]\n", + ntk.node_to_index( ntk.get_node( f ) ), + i, + drawer.signal_style( ntk, f ) ); + levels << fmt::format( "po{}; ", i ); + } ); + levels << "}\n"; + + os << "digraph {\n" + << "rankdir=BT;\n" + << nodes.str() << edges.str() << levels.str() << "}\n"; +} + +/*! \brief Writes network in DOT format into a file + * + * **Required network functions:** + * - is_constant + * - is_ci + * - foreach_node + * - foreach_fanin + * - foreach_po + * + * \param ntk Network + * \param filename Filename + */ +template> +void write_dot( Ntk const& ntk, std::string const& filename, Drawer const& drawer = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_dot( ntk, os, drawer ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_genlib.hpp b/third-party/mockturtle/include/mockturtle/io/write_genlib.hpp new file mode 100644 index 00000000000..9ef31eed00b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_genlib.hpp @@ -0,0 +1,105 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_genlib.hpp + \brief Writes library of gates to GENLIB format + + \author Alessandro tempia Calvino +*/ + +#pragma once + +#include "genlib_reader.hpp" +#include "../traits.hpp" + +#include + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Writes library of gates to GENLIB format + * + * An overloaded variant exists that writes the network into a file. + * + * \param gates List of gates + * \param os Output stream + */ +void write_genlib( std::vector const& gates, std::ostream& os ) +{ + /* compute pad spaces */ + size_t max_name_size = 0; + for ( gate const& g : gates ) + { + max_name_size = std::max( max_name_size, g.name.size() ); + } + ++max_name_size; + + for ( gate const& g : gates ) + { + os << "GATE "; + std::string name = g.name + std::string( max_name_size - g.name.size(), ' ' ); + os << fmt::format( "{} {:>5.4f} {}={};\n", name, g.area, g.output_name, g.expression ); + + for ( pin const& p : g.pins ) + { + std::string phase; + if ( p.phase == phase_type::INV ) + phase = "INV"; + else if ( p.phase == phase_type::NONINV ) + phase = "NONINV"; + else + phase = "UNKNOWN"; + + os << fmt::format( "\tPIN {} {} {:>3} {:>3} {:>6.4f} {:>6.4f} {:>6.4f} {:>6.4f}\n", + p.name, + phase, + ( uint32_t )p.input_load, + ( uint32_t )p.max_load, + p.rise_block_delay, + p.rise_fanout_delay, + p.fall_block_delay, + p.fall_fanout_delay ); + } + } +} + +/*! \brief Write library of gates into a GENLIB file + * + * \param gates List of gates + * \param filename Filename + */ +void write_genlib( std::vector const& gates, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_genlib( gates, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_patterns.hpp b/third-party/mockturtle/include/mockturtle/io/write_patterns.hpp new file mode 100644 index 00000000000..f46ad9e2dac --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_patterns.hpp @@ -0,0 +1,87 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_patterns.hpp + \brief Write simulation patterns + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../algorithms/simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Writes simulation patterns + * + * The output contains `num_pis()` lines, each line contains a stream of + * simulation values of a primary input, represented in hexadecimal. + * + * \param sim The `partial_simulator` or `bit_packed_simulator` object containing simulation patterns + * \param out Output stream + */ +template +void write_patterns( Simulator const& sim, std::ostream& out = std::cout ) +{ + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + auto const& patterns = sim.get_patterns(); + for ( auto i = 0u; i < patterns.size(); ++i ) + { + out << kitty::to_hex( patterns.at( i ) ) << "\n"; + } +} + +/*! \brief Writes simulation patterns + * + * The output contains `num_pis()` lines, each line contains a stream of + * simulation values of a primary input, represented in hexadecimal. + * + * \param sim The `partial_simulator` or `bit_packed_simulator` object containing simulation patterns + * \param filename Filename + */ +template +void write_patterns( Simulator const& sim, std::string const& filename ) +{ + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_patterns( sim, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/io/write_verilog.hpp b/third-party/mockturtle/include/mockturtle/io/write_verilog.hpp new file mode 100644 index 00000000000..607b41d1ff4 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/write_verilog.hpp @@ -0,0 +1,1315 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_verilog.hpp + \brief Write networks to structural Verilog format + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/string_utils.hpp" +#include "../views/binding_view.hpp" +#include "../views/topo_view.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +using namespace std::string_literals; + +namespace detail +{ + +template +std::vector> +format_fanin( Ntk const& ntk, node const& n, node_map& node_names ) +{ + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if constexpr ( is_crossed_network_type_v ) + { + std::string postfix = ntk.is_crossing( ntk.get_node( f ) ) ? ( ntk.is_second( f ) ? "_2" : "_1" ) : ""; + children.emplace_back( std::make_pair( ntk.get_fanin_negations( n )[i], node_names[f] + postfix ) ); + } + else + { + children.emplace_back( std::make_pair( ntk.is_complemented( f ), node_names[f] ) ); + } + } ); + return children; +} + +template +std::vector> +format_fanin( Ntk const& ntk, node const& n, node_map, Ntk>& node_names ) +{ + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if constexpr ( is_crossed_network_type_v ) + { + std::string postfix = ntk.is_crossing( ntk.get_node( f ) ) ? ( ntk.is_second( f ) ? "_2" : "_1" ) : ""; + children.emplace_back( std::make_pair( ntk.get_fanin_negations( n )[i], node_names[f].front() + postfix ) ); + } + else if constexpr ( has_is_multioutput_v ) + { + children.emplace_back( std::make_pair( ntk.is_complemented( f ), node_names[f][ntk.get_output_pin( f )] ) ); + } + else + { + children.emplace_back( std::make_pair( ntk.is_complemented( f ), node_names[f].front() ) ); + } + } ); + return children; +} + +template +struct verilog_writer_signal_hash +{ + uint64_t operator()( const Signal& f ) const + { + return f.data; + } +}; + +} // namespace detail + +struct write_verilog_params +{ + std::optional module_name{ std::nullopt }; + std::vector> input_names; + std::vector> output_names; + bool verbose{ false }; +}; + +/*! \brief Writes network in structural Verilog format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `is_and` + * - `is_or` + * - `is_xor` + * - `is_xor3` + * - `is_maj` + * - `is_ite` + * - `node_to_index` + * + * \param ntk Network + * \param os Output stream + */ +template +void write_verilog( Ntk const& ntk, std::ostream& os, write_verilog_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_is_or_v, "Ntk does not implement the is_or method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_xor3 method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_is_ite_v, "Ntk does not implement the is_ite method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + + assert( ntk.is_combinational() && "Network has to be combinational" ); + + lorina::verilog_writer writer( os ); + + if constexpr ( is_buffered_network_type_v ) + { + writer.on_module_begin( "buffer", { "i" }, { "o" } ); + writer.on_input( "i" ); + writer.on_output( "o" ); + writer.on_module_end(); + + writer.on_module_begin( "inverter", { "i" }, { "o" } ); + writer.on_input( "i" ); + writer.on_output( "o" ); + writer.on_module_end(); + } + if constexpr ( is_crossed_network_type_v ) + { + writer.on_module_begin( "crossing", { "i1", "i2" }, { "o1", "o2" } ); + writer.on_input( std::vector( { "i1", "i2" } ) ); + writer.on_output( std::vector( { "o1", "o2" } ) ); + writer.on_module_end(); + } + + std::vector xs, inputs; + if ( ps.input_names.empty() ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + if ( ntk.has_name( ntk.make_signal( i ) ) ) + { + xs.emplace_back( ntk.get_name( ntk.make_signal( i ) ) ); + } + else + { + xs.emplace_back( fmt::format( "x{}", index ) ); + } + } ); + } + else + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + (void)i; + xs.emplace_back( fmt::format( "x{}", index ) ); + } ); + } + inputs = xs; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.input_names ) + { + inputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + xs.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pis() ) + { + std::cerr << "[e] input names do not partition all inputs\n"; + } + } + + std::vector ys, outputs; + if ( ps.output_names.empty() ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + if ( ntk.has_output_name( index ) ) + { + ys.emplace_back( ntk.get_output_name( index ) ); + } + else + { + ys.emplace_back( fmt::format( "y{}", index ) ); + } + } ); + } + else + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + (void)o; + ys.emplace_back( fmt::format( "y{}", index ) ); + } ); + } + outputs = ys; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.output_names ) + { + outputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + ys.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pos() ) + { + std::cerr << "[e] output names do not partition all outputs\n"; + } + } + + std::vector ws; + + if constexpr ( is_buffered_network_type_v ) + { + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.fanin_size( n ) > 0 ) + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } ); + } + else + { + ntk.foreach_gate( [&]( auto const& n ) { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } ); + } + + std::string module_name = "top"; + if ( ps.module_name ) + { + module_name = *ps.module_name; + } + else + { + if constexpr ( has_get_network_name_v ) + { + if ( ntk.get_network_name().length() > 0 ) + { + module_name = ntk.get_network_name(); + } + } + } + writer.on_module_begin( module_name, inputs, outputs ); + + if ( ps.input_names.empty() ) + { + writer.on_input( xs ); + } + else + { + for ( auto const& [name, width] : ps.input_names ) + { + writer.on_input( width, name ); + } + } + if ( ps.output_names.empty() ) + { + writer.on_output( ys ); + } + else + { + for ( auto const& [name, width] : ps.output_names ) + { + writer.on_output( width, name ); + } + } + if ( !ws.empty() ) + { + writer.on_wire( ws ); + } + + node_map node_names( ntk ); + node_names[ntk.get_constant( false )] = "1'b0"; + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + node_names[ntk.get_constant( true )] = "1'b1"; + + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_names[n] = xs[i]; + } ); + + topo_view ntk_topo{ ntk }; + + ntk_topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return true; + + /* assign a name */ + node_names[n] = fmt::format( "n{}", ntk.node_to_index( n ) ); + + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) ) + { + auto const fanin = detail::format_fanin( ntk, n, node_names ); + assert( fanin.size() == 1 ); + std::vector> args; + if ( fanin[0].first ) /* input negated */ + { + args.emplace_back( std::make_pair( "i", fanin[0].second ) ); + args.emplace_back( std::make_pair( "o", node_names[n] ) ); + writer.on_module_instantiation( "inverter", {}, "inv_" + node_names[n], args ); + } + else + { + args.emplace_back( std::make_pair( "i", fanin[0].second ) ); + args.emplace_back( std::make_pair( "o", node_names[n] ) ); + writer.on_module_instantiation( "buffer", {}, "buf_" + node_names[n], args ); + } + return true; + } + } + + if constexpr ( is_crossed_network_type_v ) + { + if ( ntk.is_crossing( n ) ) + { + auto const fanin = detail::format_fanin( ntk, n, node_names ); + assert( fanin.size() == 2 ); + std::vector> args; + args.emplace_back( std::make_pair( "i1", fanin[0].second ) ); + args.emplace_back( std::make_pair( "i2", fanin[1].second ) ); + args.emplace_back( std::make_pair( "o1", node_names[n] + "_1" ) ); + args.emplace_back( std::make_pair( "o2", node_names[n] + "_2" ) ); + writer.on_module_instantiation( "crossing", {}, "cross_" + node_names[n], args ); + return true; + } + } + + if ( ntk.is_and( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "&" ); + } + else if ( ntk.is_or( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "|" ); + } + else if ( ntk.is_xor( n ) || ntk.is_xor3( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "^" ); + } + else if ( ntk.is_maj( n ) ) + { + std::array, 3> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { children[i] = f; } ); + + if ( ntk.is_constant( ntk.get_node( children[0u] ) ) ) + { + std::vector> vs; + vs.emplace_back( std::make_pair( ntk.is_complemented( children[1u] ), node_names[ntk.get_node( children[1u] )] ) ); + vs.emplace_back( std::make_pair( ntk.is_complemented( children[2u] ), node_names[ntk.get_node( children[2u] )] ) ); + + if ( ntk.is_complemented( children[0u] ) ) + { + // or + writer.on_assign( node_names[n], { vs[0u], vs[1u] }, "|" ); + } + else + { + // and + writer.on_assign( node_names[n], { vs[0u], vs[1u] }, "&" ); + } + } + else + { + writer.on_assign_maj3( node_names[n], detail::format_fanin( ntk, n, node_names ) ); + } + } + else if ( ntk.is_ite( n ) ) + { + std::array, 3> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { children[i] = f; } ); + + if ( ntk.is_constant( ntk.get_node( children[1u] ) ) ) + { + assert( children[1u] == ntk.get_constant( false ) ); + // a ? 0 : c = ~a & c + std::vector> ins; + ins.emplace_back( std::make_pair( !ntk.is_complemented( children[0u] ), node_names[ntk.get_node( children[0u] )] ) ); + ins.emplace_back( std::make_pair( ntk.is_complemented( children[2u] ), node_names[ntk.get_node( children[2u] )] ) ); + writer.on_assign( node_names[n], ins, "&" ); + } + else if ( ntk.get_node( children[1u] ) == ntk.get_node( children[2u] ) ) + { + assert( !ntk.is_complemented( children[1u] ) && ntk.is_complemented( children[2u] ) ); + // a ? b : ~b = a ^ ~b + std::vector> ins; + ins.emplace_back( std::make_pair( ntk.is_complemented( children[0u] ), node_names[ntk.get_node( children[0u] )] ) ); + ins.emplace_back( std::make_pair( ntk.is_complemented( children[2u] ), node_names[ntk.get_node( children[2u] )] ) ); + writer.on_assign( node_names[n], ins, "^" ); + } + else + { + writer.on_assign_mux21( node_names[n], detail::format_fanin( ntk, n, node_names ) ); + } + } + else + { + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "&" ); + return true; + } + } + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "|" ); + return true; + } + } + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "^" ); + return true; + } + } + if constexpr ( has_is_function_v ) + { + fmt::print( stderr, "[w] unknown node function {}\n", kitty::to_hex( ntk.node_function( n ) ) ); + } + writer.on_assign_unknown_gate( node_names[n] ); + } + + return true; + } ); + + ntk.foreach_po( [&]( auto const& f, auto i ) { + writer.on_assign_po( ys[i], std::make_pair( ntk.is_complemented( f ), node_names[f] ) ); + } ); + + writer.on_module_end(); +} + +/*! \brief Writes mapped network in structural Verilog format into output stream + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_binding` + * - `get_binding_index` + * + * \param ntk Mapped network + * \param os Output stream + * \param ps Verilog parameters + */ +template +void write_verilog_with_binding( Ntk const& ntk, std::ostream& os, write_verilog_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + static_assert( has_get_binding_index_v, "Ntk does not implement the get_binding_index method" ); + + assert( ntk.is_combinational() && "Network has to be combinational" ); + + lorina::verilog_writer writer( os ); + + std::vector xs, inputs; + if ( ps.input_names.empty() ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + if ( ntk.has_name( ntk.make_signal( i ) ) ) + { + xs.emplace_back( ntk.get_name( ntk.make_signal( i ) ) ); + } + else + { + xs.emplace_back( fmt::format( "x{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pis(); ++i ) + { + xs.emplace_back( fmt::format( "x{}", i ) ); + } + } + inputs = xs; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.input_names ) + { + inputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + xs.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pis() ) + { + std::cerr << "[e] input names do not partition all inputs\n"; + } + } + + std::vector ys, outputs; + if ( ps.output_names.empty() ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + if ( ntk.has_output_name( index ) ) + { + ys.emplace_back( ntk.get_output_name( index ) ); + } + else + { + ys.emplace_back( fmt::format( "y{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pos(); ++i ) + { + ys.emplace_back( fmt::format( "y{}", i ) ); + } + } + outputs = ys; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.output_names ) + { + outputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + ys.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pos() ) + { + std::cerr << "[e] output names do not partition all outputs\n"; + } + } + + /* compute which nodes are POs and register index */ + node_map, Ntk, std::unordered_map>> po_nodes( ntk ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + po_nodes[f].push_back( i ); + } ); + + std::vector ws; + node_map node_names( ntk ); + + /* constants */ + if ( ntk.has_binding( ntk.get_constant( false ) ) ) + { + node_names[ntk.get_constant( false )] = fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( false ) ) ); + if ( !po_nodes.has( ntk.get_constant( false ) ) ) + { + ws.emplace_back( node_names[ntk.get_constant( false )] ); + } + } + else + { + node_names[ntk.get_constant( false )] = "1'b0"; + } + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + if ( ntk.has_binding( ntk.get_constant( true ) ) ) + { + node_names[ntk.get_constant( true )] = fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( true ) ) ); + if ( !po_nodes.has( ntk.get_constant( true ) ) ) + { + ws.emplace_back( node_names[ntk.get_constant( true )] ); + } + } + else + { + node_names[ntk.get_constant( true )] = "1'b1"; + } + } + + /* add wires */ + ntk.foreach_gate( [&]( auto const& n ) { + if ( !po_nodes.has( n ) ) + { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } + } ); + + std::string module_name = "top"; + if ( ps.module_name ) + { + module_name = *ps.module_name; + } + else + { + if constexpr ( has_get_network_name_v ) + { + if ( ntk.get_network_name().length() > 0 ) + { + module_name = ntk.get_network_name(); + } + } + } + writer.on_module_begin( module_name, inputs, outputs ); + if ( ps.input_names.empty() ) + { + writer.on_input( xs ); + } + else + { + for ( auto const& [name, width] : ps.input_names ) + { + writer.on_input( width, name ); + } + } + if ( ps.output_names.empty() ) + { + writer.on_output( ys ); + } + else + { + for ( auto const& [name, width] : ps.output_names ) + { + writer.on_output( width, name ); + } + } + if ( !ws.empty() ) + { + writer.on_wire( ws ); + } + + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_names[n] = xs[i]; + } ); + + auto const& gates = ntk.get_library(); + + int nDigits = (int)std::floor( std::log10( ntk.num_gates() ) ); + unsigned int length = 0; + unsigned counter = 0; + + for ( auto const& gate : gates ) + { + length = std::max( length, static_cast( gate.name.length() ) ); + } + + topo_view ntk_topo{ ntk }; + + ntk_topo.foreach_node( [&]( auto const& n ) { + if ( po_nodes.has( n ) ) + { + node_names[n] = ys[po_nodes[n][0]]; + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + node_names[n] = fmt::format( "n{}", ntk.node_to_index( n ) ); + } + + if ( ntk.has_binding( n ) ) + { + auto const& gate = gates[ntk.get_binding_index( n )]; + std::string name = gate.name; + + int digits = counter == 0 ? 0 : (int)std::floor( std::log10( counter ) ); + auto fanin_names = detail::format_fanin( ntk, n, node_names ); + std::vector> args; + + auto i = 0; + for ( auto pair : fanin_names ) + { + args.emplace_back( std::make_pair( gate.pins[i++].name, pair.second ) ); + } + args.emplace_back( std::make_pair( gate.output_name, node_names[n] ) ); + + writer.on_module_instantiation( name.append( std::string( length - name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + + /* if node drives multiple POs, duplicate */ + if ( po_nodes.has( n ) && po_nodes[n].size() > 1 ) + { + if ( ps.verbose ) + { + std::cerr << "[i] node " << n << " driving multiple POs has been duplicated.\n"; + } + auto const& po_list = po_nodes[n]; + for ( auto i = 1u; i < po_list.size(); ++i ) + { + digits = counter == 0 ? 0 : (int)std::floor( std::log10( counter ) ); + args[args.size() - 1] = std::make_pair( gate.output_name, ys[po_list[i]] ); + + writer.on_module_instantiation( name.append( std::string( length - name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + } + } + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + std::cerr << "[e] internal node " << n << " is not mapped.\n"; + } + + return true; + } ); + + writer.on_module_end(); +} + +/*! \brief Writes mapped network in structural Verilog format into output stream + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_cell` + * - `get_cell_index` + * + * \param ntk Mapped network + * \param os Output stream + * \param ps Verilog parameters + */ +template +void write_verilog_with_cell( Ntk const& ntk, std::ostream& os, write_verilog_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_has_cell_v, "Ntk does not implement the has_cell method" ); + static_assert( has_get_cell_index_v, "Ntk does not implement the get_cell_index method" ); + + assert( ntk.is_combinational() && "Network has to be combinational" ); + + lorina::verilog_writer writer( os ); + + std::vector xs, inputs; + if ( ps.input_names.empty() ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + if ( ntk.has_name( ntk.make_signal( i ) ) ) + { + xs.emplace_back( ntk.get_name( ntk.make_signal( i ) ) ); + } + else + { + xs.emplace_back( fmt::format( "x{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pis(); ++i ) + { + xs.emplace_back( fmt::format( "x{}", i ) ); + } + } + inputs = xs; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.input_names ) + { + inputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + xs.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pis() ) + { + std::cerr << "[e] input names do not partition all inputs\n"; + } + } + + std::vector ys, outputs; + if ( ps.output_names.empty() ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + if ( ntk.has_output_name( index ) ) + { + ys.emplace_back( ntk.get_output_name( index ) ); + } + else + { + ys.emplace_back( fmt::format( "y{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pos(); ++i ) + { + ys.emplace_back( fmt::format( "y{}", i ) ); + } + } + outputs = ys; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.output_names ) + { + outputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + ys.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pos() ) + { + std::cerr << "[e] output names do not partition all outputs\n"; + } + } + + /* compute which nodes are POs and register index */ + uint32_t additional_buffers = 0; + std::unordered_map, detail::verilog_writer_signal_hash> po_nodes; + ntk.foreach_po( [&]( auto const& f, auto i ) { + po_nodes[f ^ ntk.is_complemented( f )].push_back( i ); + additional_buffers += po_nodes[f ^ ntk.is_complemented( f )].size() > 1 ? 1 : 0; + } ); + + std::vector ws; + node_map, Ntk> node_names( ntk ); + + /* constants */ + if ( ntk.has_cell( ntk.get_node( ntk.get_constant( false ) ) ) ) + { + if ( po_nodes.find( ntk.get_constant( false ) ) == po_nodes.end() ) + { + node_names[ntk.get_node( ntk.get_constant( false ) )].push_back( fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( false ) ) ) ); + ws.emplace_back( node_names[ntk.get_constant( false )].front() ); + } + } + else + { + node_names[ntk.get_node( ntk.get_constant( false ) )].push_back( "1'b0" ); + } + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + if ( ntk.has_cell( ntk.get_node( ntk.get_constant( true ) ) ) ) + { + if ( po_nodes.find( ntk.get_constant( true ) ) == po_nodes.end() ) + { + node_names[ntk.get_node( ntk.get_constant( true ) )].push_back( fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( true ) ) ) ); + ws.emplace_back( node_names[ntk.get_constant( true )].front() ); + } + } + else + { + node_names[ntk.get_constant( true )].push_back( "1'b1" ); + } + } + + /* add wires */ + ntk.foreach_gate( [&]( auto const& n ) { + if constexpr ( has_is_multioutput_v ) + { + /* create wire for each individual output */ + if ( !ntk.is_multioutput( n ) && po_nodes.find( ntk.make_signal( n ) ) == po_nodes.end() ) + { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + return; + } + + for ( uint32_t i = 0; i < ntk.num_outputs( n ); ++i ) + { + if ( po_nodes.find( ntk.make_signal( n, i ) ) == po_nodes.end() ) + { + ws.emplace_back( fmt::format( "n{}_{}", ntk.node_to_index( n ), i ) ); + } + } + + return; + } + + if ( po_nodes.find( ntk.make_signal( n ) ) == po_nodes.end() ) + { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } + } ); + + std::string module_name = "top"; + if ( ps.module_name ) + { + module_name = *ps.module_name; + } + else + { + if constexpr ( has_get_network_name_v ) + { + if ( ntk.get_network_name().length() > 0 ) + { + module_name = ntk.get_network_name(); + } + } + } + writer.on_module_begin( module_name, inputs, outputs ); + if ( ps.input_names.empty() ) + { + writer.on_input( xs ); + } + else + { + for ( auto const& [name, width] : ps.input_names ) + { + writer.on_input( width, name ); + } + } + if ( ps.output_names.empty() ) + { + writer.on_output( ys ); + } + else + { + for ( auto const& [name, width] : ps.output_names ) + { + writer.on_output( width, name ); + } + } + if ( !ws.empty() ) + { + writer.on_wire( ws ); + } + + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_names[n].push_back( xs[i] ); + } ); + + auto const& cells = ntk.get_library(); + + /* get buffer */ + uint32_t buf_id = UINT32_MAX; + double buf_area = std::numeric_limits::max(); + for ( uint32_t i = 0; i < cells.size(); ++i ) + { + auto const& g = cells[i].gates.front(); + if ( cells[i].gates.size() > 1 || g.num_vars != 1 ) + continue; + if ( g.function._bits[0] != 0x2 ) + continue; + + if ( buf_id == UINT32_MAX || g.area < buf_area ) + { + buf_id = i; + buf_area = g.area; + } + } + + int nDigits = (int)std::floor( std::log10( ntk.num_gates() + additional_buffers ) ); + unsigned int length = 0; + unsigned counter = 0; + + for ( auto const& cell : cells ) + { + length = std::max( length, static_cast( cell.name.length() ) ); + } + + topo_view ntk_topo{ ntk }; + + ntk_topo.foreach_node( [&]( auto const& n ) { + /* load names of n */ + if constexpr ( has_is_multioutput_v ) + { + /* create wire for each individual output */ + if ( !ntk.is_multioutput( n ) ) + { + if ( auto el = po_nodes.find( ntk.make_signal( n ) ); el != po_nodes.end() ) + { + node_names[n].emplace_back( ys[el->second.front()] ); + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + node_names[n].emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } + } + else + { + for ( uint32_t i = 0; i < ntk.num_outputs( n ); ++i ) + { + if ( auto el = po_nodes.find( ntk.make_signal( n, i ) ); el != po_nodes.end() ) + { + node_names[n].emplace_back( ys[el->second.front()] ); + } + else + { + node_names[n].emplace_back( fmt::format( "n{}_{}", ntk.node_to_index( n ), i ) ); + } + } + } + } + else + { + if ( auto el = po_nodes.find( ntk.make_signal( n ) ); el != po_nodes.end() ) + { + node_names[n] = ys[el->second.front()]; + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + node_names[n] = fmt::format( "n{}", ntk.node_to_index( n ) ); + } + } + + if ( ntk.has_cell( n ) ) + { + auto const& cell = cells[ntk.get_cell_index( n )]; + std::string name = cell.name; + + int digits = counter == 0 ? 0 : (int)std::floor( std::log10( counter ) ); + auto fanin_names = detail::format_fanin( ntk, n, node_names ); + std::vector> args; + + auto i = 0; + for ( auto pair : fanin_names ) + { + args.emplace_back( std::make_pair( cell.gates[0].pins[i++].name, pair.second ) ); + } + + assert( cell.gates.size() == node_names[n].size() ); + + i = 0; + for ( auto const& ogate : cell.gates ) + { + args.emplace_back( std::make_pair( ogate.output_name, node_names[n][i++] ) ); + } + + writer.on_module_instantiation( name.append( std::string( length - name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + + /* if node drives multiple POs, buffer */ + if constexpr ( has_is_multioutput_v ) + { + i = 0; + for ( i = 0; i < ntk.num_outputs( n ); ++i ) + { + if ( auto el = po_nodes.find( ntk.make_signal( n, i ) ); el != po_nodes.end() && el->second.size() > 1 ) + { + if ( buf_id == UINT32_MAX ) + { + std::cerr << "[e] Error: cell library does not contain a buffer cell\n"; + return false; + } + + if ( ps.verbose ) + { + std::cerr << "[i] Buffering node " << n << " driving multiple POs.\n"; + } + + gate const& g = cells[buf_id].gates.front(); + std::string buf_name = g.name; + auto const& po_list = el->second; + args.clear(); + args.emplace_back( std::make_pair( g.pins.front().name, node_names[n].at( i ) ) ); + args.emplace_back( std::make_pair( "", "" ) ); + for ( uint32_t j = 1u; j < po_list.size(); ++j ) + { + digits = counter == (int)std::floor( std::log10( counter ) ); + args[args.size() - 1] = std::make_pair( g.output_name, ys[po_list[j]] ); + + writer.on_module_instantiation( buf_name.append( std::string( length - buf_name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + } + } + } + } + else + { + if ( auto el = po_nodes.find( ntk.make_signal( n ) ); el != po_nodes.end() && el->second.size() > 1 ) + { + if ( buf_id == UINT32_MAX ) + { + std::cerr << "[e] Error: cell library does not contain a buffer cell\n"; + return false; + } + + std::cerr << "[i] Buffering node " << n << " driving multiple POs.\n"; + + gate const& g = cells[buf_id].gates.front(); + std::string buf_name = g.name; + auto const& po_list = el->second; + args.clear(); + args.emplace_back( std::make_pair( g.pins.front().name, node_names[n].front() ) ); + args.emplace_back( std::make_pair( "", "" ) ); + for ( i = 1u; i < po_list.size(); ++i ) + { + digits = counter == (int)std::floor( std::log10( counter ) ); + args[args.size() - 1] = std::make_pair( g.output_name, ys[po_list[i]] ); + + writer.on_module_instantiation( buf_name.append( std::string( length - buf_name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + } + } + } + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + std::cerr << "[e] internal node " << n << " is not mapped.\n"; + } + + return true; + } ); + + writer.on_module_end(); +} + +/*! \brief Writes network in structural Verilog format into a file + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `is_and` + * - `is_or` + * - `is_xor` + * - `is_xor3` + * - `is_maj` + * - `node_to_index` + * + * \param ntk Network + * \param filename Filename + */ +template +void write_verilog( Ntk const& ntk, std::string const& filename, write_verilog_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_verilog( ntk, os, ps ); + os.close(); +} + +/*! \brief Writes mapped network in structural Verilog format into a file + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_binding` + * - `get_binding_index` + * + * \param ntk Network (binding_view) + * \param filename Filename + */ +template +void write_verilog_with_binding( Ntk const& ntk, std::string const& filename, write_verilog_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_verilog_with_binding( ntk, os, ps ); + os.close(); +} + +/*! \brief Writes mapped network in structural Verilog format into a file + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_cell` + * - `get_cell_index` + * + * \param ntk Network (cell_view) + * \param filename Filename + */ +template +void write_verilog_with_cell( Ntk const& ntk, std::string const& filename, write_verilog_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_verilog_with_cell( ntk, os, ps ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/mockturtle.hpp b/third-party/mockturtle/include/mockturtle/mockturtle.hpp new file mode 100644 index 00000000000..2a03d1d32aa --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/mockturtle.hpp @@ -0,0 +1,218 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mockturtle.hpp + \brief Main header file for mockturtle +*/ + +#pragma once + +#include "mockturtle/algorithms/aig_resub.hpp" +#include "mockturtle/algorithms/akers_synthesis.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_assumptions.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_db.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp" +#include "mockturtle/algorithms/aqfp/buffer_insertion.hpp" +#include "mockturtle/algorithms/aqfp/buffer_verification.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag_cost.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag_gen.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag_util.hpp" +#include "mockturtle/algorithms/aqfp/detail/db_builder.hpp" +#include "mockturtle/algorithms/aqfp/detail/db_utils.hpp" +#include "mockturtle/algorithms/aqfp/detail/npn_cache.hpp" +#include "mockturtle/algorithms/aqfp/detail/partial_dag.hpp" +#include "mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp" +#include "mockturtle/algorithms/aqfp/mig_resub_splitters.hpp" +#include "mockturtle/algorithms/balancing.hpp" +#include "mockturtle/algorithms/balancing/esop_balancing.hpp" +#include "mockturtle/algorithms/balancing/sop_balancing.hpp" +#include "mockturtle/algorithms/balancing/utils.hpp" +#include "mockturtle/algorithms/bi_decomposition.hpp" +#include "mockturtle/algorithms/cell_window.hpp" +#include "mockturtle/algorithms/circuit_validator.hpp" +#include "mockturtle/algorithms/cleanup.hpp" +#include "mockturtle/algorithms/cnf.hpp" +#include "mockturtle/algorithms/collapse_mapped.hpp" +#include "mockturtle/algorithms/cover_to_graph.hpp" +#include "mockturtle/algorithms/cut_enumeration.hpp" +#include "mockturtle/algorithms/cut_enumeration/cnf_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/gia_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/mf_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/spectr_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp" +#include "mockturtle/algorithms/cut_rewriting.hpp" +#include "mockturtle/algorithms/decomposition.hpp" +#include "mockturtle/algorithms/detail/database_generator.hpp" +#include "mockturtle/algorithms/detail/mffc_utils.hpp" +#include "mockturtle/algorithms/detail/minmc_xags.hpp" +#include "mockturtle/algorithms/detail/resub_utils.hpp" +#include "mockturtle/algorithms/detail/switching_activity.hpp" +#include "mockturtle/algorithms/dont_cares.hpp" +#include "mockturtle/algorithms/dsd_decomposition.hpp" +#include "mockturtle/algorithms/equivalence_checking.hpp" +#include "mockturtle/algorithms/equivalence_classes.hpp" +#include "mockturtle/algorithms/exact_mc_synthesis.hpp" +#include "mockturtle/algorithms/exorcism.hpp" +#include "mockturtle/algorithms/experimental/boolean_optimization.hpp" +#include "mockturtle/algorithms/experimental/cost_generic_resub.hpp" +#include "mockturtle/algorithms/experimental/cost_resyn.hpp" +#include "mockturtle/algorithms/experimental/sim_resub.hpp" +#include "mockturtle/algorithms/experimental/window_resub.hpp" +#include "mockturtle/algorithms/extract_linear.hpp" +#include "mockturtle/algorithms/functional_reduction.hpp" +#include "mockturtle/algorithms/gates_to_nodes.hpp" +#include "mockturtle/algorithms/klut_to_graph.hpp" +#include "mockturtle/algorithms/linear_resynthesis.hpp" +#include "mockturtle/algorithms/lut_mapping.hpp" +#include "mockturtle/algorithms/mapper.hpp" +#include "mockturtle/algorithms/mig_algebraic_rewriting.hpp" +#include "mockturtle/algorithms/mig_resub.hpp" +#include "mockturtle/algorithms/miter.hpp" +#include "mockturtle/algorithms/network_fuzz_tester.hpp" +#include "mockturtle/algorithms/node_resynthesis.hpp" +#include "mockturtle/algorithms/node_resynthesis/akers.hpp" +#include "mockturtle/algorithms/node_resynthesis/bidecomposition.hpp" +#include "mockturtle/algorithms/node_resynthesis/cached.hpp" +#include "mockturtle/algorithms/node_resynthesis/composed.hpp" +#include "mockturtle/algorithms/node_resynthesis/davio.hpp" +#include "mockturtle/algorithms/node_resynthesis/direct.hpp" +#include "mockturtle/algorithms/node_resynthesis/dsd.hpp" +#include "mockturtle/algorithms/node_resynthesis/exact.hpp" +#include "mockturtle/algorithms/node_resynthesis/mig_npn.hpp" +#include "mockturtle/algorithms/node_resynthesis/null.hpp" +#include "mockturtle/algorithms/node_resynthesis/shannon.hpp" +#include "mockturtle/algorithms/node_resynthesis/traits.hpp" +#include "mockturtle/algorithms/node_resynthesis/xag_minmc.hpp" +#include "mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp" +#include "mockturtle/algorithms/node_resynthesis/xag_npn.hpp" +#include "mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp" +#include "mockturtle/algorithms/node_resynthesis/xmg_npn.hpp" +#include "mockturtle/algorithms/pattern_generation.hpp" +#include "mockturtle/algorithms/reconv_cut.hpp" +#include "mockturtle/algorithms/refactoring.hpp" +#include "mockturtle/algorithms/resubstitution.hpp" +#include "mockturtle/algorithms/resyn_engines/aig_enumerative.hpp" +#include "mockturtle/algorithms/resyn_engines/mig_enumerative.hpp" +#include "mockturtle/algorithms/resyn_engines/mig_resyn.hpp" +#include "mockturtle/algorithms/resyn_engines/xag_resyn.hpp" +#include "mockturtle/algorithms/satlut_mapping.hpp" +#include "mockturtle/algorithms/sim_resub.hpp" +#include "mockturtle/algorithms/simulation.hpp" +#include "mockturtle/algorithms/testcase_minimizer.hpp" +#include "mockturtle/algorithms/window_rewriting.hpp" +#include "mockturtle/algorithms/xag_optimization.hpp" +#include "mockturtle/algorithms/xag_resub_withDC.hpp" +#include "mockturtle/algorithms/xmg_algebraic_rewriting.hpp" +#include "mockturtle/algorithms/xmg_optimization.hpp" +#include "mockturtle/algorithms/xmg_resub.hpp" +#include "mockturtle/generators/arithmetic.hpp" +#include "mockturtle/generators/control.hpp" +#include "mockturtle/generators/legacy.hpp" +#include "mockturtle/generators/majority.hpp" +#include "mockturtle/generators/majority_n.hpp" +#include "mockturtle/generators/modular_arithmetic.hpp" +#include "mockturtle/generators/random_network.hpp" +#include "mockturtle/generators/self_dualize.hpp" +#include "mockturtle/generators/sorting.hpp" +#include "mockturtle/io/aiger_reader.hpp" +#include "mockturtle/io/bench_reader.hpp" +#include "mockturtle/io/blif_reader.hpp" +#include "mockturtle/io/bristol_reader.hpp" +#include "mockturtle/io/dimacs_reader.hpp" +#include "mockturtle/io/genlib_reader.hpp" +#include "mockturtle/io/pla_reader.hpp" +#include "mockturtle/io/serialize.hpp" +#include "mockturtle/io/super_reader.hpp" +#include "mockturtle/io/verilog_reader.hpp" +#include "mockturtle/io/write_aiger.hpp" +#include "mockturtle/io/write_bench.hpp" +#include "mockturtle/io/write_blif.hpp" +#include "mockturtle/io/write_dimacs.hpp" +#include "mockturtle/io/write_dot.hpp" +#include "mockturtle/io/write_patterns.hpp" +#include "mockturtle/io/write_verilog.hpp" +#include "mockturtle/networks/abstract_xag.hpp" +#include "mockturtle/networks/aig.hpp" +#include "mockturtle/networks/aqfp.hpp" +#include "mockturtle/networks/buffered.hpp" +#include "mockturtle/networks/cover.hpp" +#include "mockturtle/networks/detail/foreach.hpp" +#include "mockturtle/networks/events.hpp" +#include "mockturtle/networks/klut.hpp" +#include "mockturtle/networks/mig.hpp" +#include "mockturtle/networks/muxig.hpp" +#include "mockturtle/networks/sequential.hpp" +#include "mockturtle/networks/storage.hpp" +#include "mockturtle/networks/tig.hpp" +#include "mockturtle/networks/xag.hpp" +#include "mockturtle/networks/xmg.hpp" +#include "mockturtle/networks/crossed.hpp" +#include "mockturtle/properties/aqfpcost.hpp" +#include "mockturtle/properties/mccost.hpp" +#include "mockturtle/properties/migcost.hpp" +#include "mockturtle/properties/xmgcost.hpp" +#include "mockturtle/traits.hpp" +#include "mockturtle/utils/algorithm.hpp" +#include "mockturtle/utils/cost_functions.hpp" +#include "mockturtle/utils/cuts.hpp" +#include "mockturtle/utils/debugging_utils.hpp" +#include "mockturtle/utils/hash_functions.hpp" +#include "mockturtle/utils/include/percy.hpp" +#include "mockturtle/utils/index_list/index_list.hpp" +#include "mockturtle/utils/json_utils.hpp" +#include "mockturtle/utils/mixed_radix.hpp" +#include "mockturtle/utils/name_utils.hpp" +#include "mockturtle/utils/network_cache.hpp" +#include "mockturtle/utils/network_utils.hpp" +#include "mockturtle/utils/node_map.hpp" +#include "mockturtle/utils/progress_bar.hpp" +#include "mockturtle/utils/recursive_cost_functions.hpp" +#include "mockturtle/utils/stopwatch.hpp" +#include "mockturtle/utils/string_utils.hpp" +#include "mockturtle/utils/super_utils.hpp" +#include "mockturtle/utils/tech_library.hpp" +#include "mockturtle/utils/truth_table_cache.hpp" +#include "mockturtle/utils/truth_table_utils.hpp" +#include "mockturtle/utils/window_utils.hpp" +#include "mockturtle/views/binding_view.hpp" +#include "mockturtle/views/cnf_view.hpp" +#include "mockturtle/views/color_view.hpp" +#include "mockturtle/views/cost_view.hpp" +#include "mockturtle/views/cut_view.hpp" +#include "mockturtle/views/depth_view.hpp" +#include "mockturtle/views/fanout_limit_view.hpp" +#include "mockturtle/views/fanout_view.hpp" +#include "mockturtle/views/immutable_view.hpp" +#include "mockturtle/views/mapping_view.hpp" +#include "mockturtle/views/mffc_view.hpp" +#include "mockturtle/views/names_view.hpp" +#include "mockturtle/views/topo_view.hpp" +#include "mockturtle/views/window_view.hpp" +#include "mockturtle/views/rank_view.hpp" diff --git a/third-party/mockturtle/include/mockturtle/networks/abstract_xag.hpp b/third-party/mockturtle/include/mockturtle/networks/abstract_xag.hpp new file mode 100644 index 00000000000..632f10952cc --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/abstract_xag.hpp @@ -0,0 +1,892 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file abstract_xag.hpp + \brief Abstract XAG logic network implementation + + This network type is (for now) only meant for experimental use cases. + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" + +namespace mockturtle +{ + +struct abstract_xag_storage +{ + struct node_type + { + uint32_t* fanin{}; + uint32_t fanin_size{}; + uint32_t fanout_size{}; + uint32_t value{}; + uint32_t visited{}; + uint32_t level{}; + }; + + abstract_xag_storage() + { + /* constant 0 node */ + nodes.emplace_back(); + + // nodes.reserve( 10000u ); + } + + ~abstract_xag_storage() + { + for ( auto& n : nodes ) + { + if ( n.fanin ) + { + delete[] n.fanin; + } + } + } + + struct abstract_xag_node_eq + { + bool operator()( abstract_xag_storage::node_type const& a, abstract_xag_storage::node_type const& b ) const + { + return a.fanin_size == b.fanin_size && std::equal( a.fanin, a.fanin + a.fanin_size, b.fanin ); + } + }; + + struct abstract_xag_node_hash + { + uint64_t operator()( abstract_xag_storage::node_type const& n ) const + { + return std::accumulate( n.fanin, n.fanin + n.fanin_size, UINT64_C( 0 ), [&]( auto accu, auto c ) { return accu + std::hash{}( c ); } ); + } + }; + + std::vector nodes; + std::vector children; + std::vector inputs; + std::vector> outputs; + phmap::flat_hash_map hash; + + uint32_t trav_id = 0u; + uint32_t depth = 0u; +}; + +class abstract_xag_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1u; + static constexpr auto max_fanin_size = std::numeric_limits::max(); + + using base_type = abstract_xag_network; + using storage_type = abstract_xag_storage; + using storage = std::shared_ptr; + using node = uint32_t; + + struct signal + { + signal() = default; + signal( uint32_t index, bool complement = false ) : index( index ), complement( complement ) {} + + uint32_t index; + bool complement; + + signal operator!() const + { + return { index, !complement }; + } + + signal operator+() const + { + return { index, false }; + } + + signal operator-() const + { + return { index, true }; + } + + signal operator^( bool complement ) const + { + return { index, this->complement != complement }; + } + + bool operator==( signal const& other ) const + { + return index == other.index && complement == other.complement; + } + + bool operator!=( signal const& other ) const + { + return index != other.index || complement != other.complement; + } + + bool operator<( signal const& other ) const + { + return index < other.index || ( index == other.index && !complement && other.complement ); + } + }; + + abstract_xag_network() + : _storage( std::make_shared() ) + { + } + + abstract_xag_network( std::shared_ptr storage ) + : _storage( storage ) + { + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, value }; + } + + signal create_pi() + { + const auto index = static_cast( _storage->nodes.size() ); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].fanout_size++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + _storage->depth = std::max( _storage->depth, level( f.index ) ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_pi( node const& n ) const + { + return n > 0 && _storage->nodes[n].fanin_size == 0u; + } + + bool is_ci( node const& n ) const + { + return n > 0 && _storage->nodes[n].fanin_size == 0u; + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary functions + signal _create_node( std::vector const& fanin, uint32_t level_offset ) + { + storage::element_type::node_type node; + node.fanin = new uint32_t[fanin.size()]; + node.fanin_size = fanin.size(); + std::copy( fanin.begin(), fanin.end(), node.fanin ); + + /* structural hashing */ + if ( const auto it = _storage->hash.find( node ); it != _storage->hash.end() ) + { + delete[] node.fanin; + return { it->second, 0 }; + } + + const auto index = static_cast( _storage->nodes.size() ); + _storage->nodes.push_back( node ); + _storage->hash.emplace( node, index ); + + /* increase ref-count to children */ + uint32_t _level = 0u; + for ( auto const& f : fanin ) + { + _storage->nodes[f].fanout_size++; + _level = std::max( _level, level( f ) ); + } + _storage->nodes[index].level = _level + level_offset; + + return { index, 0u }; + } + + signal create_and( signal a, signal b ) + { + /* order inputs a > b it is a AND */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + /* trivial cases */ + if ( a.index == b.index ) + { + return a.complement == b.complement ? a : get_constant( false ); + } + else if ( b.index == 0 ) + { + return b.complement == false ? get_constant( false ) : a; + } + /* constant propagation */ + else if ( a.complement && b.complement ) + { + return !create_nary_xor( { +a, +b, create_and( +a, +b ) } ); + } + else if ( a.complement ) + { + return create_xor( create_and( +a, b ), b ); + } + else if ( b.complement ) + { + return create_xor( create_and( a, +b ), a ); + } + + /* subset resolution */ + const auto& anode = _storage->nodes[a.index]; + const auto& bnode = _storage->nodes[b.index]; + + const auto a_begin = is_nary_xor( a.index ) ? anode.fanin : &a.index; + const auto a_end = is_nary_xor( a.index ) ? anode.fanin + anode.fanin_size : &a.index + 1; + const auto b_begin = is_nary_xor( b.index ) ? bnode.fanin : &b.index; + const auto b_end = is_nary_xor( b.index ) ? bnode.fanin + bnode.fanin_size : &b.index + 1; + + if ( std::includes( a_begin, a_end, b_begin, b_end ) ) + { + std::vector set_anew; + std::set_difference( a_begin, a_end, b_begin, b_end, std::back_inserter( set_anew ) ); + return create_xor( b, create_and( b, _create_nary_xor( set_anew ) ) ); + } + else if ( std::includes( b_begin, b_end, a_begin, a_end ) ) + { + std::vector set_bnew; + std::set_difference( b_begin, b_end, a_begin, a_end, std::back_inserter( set_bnew ) ); + return create_xor( a, create_and( a, _create_nary_xor( set_bnew ) ) ); + } + + return _create_node( std::vector{ { a.index, b.index } }, 1u ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return create_and( !a, !b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + return create_nary_xor( { a, b } ); + } + + signal create_xnor( signal const& a, signal const& b ) + { + return !create_xor( a, b ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_xor( create_and( !cond, create_xor( f_then, f_else ) ), f_then ) ^ f_compl; + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + auto c1 = create_xor( a, b ); + auto c2 = create_xor( a, c ); + auto c3 = create_and( c1, c2 ); + return create_xor( a, c3 ); + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_nary_xor( { a, b, c } ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal _create_nary_xor( std::vector const& fs ) + { + std::vector _fs; + + const auto merge_one = [&]( uint32_t f ) { + const auto it = std::lower_bound( _fs.begin(), _fs.end(), f ); + if ( it != _fs.end() && *it == f ) + { + _fs.erase( it ); + } + else + { + _fs.insert( it, f ); + } + }; + + const auto merge_many = [&]( uint32_t* begin, uint32_t* end ) { + std::vector tmp; + std::set_symmetric_difference( _fs.begin(), _fs.end(), begin, end, std::back_inserter( tmp ) ); + _fs = std::move( tmp ); + }; + + for ( auto const& f : fs ) + { + auto const& node = _storage->nodes[f]; + if ( node.fanin_size == 0u ) + { + merge_one( f ); + } + else if ( node.fanin[0] > node.fanin[1] ) + { + merge_one( f ); + } + else + { + merge_many( node.fanin, node.fanin + node.fanin_size ); + } + } + + if ( _fs.empty() ) + { + return get_constant( false ); + } + else if ( _fs.size() == 1u ) + { + return { _fs.front(), false }; + } + else + { + return _create_node( _fs, 0u ); + } + } + + signal create_nary_xor( std::vector const& fs ) + { + std::vector _fs; + + const auto merge_one = [&]( uint32_t f ) { + const auto it = std::lower_bound( _fs.begin(), _fs.end(), f ); + if ( it != _fs.end() && *it == f ) + { + _fs.erase( it ); + } + else + { + _fs.insert( it, f ); + } + }; + + const auto merge_many = [&]( uint32_t* begin, uint32_t* end ) { + std::vector tmp; + std::set_symmetric_difference( _fs.begin(), _fs.end(), begin, end, std::back_inserter( tmp ) ); + _fs = std::move( tmp ); + }; + + bool complement{ false }; + for ( auto const& f : fs ) + { + complement ^= f.complement; + auto const& node = _storage->nodes[f.index]; + if ( f.index == 0 ) + { + // do nothing + } + else if ( node.fanin_size == 0u ) + { + merge_one( f.index ); + } + else if ( node.fanin[0] > node.fanin[1] ) + { + merge_one( f.index ); + } + else + { + merge_many( node.fanin, node.fanin + node.fanin_size ); + } + } + + if ( _fs.empty() ) + { + return get_constant( complement ); + } + else if ( _fs.size() == 1u ) + { + return { _fs.front(), complement }; + } + else + { + return _create_node( _fs, 0u ) ^ complement; + } + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( abstract_xag_network const& other, node const& source, std::vector const& children ) + { + if ( other.is_and( source ) ) + { + return create_and( children[0u], children[1u] ); + } + else + { + return create_nary_xor( children ); + } + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return { n, false }; + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return n; + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return _storage->inputs[index]; + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + const auto po = _storage->outputs[index]; + return { po.first, po.second }; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using Iterator = decltype( _storage->outputs.begin() ); + using ElementType = signal; + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto const& po ) -> signal { return { po.first, po.second }; }, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using Iterator = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto const& po ) -> signal { return { po.first, po.second }; }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_pi( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_pi( n ) ) + return; + + const auto& node = _storage->nodes[n]; + detail::foreach_element_transform( + node.fanin, node.fanin + node.fanin_size, []( auto c ) -> signal { return { c, false }; }, fn ); + } +#pragma endregion + +#pragma region Structural properties + uint32_t size() const + { + return num_gates() + num_pis() + 1u; + } + + uint32_t num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + uint32_t num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + return _storage->nodes[n].fanin_size; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].fanout_size; + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].fanout_size++; + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].fanout_size; + } + + uint32_t depth() const + { + return _storage->depth; + } + + uint32_t level( node const& n ) const + { + return _storage->nodes[n].level; + } + + bool is_and( node const& n ) const + { + const auto& node = _storage->nodes[n]; + return node.fanin_size == 2 && ( node.fanin[0] > node.fanin[1] ); + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + const auto& node = _storage->nodes[n]; + return node.fanin_size != 0u && ( node.fanin[0] < node.fanin[1] ); + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( n != 0 && !is_pi( n ) ); + + const auto& node = _storage->nodes[n]; + + if ( node.fanin[0] > node.fanin[1] ) + { + auto v1 = *begin++; + auto v2 = *begin++; + return v1 && v2; + } + else + { + auto v = *begin++; + while ( begin != end ) + { + v ^= *begin++; + } + return v; + } + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_pi( n ) ); + + const auto& node = _storage->nodes[n]; + + if ( node.fanin[0] > node.fanin[1] ) + { + auto v1 = *begin++; + auto v2 = *begin++; + return v1 & v2; + } + else + { + auto v = *begin++; + while ( begin != end ) + { + v ^= *begin++; + } + return v; + } + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.value = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].value; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].value = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].value++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].value; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.visited = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].visited; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].visited = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +public: + storage _storage; +}; + +} // namespace mockturtle + +template<> +struct fmt::formatter +{ + constexpr auto parse( format_parse_context& ctx ) + { + auto it = ctx.begin(), end = ctx.end(); + if ( it != end && *it != '}' ) + { + throw format_error( "invalid format" ); + } + return it; + } + + template + auto format( const mockturtle::abstract_xag_network::signal& f, FormatContext& ctx ) + { + return format_to( ctx.out(), "{}{}", f.complement ? "~" : "", f.index ); + } +}; + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::abstract_xag_network::signal const& s ) const noexcept + { + uint64_t k = s.index; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + k ^= s.complement; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/aig.hpp b/third-party/mockturtle/include/mockturtle/networks/aig.hpp new file mode 100644 index 00000000000..c8c4643a921 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/aig.hpp @@ -0,0 +1,1265 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aig.hpp + \brief AIG logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Hash function for AIGs (from ABC) */ +template +struct aig_hash +{ + uint64_t operator()( Node const& n ) const + { + uint64_t seed = -2011; + seed += n.children[0].index * 7937; + seed += n.children[1].index * 2971; + seed += n.children[0].weight * 911; + seed += n.children[1].weight * 353; + return seed; + } +}; + +/*! \brief AIG storage container + + AIGs have nodes with fan-in 2. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + `data[0].h2`: Application-specific value + `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) +*/ +using aig_storage = storage, + empty_storage_data, + aig_hash>>; + +class aig_network +{ +public: +#pragma region Types and constructors + static constexpr bool is_aig_network_type = true; + static constexpr auto min_fanin_size = 2u; + static constexpr auto max_fanin_size = 2u; + + using base_type = aig_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( aig_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator aig_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( aig_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + aig_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + aig_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + aig_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0].data = node.children[1].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = _storage->outputs.size(); + _storage->outputs.emplace_back( f.index, f.complement ); + return static_cast( po_index ); + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement ? b : get_constant( false ); + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return { it->second, 0 }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return create_and( !a, !b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_xnor( signal const& a, signal const& b ) + { + return !create_xor( a, b ); + } +#pragma endregion + +#pragma region Createy ternary functions + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + return create_or( create_and( a, b ), create_and( c, !create_and( !a, !b ) ) ); + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_xor( create_xor( a, b ), c ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 2u ); + return create_and( children[0u], children[1u] ); + } +#pragma endregion + +#pragma region Has node + std::optional has_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return a.complement == b.complement ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement == false ? get_constant( false ) : b; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, 0 ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return std::nullopt; + } + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + // check for trivial cases? + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + return std::make_pair( n, diff_pol ? get_constant( false ) : child1 ); + } + else if ( child0.index == 0 ) /* constant child */ + { + return std::make_pair( n, child0.complement ? child1 : get_constant( false ) ); + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return; + } + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into the hash table + node.children[0] = child0; + node.children[1] = child1; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + /* delete the node (ignoring its current fanout_size) */ + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + /* if the node has been deleted, then deref fanout_size of + fanins and try to take them out if their fanout_size become 0 */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + /* recursively reset old node */ + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } + + void substitute_nodes( std::list> substitutions ) + { + auto clean_substitutions = [&]( node const& n ) { + substitutions.erase( std::remove_if( std::begin( substitutions ), std::end( substitutions ), + [&]( auto const& s ) { + if ( s.first == n ) + { + node const nn = get_node( s.second ); + if ( is_dead( nn ) ) + return true; + + /* deref fanout_size of the node */ + if ( fanout_size( nn ) > 0 ) + { + decr_fanout_size( nn ); + } + /* remove the node if it's fanout_size becomes 0 */ + if ( fanout_size( nn ) == 0 ) + { + take_out_node( nn ); + } + /* remove substitution from list */ + return true; + } + return false; /* keep */ + } ), + std::end( substitutions ) ); + }; + + /* register event to delete substitutions if their right-hand side + nodes get deleted */ + auto clean_sub_event = _events->register_delete_event( clean_substitutions ); + + /* increment fanout_size of all signals to be used in + substitutions to ensure that they will not be deleted */ + for ( const auto& s : substitutions ) + { + incr_fanout_size( get_node( s.second ) ); + } + + while ( !substitutions.empty() ) + { + auto const [old_node, new_signal] = substitutions.front(); + substitutions.pop_front(); + + for ( auto index = 1u; index < _storage->nodes.size(); ++index ) + { + /* skip CIs and dead nodes */ + if ( is_ci( index ) || is_dead( index ) ) + continue; + + /* skip nodes that will be deleted */ + if ( std::find_if( std::begin( substitutions ), std::end( substitutions ), + [&index]( auto s ) { return s.first == index; } ) != std::end( substitutions ) ) + continue; + + /* replace in node */ + if ( const auto repl = replace_in_node( index, old_node, new_signal ); repl ) + { + incr_fanout_size( get_node( repl->second ) ); + substitutions.emplace_back( *repl ); + } + } + + /* replace in outputs */ + replace_in_outputs( old_node, new_signal ); + + /* replace in substitutions */ + for ( auto& s : substitutions ) + { + if ( get_node( s.second ) == old_node ) + { + s.second = is_complemented( s.second ) ? !new_signal : new_signal; + incr_fanout_size( get_node( new_signal ) ); + } + } + + /* finally remove the node: note that we never decrement the + fanout_size of the old_node. instead, we remove the node and + reset its fanout_size to 0 knowing that it must be 0 after + substituting all references. */ + assert( !is_dead( old_node ) ); + take_out_node( old_node ); + + /* decrement fanout_size when released from substitution list */ + decr_fanout_size( get_node( new_signal ) ); + } + + _events->release_delete_event( clean_sub_event ); + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 2; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + return n > 0 && !is_ci( n ); + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + (void)n; + kitty::dynamic_truth_table _and( 2 ); + _and._bits[0] = 0x8; + return _and; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto v1 = *begin++; + auto v2 = *begin++; + + return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::aig_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/aqfp.hpp b/third-party/mockturtle/include/mockturtle/networks/aqfp.hpp new file mode 100644 index 00000000000..e739860ac07 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/aqfp.hpp @@ -0,0 +1,1121 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp.hpp + \brief AQFP network implementation + + \author Alessandro Tempia Calvino + \author Dewmini Sudara Marakkalage + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +struct aqfp_storage_data +{ + std::unordered_map node_fn_cache; +}; + +/*! \brief AQFP storage container + + We use one bit of the index pointer to store a complemented attribute. + Every node has 64-bit of additional data used for the following purposes: + + `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + `data[0].h2`: Application-specific value + `data[1].h1`: Visited flag +*/ +using aqfp_storage = storage, aqfp_storage_data>; + +class aqfp_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 5u; + + using base_type = aqfp_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( aqfp_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator aqfp_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( aqfp_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + aqfp_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _storage->nodes[0].children.resize( 3u ); + _storage->nodes[0].children[0].data = _storage->nodes[0].children[0].data = _storage->nodes[0].children[0].data = static_cast( 0u ); + } + + aqfp_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children.resize( 3u ); + node.children[0].data = node.children[1].data = node.children[2].data = ~static_cast( 0 ); + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].children[0].data == ~static_cast( 0 ) && _storage->nodes[n].children[1].data == ~static_cast( 0 ) && _storage->nodes[n].children[2].data == ~static_cast( 0 ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + signal create_maj( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + else + { + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + static_cast( c.complement ) >= 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + + node.children.resize( 3u ); + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + const auto index = _storage->nodes.size(); + + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_maj( std::vector children ) + { + assert( children.size() > 0u ); + assert( children.size() % 2 == 1u ); + + if ( children.size() == 1u ) + { + return children[0u]; + } + + std::stable_sort( children.begin(), children.end(), []( auto f, auto s ) { return f.index < s.index; } ); + + for ( auto i = 1u; i < children.size(); i++ ) + { + if ( children[i - 1].index == children[i].index && children[i - 1].complement != children[i].complement ) + { + children.erase( children.begin() + ( i - 1 ), children.begin() + ( i + 1 ) ); + return create_maj( children ); + } + } + + for ( auto i = 0u; i < children.size(); i++ ) + { + const auto index = children[i].index; + const auto complement = children[i].complement; + auto j = i + 1; + while ( j < children.size() && children[j].index == index && children[j].complement == complement ) + { + j++; + } + if ( j - i > children.size() / 2 ) + { + return { index, complement }; + } + } + + auto node_complement = false; + + auto num_complemented = 0u; + for ( const auto& c : children ) + { + num_complemented += static_cast( c.complement ); + } + + if ( num_complemented > children.size() / 2 ) + { + node_complement = true; + for ( auto& c : children ) + { + c.complement = !c.complement; + } + } + + storage::element_type::node_type node; + + for ( const auto& c : children ) + { + node.children.push_back( c ); + } + + const auto index = _storage->nodes.size(); + + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( const auto& c : children ) + { + _storage->nodes[c.index].data[0].h1++; + } + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + const auto f = create_maj( a, !b, c ); + const auto g = create_maj( a, b, !c ); + return create_maj( !a, f, g ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aqfp_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() > 1 && children.size() % 2 == 1 ); + return create_maj( children ); + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + std::vector old_children; + + bool replacement = false; + for ( size_t i = 0u; i < node.children.size(); ++i ) + { + old_children.push_back( signal{ node.children[i] } ); + + if ( node.children[i].index == old_node ) + { + node.children[i] = node.children[i].weight ? !new_signal : new_signal; + replacement = true; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + } + } + + if ( !replacement ) + { + return std::nullopt; + } + + /* TODO: Do the simplifications if possible and ordering */ + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + + return std::nullopt; + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + // increment fan-in of new node + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || is_ci( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < nobj.children.size(); ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _new] = to_substitute.top(); + to_substitute.pop(); + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + take_out_node( _old ); + } + } + +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - 1u - _storage->inputs.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return _storage->nodes[n].children.size(); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ); + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + const auto num_fanin = _storage->nodes[n].children.size(); + + if ( num_fanin == 3u ) + { + kitty::dynamic_truth_table _maj( 3u ); + _maj._bits[0] = 0xe8; + return _maj; + } + else if ( num_fanin == 5u ) + { + kitty::dynamic_truth_table _maj( 5u ); + _maj._bits[0] = 0xfee8e880; + return _maj; + } + else + { + if ( _storage->data.node_fn_cache.count( num_fanin ) ) + { + return _storage->data.node_fn_cache[num_fanin]; + } + + std::vector> dp; + for ( auto i = 0u; i <= num_fanin; i++ ) + { + dp.push_back( { ~kitty::dynamic_truth_table( num_fanin ) } ); + if ( i == 0u ) + continue; + auto ith_var = kitty::nth_var( num_fanin, i - 1 ); + for ( auto j = 1u; j <= i && j <= ( num_fanin / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) | dp[i - 1][j] : ( ith_var & dp[i - 1][j - 1] ) ); + } + } + + return ( _storage->data.node_fn_cache[num_fanin] = dp[num_fanin][( num_fanin / 2 ) + 1] ); + } + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] } ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] }, i ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] } ); + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] }, i ); + } + } + } +#pragma endregion + + template + iterates_over_t + compute_majority_n_with_bool( Iterator begin, Iterator end ) const + { + std::vector> dp; + std::vector v( begin, end ); + + const auto n = v.size(); + typename Iterator::value_type one = true; + + for ( auto i = 0u; i <= n; i++ ) + { + dp.push_back( { one } ); + if ( i == 0u ) + continue; + auto ith_var = v[i - 1]; + for ( auto j = 1u; j <= i && j <= ( n / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) || dp[i - 1][j] : ( ith_var && dp[i - 1][j - 1] ) ); + } + } + + return ( dp[n][( n / 2 ) + 1] ); + } + + template + auto compute_majority_n( Iterator begin, Iterator end ) const + { + std::vector> dp; + std::vector v( begin, end ); + + const auto n = v.size(); + typename Iterator::value_type one = ~( v[0] ^ v[0] ); + + for ( auto i = 0u; i <= n; i++ ) + { + dp.push_back( { one } ); + if ( i == 0u ) + continue; + auto ith_var = v[i - 1]; + for ( auto j = 1u; j <= i && j <= ( n / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) | dp[i - 1][j] : ( ith_var & dp[i - 1][j - 1] ) ); + } + } + + return ( dp[n][( n / 2 ) + 1] ); + } + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( ( *it ) ^ _storage->nodes[n].children[i].weight ); + } + return compute_majority_n_with_bool( v.begin(), v.end() ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( *it ) : ( *it ) ); + } + + return compute_majority_n( v.begin(), v.end() ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + assert( begin->num_bits() > 0 && "truth tables must not be empty" ); + for ( auto it = begin; it != end; it++ ) + { + assert( begin->num_bits() == it->num_bits() ); + } + assert( begin->num_bits() >= result.num_bits() ); + assert( result.num_blocks() == begin->num_blocks() || ( result.num_blocks() == begin->num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( begin->num_bits() ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( it->_bits.back() ) : ( it->_bits.back() ) ); + } + + result._bits.back() = compute_majority_n( v.begin(), v.end() ); + + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::aqfp_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/block.hpp b/third-party/mockturtle/include/mockturtle/networks/block.hpp new file mode 100644 index 00000000000..0097e5ac230 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/block.hpp @@ -0,0 +1,1078 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file block.hpp + \brief Block logic network implementation with multi-output support + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct block_storage_data +{ + truth_table_cache cache; +}; + +/*! \brief Block node + * + * `data[0].h1` : Application-specific value + * `data[1].h1` : Visited flags + * `data[1].h2` : Total fan-out size (we use MSB to indicate whether a node is dead) + * `data[2+i].h1`: Function literal in truth table cache for the fanout + * `data[2+i].h2`: Fan-out size + * + */ +struct block_storage_node : block_fanin_node<2> +{ + block_storage_node() + { + data = decltype( data )( 3 ); + } + + bool operator==( block_storage_node const& other ) const + { + if ( data.size() != other.data.size() ) + return false; + + for ( auto i = 2; i < data.size() + 2; ++i ) + if ( ( data[i].h1 != other.data[i].h1 ) || ( children != other.children ) ) + return false; + + return true; + } +}; + +/*! \brief Block storage container + + ... +*/ +using block_storage = storage_no_hash; + +class block_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + static constexpr auto min_gate_output_size = 1; + static constexpr auto max_gate_output_size = 2; + static constexpr auto output_signal_bits = 1; + + using base_type = block_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), output( 0 ), index( index ) + { + } + + signal( uint32_t index ) + : complement( 0 ), output( 0 ), index( index ) + { + } + + signal( uint64_t index, uint64_t complement, uint64_t output ) + : complement( complement ), output( output ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( block_storage::node_type::pointer_type const& p ) + : complement( p.weight & 1 ), output( p.weight >> 1 ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t output : output_signal_bits; + uint64_t index : 63 - output_signal_bits; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, output, 0 }; + } + + signal operator-() const + { + return { index, output, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator block_storage::node_type::pointer_type() const + { + return { index, ( output << 1 ) | complement }; + } + + operator uint64_t() const + { + return data; + } + +#if __cplusplus > 201703L + bool operator==( block_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + block_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + block_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + + block_network clone() const + { + return { std::make_shared( *_storage ) }; + } + +protected: + inline void _init() + { + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[2].h1 = 0; + _storage->nodes[1].data[2].h1 = 1; + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant( bool value = false ) const + { + return value ? signal( 1, 0 ) : signal( 0, 0 ); + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[2].h1 = 2; + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[1].h2++; + _storage->nodes[f.index].data[2 + f.output].h2++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, ( f.output << 1 ) | f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_multioutput( node const& n ) const + { + return _storage->nodes[n].data.size() > 3; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return n > 1 && _storage->nodes[n].children.size() == 0u; + } + + bool is_pi( node const& n ) const + { + return n > 1 && _storage->nodes[n].children.size() == 0u; + } + + bool constant_value( node const& n ) const + { + return n != 0; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return _create_node( { a }, 2 ); + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } + + signal create_ha( signal a, signal b ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b }, { 4, 12 } ); + } + + signal create_hai( signal a, signal b ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b }, { 5, 13 } ); + } + + signal create_fa( signal a, signal b, signal c ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b, c }, { 14, 18 } ); + } + + signal create_fai( signal a, signal b, signal c ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b, c }, { 15, 19 } ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[2].h1 = literal; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[1].h2++; /* TODO: increase fanout count for output */ + _storage->nodes[c.index].data[2 + c.output].h2++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal _create_node( std::vector const& children, std::vector const& literals ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + + node.data = decltype( node.data )( 2 + literals.size() ); + + for ( auto i = 0; i < literals.size(); ++i ) + node.data[2 + i].h1 = literals[i]; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[1].h2++; + _storage->nodes[c.index].data[2 + c.output].h2++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal create_node( std::vector const& children, std::vector const& functions ) + { + assert( functions.size() > 0 ); + + if ( children.size() == 0u ) + { + assert( functions[0].num_vars() == 0u ); + return get_constant( !kitty::is_const0( functions[0] ) ); + } + std::vector literals; + for ( auto const& tt : functions ) + literals.push_back( _storage->data.cache.insert( tt ) ); + + return _create_node( children, literals ); + } + + signal clone_node( block_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + if ( other.is_multioutput( source ) ) + { + std::vector tts; + for ( auto i = 2; i < other._storage->nodes[source].data.size(); ++i ) + tts.push_back( other._storage->data.cache[other._storage->nodes[source].data[i].h1] ); + return create_node( children, tts ); + } + else + { + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[2].h1]; + return create_node( children, tt ); + } + } +#pragma endregion + +#pragma region Restructuring + void replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + bool in_fanin = false; + auto& nobj = _storage->nodes[n]; + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + in_fanin = true; + break; + } + } + + if ( !in_fanin ) + return; + + // remember before + std::vector old_children( nobj.children.size() ); + std::transform( nobj.children.begin(), nobj.children.end(), old_children.begin(), []( auto c ) { return signal{ c }; } ); + + /* replace in node */ + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + child = signal{ new_signal.data ^ ( child.data & 1 ) }; + // increment fan-out of new node + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + replace_in_node( n, old_node, new_signal ); + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output = signal{ new_signal.data ^ ( output.data & 1 ) }; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n < 2 || is_ci( n ) ) + return; + + /* delete the node */ + auto& nobj = _storage->nodes[n]; + nobj.data[1].h2 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + /* remove fanout count over output pins */ + for ( uint32_t i = 2; i < _storage->nodes[n].data.size(); ++i ) + { + nobj.data[i].h2 = 0; + } + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + /* if the node has been deleted, then deref fanout_size of + fanins and try to take them out if their fanout_size become 0 */ + for ( auto i = 0; i < nobj.children.size(); ++i ) + { + auto& child = nobj.children[i]; + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + + decr_fanout_size_pin( nobj.children[i].index, signal{ child }.output ); + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + assert( !is_dead( n ) ); + return; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto idx = 2u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + substitute_node( old_node, new_signal ); + } + + inline bool is_dead( node const& n ) const + { + /* A dead node is simply a dangling node */ + return ( _storage->nodes[n].data[1].h2 >> 31 ) & 1; + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t num_outputs( node const& n ) const + { + return static_cast( _storage->nodes[n].data.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[1].h2++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return _storage->nodes[n].data[2 + pin_index].h2++; + } + + uint32_t decr_fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return --_storage->nodes[n].data[2 + pin_index].h2; + } + + uint32_t fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return _storage->nodes[n].data[2 + pin_index].h2; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } + + bool is_and( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 4; + } + + bool is_and( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 4; + } + + bool is_or( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 6; + } + + bool is_or( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 6; + } + + bool is_xor( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 12; + } + + bool is_xor( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 12; + } + + bool is_maj( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 14; + } + + bool is_maj( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 14; + } + + bool is_ite( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 16; + } + + bool is_ite( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 16; + } + + bool is_xor3( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 18; + } + + bool is_xor3( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 18; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[2].h1]; + } + + kitty::dynamic_truth_table node_function_pin( const node& n, uint32_t pin_index ) const + { + return _storage->data.cache[_storage->nodes[n].data[2 + pin_index].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return { n, 0 }; + } + + signal make_signal( node const& n, uint32_t output_pin ) const + { + return { n, 0, output_pin }; + } + + bool is_complemented( signal const& f ) const + { + return f.complement ? true : false; + } + + uint32_t get_output_pin( signal const& f ) const + { + return static_cast( f.output ); + } + + signal next_output_pin( signal const& f ) const + { + return { f.index, f.complement, f.output + 1 }; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto f ) { return signal( f ); }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto f ) { return signal( f ); }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return signal( f ); }, fn ); + } +#pragma endregion + +#pragma region Simulate values // (Works on single-output gates only) + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + auto it = _storage->nodes[n].children.begin(); + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? ( ~( it->weight ) & 1 ) : ( ( it->weight ) & 1 ); + ++it; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[2].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector::value_type> tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* adjust polarities */ + for ( auto j = 0u; j < nfanin; ++j ) + { + if ( _storage->nodes[n].children[j].weight & 1 ) + tts[j] = ~tts[j]; + } + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[2].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h1 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h1 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h1++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h1 ); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/buffered.hpp b/third-party/mockturtle/include/mockturtle/networks/buffered.hpp new file mode 100644 index 00000000000..eef214f7687 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/buffered.hpp @@ -0,0 +1,1030 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file buffered.hpp + \brief Buffered networks implementation + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "aig.hpp" +#include "aqfp.hpp" +#include "crossed.hpp" +#include "mig.hpp" + +#include + +namespace mockturtle +{ + +class buffered_aig_network : public aig_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0] = a; + node.children[1] = !a; + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + } + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + void invert( node const& n ) + { + assert( !is_constant( n ) && !is_pi( n ) ); + assert( fanout_size( n ) == 0 ); + _storage->nodes[n].children[0].weight ^= 1; + _storage->nodes[n].children[1].weight ^= 1; + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( other.is_and( source ) ); + assert( children.size() == 2u ); + return create_and( children[0u], children[1u] ); + } +#pragma endregion + +#pragma region Restructuring + // disable restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) = delete; + void replace_in_outputs( node const& old_node, signal const& new_signal ) = delete; + void take_out_node( node const& n ) = delete; + void substitute_node( node const& old_node, signal const& new_signal ) = delete; + void substitute_nodes( std::list> substitutions ) = delete; +#pragma endregion + +#pragma region Structural properties + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + else if ( is_buf( n ) ) + return 1; + else + return 2; + } + + // including buffers, splitters, and inverters + bool is_buf( node const& n ) const + { + return _storage->nodes[n].children[0].index == _storage->nodes[n].children[1].index && _storage->nodes[n].children[0].weight != _storage->nodes[n].children[1].weight; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].children[0].weight; + } + + bool is_and( node const& n ) const + { + return n > 0 && !is_ci( n ) && !is_buf( n ); + } + +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + if ( is_buf( n ) ) + { + kitty::dynamic_truth_table _buf( 1 ); + _buf._bits[0] = 0x2; + return _buf; + } + + kitty::dynamic_truth_table _and( 2 ); + _and._bits[0] = 0x8; + return _and; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ) && !is_buf( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if ( is_buf( n ) ) + { + if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + } + else + { + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? !( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto v1 = *begin++; + auto v2 = *begin++; + + return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? ~( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + { + result.resize( begin->num_bits() ); + result._bits.back() = is_complemented( _storage->nodes[n].children[0] ) ? ~( begin->_bits.back() ) : begin->_bits.back(); + result.mask_bits(); + return; + } + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + result.mask_bits(); + } +#pragma endregion +}; /* buffered_aig_network */ + +class buffered_mig_network : public mig_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0] = a; + node.children[1] = !a; + // node.children[2] = a; // not used + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + } + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + void invert( node const& n ) + { + assert( !is_constant( n ) && !is_pi( n ) ); + assert( fanout_size( n ) == 0 ); + _storage->nodes[n].children[0].weight ^= 1; + _storage->nodes[n].children[1].weight ^= 1; + _storage->nodes[n].children[2].weight ^= 1; + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( mig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( other.is_maj( source ) ); + assert( children.size() == 3u ); + return create_maj( children[0u], children[1u], children[2u] ); + } +#pragma endregion + +#pragma region Restructuring + // disable restructuring + void replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + assert( is_buf( old_node ) ); + auto& node = _storage->nodes[n]; + + if ( is_buf( n ) ) + { + assert( node.children[0].index == old_node ); + new_signal.complement ^= node.children[0].weight; + node.children[0] = new_signal; + node.children[1] = !new_signal; + _storage->nodes[new_signal.index].data[0].h1++; + return; + } + + uint32_t fanin = 3u; + for ( auto i = 0u; i < 3u; ++i ) + { + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + assert( fanin < 3 ); + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + _storage->hash.erase( node ); + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + } + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + assert( !is_dead( old_node ) ); + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + // increment fan-in of new node + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + void take_out_node( node const& n ) + { + assert( is_buf( n ) ); + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + if ( decr_fanout_size( nobj.children[0].index ) == 0 ) + { + take_out_node( nobj.children[0].index ); + } + } + void substitute_node( node const& old_node, signal const& new_signal ) = delete; + void substitute_nodes( std::list> substitutions ) = delete; +#pragma endregion + +#pragma region Structural properties + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + else if ( is_buf( n ) ) + return 1; + else + return 3; + } + + // including buffers, splitters, and inverters + bool is_buf( node const& n ) const + { + return _storage->nodes[n].children[0].index == _storage->nodes[n].children[1].index && _storage->nodes[n].children[0].weight != _storage->nodes[n].children[1].weight; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].children[0].weight; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ) && !is_buf( n ); + } + +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + if ( is_buf( n ) ) + { + kitty::dynamic_truth_table _buf( 1 ); + _buf._bits[0] = 0x2; + return _buf; + } + + kitty::dynamic_truth_table _maj( 3 ); + _maj._bits[0] = 0xe8; + return _maj; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ) && !is_buf( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if ( is_buf( n ) ) + { + if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + } + else + { + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? !( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + return ( ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ) ) || ( ( v3 ^ c3.weight ) && ( v1 ^ c1.weight ) ) || ( ( v3 ^ c3.weight ) && ( v2 ^ c2.weight ) ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? ~( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + return kitty::ternary_majority( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + { + result.resize( begin->num_bits() ); + result._bits.back() = is_complemented( _storage->nodes[n].children[0] ) ? ~( begin->_bits.back() ) : begin->_bits.back(); + result.mask_bits(); + return; + } + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ) | + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ) | + ( ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ); + result.mask_bits(); + } +#pragma endregion +}; /* buffered_mig_network */ + +class buffered_aqfp_network : public aqfp_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Primary I / O and constants + bool is_ci( node const& n ) const + { + if ( is_buf( n ) ) + return false; + + return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data; + } + + bool is_pi( node const& n ) const + { + if ( is_buf( n ) ) + return false; + + return _storage->nodes[n].children[0].data == ~static_cast( 0 ) && _storage->nodes[n].children[1].data == ~static_cast( 0 ) && _storage->nodes[n].children[2].data == ~static_cast( 0 ); + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + if ( is_constant( get_node( a ) ) ) + return a; + + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + + node.children.resize( 1u ); + node.children[0] = a; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + void invert( node const& n ) + { + assert( !is_constant( n ) && !is_pi( n ) ); + assert( fanout_size( n ) == 0 ); + for ( auto& s : _storage->nodes[n].children ) + { + s.weight ^= 1; + } + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aqfp_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( other.is_maj( source ) ); + assert( children.size() > 1 && children.size() % 2 == 1 ); + return create_maj( children ); + } +#pragma endregion + +#pragma region Structural properties + /* redefinition of num_gates counting the gates */ + auto num_gates() const + { + uint32_t gate_count = 0; + foreach_gate( [&gate_count]( auto const& n ) { + ++gate_count; + } ); + return gate_count; + } + + bool is_buf( node const& n ) const + { + return _storage->nodes[n].children.size() == 1; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].children.size() == 1 && _storage->nodes[n].children[0].weight; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ) && !is_buf( n ); + } + +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + if ( is_buf( n ) ) + { + kitty::dynamic_truth_table _buf( 1 ); + _buf._bits[0] = 0x2; + return _buf; + } + + const auto num_fanin = _storage->nodes[n].children.size(); + + if ( num_fanin == 3u ) + { + kitty::dynamic_truth_table _maj( 3u ); + _maj._bits[0] = 0xe8; + return _maj; + } + else if ( num_fanin == 5u ) + { + kitty::dynamic_truth_table _maj( 5u ); + _maj._bits[0] = 0xfee8e880; + return _maj; + } + else + { + if ( _storage->data.node_fn_cache.count( num_fanin ) ) + { + return _storage->data.node_fn_cache[num_fanin]; + } + + std::vector> dp; + for ( auto i = 0u; i <= num_fanin; i++ ) + { + dp.push_back( { ~kitty::dynamic_truth_table( num_fanin ) } ); + if ( i == 0u ) + continue; + auto ith_var = kitty::nth_var( num_fanin, i - 1 ); + for ( auto j = 1u; j <= i && j <= ( num_fanin / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) | dp[i - 1][j] : ( ith_var & dp[i - 1][j - 1] ) ); + } + } + + return ( _storage->data.node_fn_cache[num_fanin] = dp[num_fanin][( num_fanin / 2 ) + 1] ); + } + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ) && !is_buf( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] } ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] }, i ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] } ); + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] }, i ); + } + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? !( *begin ) : *begin; + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( ( *it ) ^ _storage->nodes[n].children[i].weight ); + } + return compute_majority_n_with_bool( v.begin(), v.end() ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? ~( *begin ) : *begin; + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( *it ) : ( *it ) ); + } + + return compute_majority_n( v.begin(), v.end() ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + { + result.resize( begin->num_bits() ); + result._bits.back() = is_complemented( _storage->nodes[n].children[0] ) ? ~( begin->_bits.back() ) : begin->_bits.back(); + result.mask_bits(); + return; + } + + assert( begin->num_bits() > 0 && "truth tables must not be empty" ); + for ( auto it = begin; it != end; it++ ) + { + assert( begin->num_bits() == it->num_bits() ); + } + assert( begin->num_bits() >= result.num_bits() ); + assert( result.num_blocks() == begin->num_blocks() || ( result.num_blocks() == begin->num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( begin->num_bits() ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( it->_bits.back() ) : ( it->_bits.back() ) ); + } + + result._bits.back() = compute_majority_n( v.begin(), v.end() ); + + result.mask_bits(); + } +#pragma endregion +}; /* buffered_aqfp_network */ + +class buffered_crossed_klut_network : public crossed_klut_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return _create_node( { a }, 2 ); + } + + void invert( node const& n ) + { + if ( _storage->nodes[n].data[1].h1 == 2 ) + _storage->nodes[n].data[1].h1 = 3; + else if ( _storage->nodes[n].data[1].h1 == 3 ) + _storage->nodes[n].data[1].h1 = 2; + else + assert( false ); + } +#pragma endregion + +#pragma region Crossings + /*! \brief Merges two buffer nodes into a crossing cell + * + * After this operation, the network will not be in a topological order. Additionally, buf1 and buf2 will be dangling. + * + * \param buf1 First buffer node. + * \param buf2 Second buffer node. + * \return The created crossing cell. + */ + node merge_into_crossing( node const& buf1, node const& buf2 ) + { + assert( is_buf( buf1 ) && is_buf( buf2 ) ); + + auto const& in_buf1 = _storage->nodes[buf1].children[0]; + auto const& in_buf2 = _storage->nodes[buf2].children[0]; + + node out_buf1{}, out_buf2{}; + uint32_t fanin_index1 = std::numeric_limits::max(), fanin_index2 = std::numeric_limits::max(); + foreach_node( [&]( node const& n ) { + foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( auto const fin = get_node( f ); fin == buf1 ) + { + out_buf1 = n; + fanin_index1 = i; + } + else if ( fin == buf2 ) + { + out_buf2 = n; + fanin_index2 = i; + } + } ); + } ); + assert( out_buf1 != 0 && out_buf2 != 0 ); + assert( fanin_index1 != std::numeric_limits::max() && fanin_index2 != std::numeric_limits::max() ); + + auto const [fout1, fout2] = create_crossing( in_buf1, in_buf2 ); + + _storage->nodes[out_buf1].children[fanin_index1] = fout1; + _storage->nodes[out_buf2].children[fanin_index2] = fout2; + + /* decrease ref-count to children (was increased in `create_crossing`) */ + _storage->nodes[in_buf1.index].data[0].h1--; + _storage->nodes[in_buf2.index].data[0].h1--; + + _storage->nodes[buf1].children.clear(); + _storage->nodes[buf2].children.clear(); + + return get_node( fout1 ); + } + +#pragma endregion + +#pragma region Structural properties + // including buffers, splitters, and inverters + bool is_buf( node const& n ) const + { + return _storage->nodes[n].data[1].h1 == 2 || _storage->nodes[n].data[1].h1 == 3; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].data[1].h1 == 3; + } +#pragma endregion + +#pragma region Node and signal iterators + /* Note: crossings are included; buffers, splitters, inverters are not */ + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_buf( n ); }, + fn ); + } +#pragma endregion +}; /* buffered_crossed_klut_network */ + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/cover.hpp b/third-party/mockturtle/include/mockturtle/networks/cover.hpp new file mode 100644 index 00000000000..f79f2f2f49b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/cover.hpp @@ -0,0 +1,808 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cover.hpp + \brief single output cover logic network implementation + + \author Andrea Costamagna + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" + +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include +#include + +#include + +namespace mockturtle +{ +/*! \brief cover storage data + * + * This struct contains the constituents of the network and its main features. + * + * The constituents of the network are the covers representing the boolean functions stored in each node. + * These are stored in a vector of pairs. Each element is the cover of a function and a boolean value indicating whether the + * cover indicates the ON-set or the OFF set. More precisely: + * `covers` : Vector of pairs for covers storage + * `covers[i].first` : Cubes i-th cover + * `covers[i].second` : Boolean true (false) if ON set (OFF set) + * This data structure directly originates from the k-LUT one and, for this reason, it inherits from it the vast majority of the features. + * The main difference is the way the nodes are stored and future improvements could include the substitution of the current covers storage with + * a cache, to avoid the redundant storage of some recurrent boolean functions. + */ +struct cover_storage_data +{ + uint64_t insert( std::pair, bool> const& cover ) + { + const auto index = covers.size(); + covers.emplace_back( cover ); + return index; + } + + std::vector, bool>> covers; +}; + +/*! \brief cover node + * + * The cover node is a mixed fanin node with the following attributes: + * `children` : vector of pointers to children + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Index of the cover of the node in the covers container + * `data[1].h2`: Visited flags + */ +struct cover_storage_node : mixed_fanin_node<2> +{ + bool operator==( cover_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; + +/*! \brief cover storage container + * + * The network as a storage entity is defined by combining the node structure with the cover_storage structure. + * The attributes of this storage unit are listed in the following: + * `nodes` : Vector of cover storage nodes + * `inputs` : Vector of indices to inputs nodes + * `outputs` : Vector of pointers to node types + * `hash` : maps a node to its index in the nodes vector + * `data` : cover storage data + */ +using cover_storage = storage; + +/*! \brief cover_network + * + * This class implements a data structure for a cover based network. + * In this representation, each node is represented by specifying its ON set or its OFF set, that in both cases are stored as a vector of cubes. + * The information related to the set to which the node refers to is contained in a boolean variable, that is true (false) if the + * ON set (OFF set) is considered. + * + * This data structure is primarily meant to be used for reading .blif files in which the number of variables would make it unfeasible the reading via a k-LUT network. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + cover_network cover; + + const auto a = cover.create_pi(); + const auto b = cover.create_pi(); + const auto c = cover.create_pi(); + + kitty::cube _11 = kitty::cube("11"); + + std::vector nand_from_offset { _11 }; + const auto n1 = cover.create_cover_node( {a, b}, std::make_pair( nand_from_offset, false ) ); + + const auto y1 = cover.create_and( n1, c ); + cover.create_po( y1 ); + + \endverbatim + */ + +class cover_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + using base_type = cover_network; + using cover_type = std::pair, bool>; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = uint64_t; + + cover_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + cover_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + +protected: + inline void _init() + { + uint64_t index; + + std::vector cube_dc = { kitty::cube() }; + + /* first node reserved for constant 0 */ + index = _storage->data.insert( std::make_pair( cube_dc, false ) ); + cover_storage_node& node_0 = _storage->nodes[0]; + node_0.data[1].h1 = index; + _storage->hash[node_0] = 0; + + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + index = _storage->data.insert( std::make_pair( cube_dc, true ) ); + cover_storage_node& node_1 = _storage->nodes[1]; + node_1.data[1].h1 = index; + _storage->hash[node_1] = 1; + + /* reserve the third node for the identity (inputs)*/ + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant( bool value = false ) const + { + return value ? 1 : 0; + } + + signal create_pi() + { + const auto index_node = _storage->nodes.size(); + std::vector cube_I{ kitty::cube( "1" ) }; + const auto index_covers = _storage->data.insert( std::make_pair( cube_I, true ) ); + + _storage->nodes.emplace_back(); + cover_storage_node& node_in = _storage->nodes[index_node]; + node_in.data[1].h1 = index_node; + _storage->hash[node_in] = index_node; + _storage->inputs.emplace_back( index_node ); + + return index_node; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool constant_value( node const& n ) const + { + return n == 1; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + std::vector _not{ kitty::cube( "0" ) }; + return _create_cover_node( { a }, std::make_pair( _not, true ) ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + std::vector _and{ kitty::cube( "11" ) }; + return _create_cover_node( { a, b }, std::make_pair( _and, true ) ); + } + + signal create_nand( signal a, signal b ) + { + std::vector _nand{ kitty::cube( "11" ) }; + return _create_cover_node( { a, b }, std::make_pair( _nand, false ) ); + } + + signal create_or( signal a, signal b ) + { + std::vector _or{ kitty::cube( "00" ) }; + return _create_cover_node( { a, b }, std::make_pair( _or, false ) ); + } + + signal create_nor( signal a, signal b ) + { + std::vector _nor{ kitty::cube( "00" ) }; + return _create_cover_node( { a, b }, std::make_pair( _nor, true ) ); + } + + signal create_lt( signal a, signal b ) + { + std::vector _lt{ kitty::cube( "01" ) }; + return _create_cover_node( { a, b }, std::make_pair( _lt, true ) ); + } + + signal create_le( signal a, signal b ) + { + std::vector _le{ kitty::cube( "10" ) }; + return _create_cover_node( { a, b }, std::make_pair( _le, false ) ); + } + + signal create_gt( signal a, signal b ) + { + std::vector _gt{ kitty::cube( "10" ) }; + return _create_cover_node( { a, b }, std::make_pair( _gt, true ) ); + } + + signal create_ge( signal a, signal b ) + { + std::vector _ge{ kitty::cube( "01" ) }; + return _create_cover_node( { a, b }, std::make_pair( _ge, false ) ); + } + + signal create_xor( signal a, signal b ) + { + std::vector _xor{ kitty::cube( "01" ), + kitty::cube( "10" ) }; + return _create_cover_node( { a, b }, std::make_pair( _xor, true ) ); + } + + signal create_xnor( signal a, signal b ) + { + std::vector _xnor{ kitty::cube( "00" ), + kitty::cube( "11" ) }; + return _create_cover_node( { a, b }, std::make_pair( _xnor, true ) ); + } +#pragma endregion + +#pragma region Create ternary functions + + signal create_maj( signal a, signal b, signal c ) + { + std::vector _maj{ kitty::cube( "011" ), + kitty::cube( "101" ), + kitty::cube( "110" ), + kitty::cube( "111" ) }; + return _create_cover_node( { a, b, c }, std::make_pair( _maj, true ) ); + } + + signal create_ite( signal a, signal b, signal c ) + { + std::vector _ite{ kitty::cube( "11-" ), + kitty::cube( "0-1" ) }; + return _create_cover_node( { a, b, c }, std::make_pair( _ite, true ) ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + std::vector _xor3{ kitty::cube( "001" ), + kitty::cube( "010" ), + kitty::cube( "100" ), + kitty::cube( "111" ) }; + return _create_cover_node( { a, b, c }, std::make_pair( _xor3, true ) ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_cover_node( std::vector const& children, cover_type const& new_cover ) + { + + uint64_t literal = _storage->data.insert( new_cover ); + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back( node ); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c].data[0].h1++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return index; + } + + /*! \brief Creates node with arbitrary cover (SOP or POS). + * + * `cover_type` is `std::pair, bool>`, where the first element defines + * the cubes (clauses) of the onset (offset) and the second element selects between the onset + * mode (true) or offset mode (false). + * + * \param children Fanin signals + * \param cover Cover for node function + */ + signal create_cover_node( std::vector const& children, cover_type cover ) + { + if ( children.size() == 0u ) + { + return get_constant( cover.second ); + } + + return _create_cover_node( children, cover ); + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + return get_constant( !kitty::is_const0( function ) ); + } + + cover_type new_cover; + bool is_sop = ( kitty::count_ones( function ) <= kitty::count_zeros( function ) ); + new_cover.second = is_sop; + uint32_t mask = 1u; + for ( uint32_t i{ 1 }; i < children.size(); ++i ) + mask |= mask << 1; + for ( uint32_t i{ 0 }; i < pow( 2, children.size() ); ++i ) + { + if ( kitty::get_bit( function, i ) == is_sop ) + { + auto cb = kitty::cube( i, mask ); + new_cover.first.push_back( cb ); + } + } + + return _create_cover_node( children, new_cover ); + } + + signal clone_node( cover_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + cover_type cb = other._storage->data.covers[other._storage->nodes[source].data[1].h1]; + return create_cover_node( children, cb ); + } +#pragma endregion + +#pragma region Restructuring + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto i = 0u; i < _storage->nodes.size(); ++i ) + { + auto& n = _storage->nodes[i]; + for ( auto& child : n.children ) + { + if ( child == old_node ) + { + std::vector old_children( n.children.size() ); + std::transform( n.children.begin(), n.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( i, old_children ); + } + } + } + } + + /* check outputs */ + for ( auto& output : _storage->outputs ) + { + if ( output == old_node ) + { + output = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + } + } + + // reset fan-out of old node + _storage->nodes[old_node].data[0].h1 = 0; + } + + inline bool is_dead( node const& n ) const + { + return false; + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } +#pragma endregion + +#pragma region Functional properties + cover_type node_cover( const node& n ) const + { + return _storage->data.covers[_storage->nodes[n].data[1].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f; + } + + signal make_signal( node const& n ) const + { + return n; + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, + fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, + fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return f.index; }, + fn ); + } + +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + uint32_t mask{ 0 }; + while ( begin != end ) + { + mask = ( mask << 1 ) | 1u; + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + auto cb_input = kitty::cube( index, mask ); + cover_type& cubes_cover = _storage->data.covers[_storage->nodes[n].data[1].h1]; + for ( auto cb : cubes_cover.first ) + { + if ( ( cb._bits & cb._mask ) == ( cb_input._bits & cb._mask ) ) + return ( cubes_cover.second == 1 ); + } + + return ( cubes_cover.second == 0 ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + cover_type& cubes_cover = _storage->data.covers[_storage->nodes[n].data[1].h1]; + bool is_found = false; + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + is_found = false; + uint32_t pattern = 0u; + uint32_t mask = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + mask |= 1u << j; + } + auto cb_input = kitty::cube( pattern, mask ); + for ( auto cb : cubes_cover.first ) + { + if ( ( cb._bits & cb._mask ) == ( cb_input._bits & cb._mask ) ) + { + is_found = true; + if ( cubes_cover.second == 1 ) + { + kitty::set_bit( result, i ); + } + break; + } + } + if ( !is_found && ( cubes_cover.second == 0 ) ) + kitty::set_bit( result, i ); + } + + return result; + } + +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/crossed.hpp b/third-party/mockturtle/include/mockturtle/networks/crossed.hpp new file mode 100644 index 00000000000..0e50305d87b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/crossed.hpp @@ -0,0 +1,867 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file crossed.hpp + \brief Implements networks with crossing cells + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "klut.hpp" + +namespace mockturtle +{ + +/* Version 1: annotate information of crossing fanout ordereing with different signals + * Share the same `klut_storage_data` as regular k-LUT network, + * but define a different `crossed_klut_storage_node` with a weight field in the pointer. + */ + +/*! \brief k-LUT node + * + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Function literal in truth table cache + * `data[1].h2`: Visited flags + */ +struct crossed_klut_storage_node : mixed_fanin_node<2, 1> +{ + bool operator==( crossed_klut_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; +using crossed_klut_storage = storage; + +class crossed_klut_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + static constexpr bool is_crossed_network_type = true; + + using base_type = crossed_klut_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = crossed_klut_storage_node::pointer_type; + + crossed_klut_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + crossed_klut_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } +#pragma endregion + +protected: + static constexpr uint32_t literal_crossing = 0xffffffff; + + inline void _init() + { + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } + +public: +#pragma region Primary I / O and constants + signal get_constant( bool value = false ) const + { + return value ? signal( 1, 0 ) : signal( 0, 0 ); + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + return signal( index, 0 ); + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool constant_value( node const& n ) const + { + return n == 1; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_nor( signal a, signal b ) + { + return _create_node( { a, b }, 7 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_ge( signal a, signal b ) + { + return _create_node( { a, b }, 9 ); + } + + signal create_gt( signal a, signal b ) + { + return _create_node( { a, b }, 10 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } + + signal create_xnor( signal a, signal b ) + { + return _create_node( { a, b }, 13 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[0].h1++; + } + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return signal( index, 0 ); + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( crossed_klut_network const& other, node const& source, std::vector const& children ) + { + assert( !other.is_crossing( source ) ); + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } + + signal clone_node( klut_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } +#pragma endregion + +#pragma region Crossings + /*! \brief Create a crossing cell + * + * \return A pair `(out1, out2)` of two signals to be used as fanouts of the crossing, + * where `in1` connects to `out1` and `in2` connects to `out2`. + */ + std::pair create_crossing( signal const& in1, signal const& in2 ) + { + storage::element_type::node_type node; + node.children.emplace_back( in1 ); + node.children.emplace_back( in2 ); + node.data[1].h1 = literal_crossing; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + _storage->nodes[in1.index].data[0].h1++; + _storage->nodes[in2.index].data[0].h1++; + + /* TODO: not sure if this is wanted/needed? */ + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return std::make_pair( signal( index, 0 ), signal( index, 1 ) ); + } + + /*! \brief Insert a crossing cell on two wires + * + * After this operation, the network will not be in a topological order. + * + * \param in1 A fanin signal of the node `out1` + * \param in2 A fanin signal of the node `out2` + * \return The created crossing cell. No more fanouts should be added to it. + */ + node insert_crossing( signal const& in1, signal const& in2, node const& out1, node const& out2 ) + { + uint32_t fanin_index1 = std::numeric_limits::max(); + foreach_fanin( out1, [&]( auto const& f, auto i ) { + if ( f == in1 ) + { + fanin_index1 = i; + return false; + } + return true; + } ); + assert( fanin_index1 != std::numeric_limits::max() ); + + uint32_t fanin_index2 = std::numeric_limits::max(); + foreach_fanin( out2, [&]( auto const& f, auto i ) { + if ( f == in2 ) + { + fanin_index2 = i; + return false; + } + return true; + } ); + assert( fanin_index2 != std::numeric_limits::max() ); + + auto [fout1, fout2] = create_crossing( in1, in2 ); + _storage->nodes[out1].children[fanin_index1] = fout1; + _storage->nodes[out2].children[fanin_index2] = fout2; + + /* decrease ref-count to children (was increased in `create_crossing`) */ + _storage->nodes[in1.index].data[0].h1--; + _storage->nodes[in2.index].data[0].h1--; + + return get_node( fout1 ); + } + + /*! \brief Whether a node is a crossing cell + * + * \param n The node to be checked + * \return Whether this node is a crossing cell + */ + bool is_crossing( node const& n ) const + { + return _storage->nodes[n].data[1].h1 == literal_crossing; + } + + /*! \brief Whether a crossing's fanout signal is the second one + * + * \param f A signal pointing to a crossing cell + * \return Whether this fanout connects to the second fanin (return 1) or the first fanin (return 0) + */ + bool is_second( signal const& f ) const + { + assert( is_crossing( f.index ) ); + return f.weight; + } + + /*! \brief Take a crossing's first fanout signal and make it the second + * + * \param f A signal pointing to a crossing cell, referring to the fanout connecting to the first fanin + * \return The signal referring to the fanout connecting to the second fanin + */ + signal make_second( signal const& f ) const + { + assert( is_crossing( f.index ) ); + assert( !is_second( f ) ); + return signal( f.index, 1 ); + } + + /*! \brief Get the real fanin signal ignoring all crossings in between + * + * \param f A signal pointing to a crossing + * \return The corresponding signal pointing to a non-crossing node + */ + signal ignore_crossings( signal const& f ) const + { + if ( !is_crossing( get_node( f ) ) ) + return f; + return ignore_crossings( _storage->nodes[f.index].children[f.weight] ); + } + + /*! \brief Iterate through the real fanins of a node, ignoring all crossings in between */ + template + void foreach_fanin_ignore_crossings( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->nodes[n].children.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), [this]( auto f ) { return ignore_crossings( f ); }, fn ); + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ) && !is_crossing( n ); + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + assert( !is_crossing( n ) ); + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } + + bool is_not( node const& n ) const + { + if ( !is_function( n ) ) + return false; + return _storage->nodes[n].children.size() == 1 && _storage->nodes[n].data[1].h1 == 3; + } + + /* AND-2 with any input negation, but not output negation */ + bool is_and( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 4 || node.data[1].h1 == 8 || node.data[1].h1 == 10 || node.data[1].h1 == 7; + } + + /* OR-2 with any input negation, but not output negation */ + bool is_or( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 5 || node.data[1].h1 == 9 || node.data[1].h1 == 11 || node.data[1].h1 == 6; + } + + /* XOR-2 or XNOR-2 */ + bool is_xor( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 12 || node.data[1].h1 == 13; + } + + /* XOR-3 or XNOR-3 */ + bool is_xor3( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 18 || node.data[1].h1 == 19; + } + + /* MAJ-3 or MINORITY-3 without non-symmetric input negation */ + bool is_maj( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 14 || node.data[1].h1 == 15; + } + + /* ITE (MUX2-1) without input negation; with or without output negation + * i.e., (x ? y :z) or !(x ? y : z) = x ? !y : !z */ + bool is_ite( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 16 || node.data[1].h1 == 17; + } + + std::vector get_fanin_negations( node const& n ) const + { + if ( is_crossing( n ) ) + return {false, false}; + if ( !is_function( n ) ) + return {}; + switch ( _storage->nodes[n].data[1].h1 ) + { + case 2: return {false}; // buf + case 3: return {true}; // not + case 4: return {false, false}; // and + case 5: return {true, true}; // x nand y = !x or !y + case 6: return {false, false}; // or + case 7: return {true, true}; // x nor y = !x and !y + case 8: return {true, false}; // !x and y + case 9: return {false, true}; // x or !y + case 10: return {false, true}; // x and !y + case 11: return {true, false}; // !x or y + case 12: return {false, false}; // xor + case 13: return {true, false}; // xnor (symmetric) + case 14: return {false, false, false}; // maj + case 15: return {true, true, true}; // minority + case 16: return {false, false, false}; // ite + case 17: return {false, true, true}; // !(x ? y : z) = x ? !y : !z + case 18: return {false, false, false}; // xor3 + case 19: return {true, false, false}; // xnor3 (symmetric) + } + return {}; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + /* Note: crossings are included */ + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + detail::foreach_element( _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), fn ); + } +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( !is_crossing( n ) ); + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( !is_crossing( n ) ); + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/detail/foreach.hpp b/third-party/mockturtle/include/mockturtle/networks/detail/foreach.hpp new file mode 100644 index 00000000000..7aadd5bbf2d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/detail/foreach.hpp @@ -0,0 +1,224 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file foreach.hpp + \brief For each functor utilities + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle::detail +{ + +template +inline constexpr bool is_callable_with_index_v = std::is_invocable_r_v; + +template +inline constexpr bool is_callable_without_index_v = std::is_invocable_r_v; + +template +Iterator foreach_element( Iterator begin, Iterator end, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !fn( *begin++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !fn( *begin++, index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + fn( *begin++ ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + fn( *begin++, index++ ); + } + return begin; + } +} + +template +Iterator foreach_element_if( Iterator begin, Iterator end, Pred&& pred, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + if ( !fn( *begin++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + if ( !fn( *begin++, index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + fn( *begin++ ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + fn( *begin++, index++ ); + } + return begin; + } +} + +template +Iterator foreach_element_transform( Iterator begin, Iterator end, Transform&& transform, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !fn( transform( *begin++ ) ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !fn( transform( *begin++ ), index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + fn( transform( *begin++ ) ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + fn( transform( *begin++ ), index++ ); + } + return begin; + } +} + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/events.hpp b/third-party/mockturtle/include/mockturtle/networks/events.hpp new file mode 100644 index 00000000000..25794d1f813 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/events.hpp @@ -0,0 +1,135 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file events.hpp + \brief Event API for updating a logic network. + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Network events. + * + * This data structure can be returned by a network. Clients can add functions + * to network events to call code whenever an event occurs. Events are adding + * a node, modifying a node, and deleting a node. + */ +template +class network_events +{ +public: + using add_event_type = std::function const& n )>; + using modified_event_type = std::function const& n, std::vector> const& previous_children )>; + using delete_event_type = std::function const& n )>; + +public: + std::shared_ptr register_add_event( add_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_add.emplace_back( pfn ); + return pfn; + } + + std::shared_ptr register_modified_event( modified_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_modified.emplace_back( pfn ); + return pfn; + } + + std::shared_ptr register_delete_event( delete_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_delete.emplace_back( pfn ); + return pfn; + } + + void release_add_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_add.erase( std::remove_if( std::begin( on_add ), std::end( on_add ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_add ) ); + } + + void release_modified_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_modified.erase( std::remove_if( std::begin( on_modified ), std::end( on_modified ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_modified ) ); + } + + void release_delete_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_delete.erase( std::remove_if( std::begin( on_delete ), std::end( on_delete ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_delete ) ); + } + +public: + /*! \brief Event when node `n` is added. */ + std::vector> on_add; + + /*! \brief Event when `n` is modified. + * + * The event also informs about the previous children. Note that the new + * children are already available at the time the event is triggered. + */ + std::vector> on_modified; + + /*! \brief Event when `n` is deleted. */ + std::vector> on_delete; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/generic.hpp b/third-party/mockturtle/include/mockturtle/networks/generic.hpp new file mode 100644 index 00000000000..067db51fdd7 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/generic.hpp @@ -0,0 +1,1065 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file generic.hpp + \brief Generic sequential logic network implementation without strashing + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "sequential.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct generic_storage_data +{ + truth_table_cache cache; + uint32_t num_pis = 0u; + uint32_t num_pos = 0u; + std::vector registers; + uint32_t trav_id = 0u; +}; + +/*! \brief Generic node + * + * `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + * `data[0].h2`: Application-specific value + * `data[1].h1`: Function literal in truth table cache + * `data[1].h2`: Visited flags + * `data[2].h1`: Node type + * `data[2].h2`: Application-specific value + * + * Node types: + * - 0: unknown + * - 1: klut node + * - 2: PI terminal + * - 3: PO terminal + * - 4: Box input terminal + * - 5: Box output terminal + * - 6: Register + * - 7: Whitebox + * - 8: Blackbox + */ +struct generic_storage_node : mixed_fanin_node<3> +{ + bool operator==( generic_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; + +/*! \brief Generic storage container + + ... +*/ +using generic_storage = storage_no_hash; + +class generic_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + using base_type = generic_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = uint64_t; + + generic_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ), + _register_information( std::make_shared>() ) + { + _init(); + } + + generic_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ), + _register_information( std::make_shared>() ) + { + _init(); + } + + generic_network( std::shared_ptr storage, std::shared_ptr> register_information ) + : _storage( storage ), + _events( std::make_shared() ), + _register_information( register_information ) + { + _init(); + } + + generic_network clone() const + { + return { std::make_shared( *_storage ), std::make_shared>( *_register_information ) }; + } + +protected: + inline void _init() + { + /* already initialized */ + if ( _storage->nodes.size() > 1 ) + return; + + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + _storage->data.cache.insert( tt_buf ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant( bool value = false ) const + { + return value ? 1 : 0; + } + + signal create_pi( std::string const& name = std::string() ) + { + (void)name; + + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + _storage->nodes[index].data[2].h1 = 2; + ++_storage->data.num_pis; + return index; + } + + uint32_t create_po( signal const& f, std::string const& name = std::string() ) + { + (void)name; + + /* check f is not a PO already */ + if ( is_po( get_node( f ) ) ) + return get_node( f ); + + const auto index = _storage->nodes.size(); + const auto po = create_buf( f ); + _storage->nodes[index].data[2].h1 = 3; + /* increase ref-count of PO */ + _storage->nodes[po].data[0].h1++; + + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( po ); + ++_storage->data.num_pos; + return po_index; + } + + signal create_ro( std::string const& name = std::string() ) + { + (void)name; + + auto const index = static_cast( _storage->nodes.size() ); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + _storage->nodes[index].data[2].h1 = 5; + return index; + } + + uint32_t create_ri( signal const& f, int8_t reset = 0, std::string const& name = std::string() ) + { + (void)name; + + /* increase ref-count to children */ + _storage->nodes[f].data[0].h1++; + auto const ri_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + _storage->data.registers.emplace_back( reset ); + return ri_index; + } + + bool is_combinational() const + { + return ( static_cast( _storage->inputs.size() ) == _storage->data.num_pis && + static_cast( _storage->outputs.size() ) == _storage->data.num_pos ); + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 2 || _storage->nodes[n].data[2].h1 == 5; + } + + bool is_co( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 3 || _storage->nodes[n].data[2].h1 == 4; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 2; + } + + bool is_po( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 3; + } + + bool is_ro( node const& n ) const + { + return std::find( _storage->inputs.begin() + _storage->data.num_pis, _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_node( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 1; + } + + bool is_register( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 6; + } + + bool is_box_input( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 4; + } + + bool is_box_output( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 5; + } + + bool constant_value( node const& n ) const + { + return n == 1; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return _create_node( { a }, 2 ); + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + node.data[2].h1 = 1; + + /* structural hashing is not used */ + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c].data[0].h1++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return index; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( generic_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } + + signal create_box_input( signal a ) + { + auto const ri = create_buf( a ); + _storage->nodes[get_node( ri )].data[2].h1 = 4; + return ri; + } + + signal create_box_output( signal a ) + { + auto const ro = create_buf( a ); + _storage->nodes[get_node( ro )].data[2].h1 = 5; + return ro; + } + + signal create_register( signal a, register_t const& l_info = {} ) + { + auto const r = create_buf( a ); + _storage->nodes[get_node( r )].data[2].h1 = 6; + _register_information->insert( { get_node( r ), l_info } ); + + return r; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& root = _storage->nodes[n]; + for ( auto& child : root.children ) + { + if ( child == old_node ) + { + std::vector old_children( root.children.size() ); + std::transform( root.children.begin(), root.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + } + } + return std::nullopt; + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + for ( auto& output : _storage->outputs ) + { + if ( output == old_node ) + { + output = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete PIs, constants, or already dead nodes */ + if ( n <= 1 || is_pi( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + /* remove register entry if register */ + if ( is_register( n ) ) + { + _register_information->erase( n ); + } + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto& child : nobj.children ) + { + if ( fanout_size( child.index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( child.index ) == 0 ) + { + take_out_node( child.index ); + } + } + } + + void revive_node( node const& n ) + { + assert( !is_dead( n ) ); + return; + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto i = 0u; i < _storage->nodes.size(); ++i ) + { + auto& n = _storage->nodes[i]; + for ( auto& child : n.children ) + { + if ( child == old_node ) + { + std::vector old_children( n.children.size() ); + std::transform( n.children.begin(), n.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( i, old_children ); + } + } + } + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* reset fan-in of old node */ + take_out_node( old_node ); + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_registers() const + { + return static_cast( _register_information->size() ); + } + + /*! \brief Standard version of num_registers replaced by the one above. */ + // auto num_registers() const + // { + // assert( static_cast( _storage->inputs.size() - _storage->data.num_pis ) == static_cast( _storage->outputs.size() - _storage->data.num_pos ) ); + // return static_cast( _storage->inputs.size() - _storage->data.num_pis ); + // } + + auto num_pis() const + { + return _storage->data.num_pis; + } + + auto num_pos() const + { + return _storage->data.num_pos; + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - _storage->outputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f; + } + + signal make_signal( node const& n ) const + { + return n; + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->data.num_pis ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->data.num_pos ); + return ( _storage->outputs.begin() + index )->index; + } + + node ro_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() - _storage->data.num_pis ); + return *( _storage->inputs.begin() + _storage->data.num_pis + index ); + } + + signal ri_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() - _storage->data.num_pos ); + return ( _storage->outputs.begin() + _storage->data.num_pos + index )->index; + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t ro_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data - _storage->data.num_pis ); + } + + uint32_t ri_index( signal const& s ) const + { + uint32_t i = -1; + foreach_ri( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + signal ro_to_ri( signal const& s ) const + { + return ( _storage->outputs.begin() + _storage->data.num_pos + _storage->nodes[s].children[0].data - _storage->data.num_pis )->index; + } + + node ri_to_ro( signal const& s ) const + { + return *( _storage->inputs.begin() + _storage->data.num_pis + ri_index( s ) ); + } +#pragma endregion + +#pragma region Children acces + signal get_fanin0( node const& n ) const + { + assert( _storage->nodes[n].children.size() ); + return _storage->nodes[n].children[0].data; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.begin() + _storage->data.num_pis, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.begin() + _storage->data.num_pos, []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin() + _storage->data.num_pis, _storage->inputs.end(), fn ); + } + + template + void foreach_ri( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin() + _storage->data.num_pos, _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_register( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return is_register( n ) && !is_dead( n ); }, + fn ); + } + + /*! \brief Standard version of foreach_register replaced by the one above. */ + // template + // void foreach_register( Fn&& fn ) const + // { + // static_assert( detail::is_callable_with_index_v, void> || + // detail::is_callable_without_index_v, void> || + // detail::is_callable_with_index_v, bool> || + // detail::is_callable_without_index_v, bool> ); + + // assert( _storage->inputs.size() - _storage->data.num_pis == _storage->outputs.size() - _storage->data.num_pos ); + // auto ro = _storage->inputs.begin() + _storage->data.num_pis; + // auto ri = _storage->outputs.begin() + _storage->data.num_pos; + // if constexpr ( detail::is_callable_without_index_v, bool> ) + // { + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // if ( !fn( std::make_pair( ( ri++ )->index, ro++ ) ) ) + // return; + // } + // } + // else if constexpr ( detail::is_callable_with_index_v, bool> ) + // { + // uint32_t index{ 0 }; + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // if ( !fn( std::make_pair( ( ri++ )->index, ro++ ), index++ ) ) + // return; + // } + // } + // else if constexpr ( detail::is_callable_without_index_v, void> ) + // { + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // fn( std::make_pair( ( ri++ )->index, *ro++ ) ); + // } + // } + // else if constexpr ( detail::is_callable_with_index_v, void> ) + // { + // uint32_t index{ 0 }; + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // fn( std::make_pair( ( ri++ )->index, *ro++ ), index++ ); + // } + // } + // } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_pi( n ) && !is_dead( n ); }, /* change to PI to cycle over boxes as well */ + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n <= 1 ) /* || is_ci( n ) */ + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return f.index; }, fn ); + } +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + std::vector tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } + + void clear_values2() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[2].h2 = 0; } ); + } + + uint32_t value2( node const& n ) const + { + return _storage->nodes[n].data[2].h2; + } + + void set_value2( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[2].h2 = v; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->data.trav_id; + } + + void incr_trav_id() const + { + ++_storage->data.trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; + std::shared_ptr> _register_information; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/gia.hpp b/third-party/mockturtle/include/mockturtle/networks/gia.hpp new file mode 100644 index 00000000000..64fed98afef --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/gia.hpp @@ -0,0 +1,269 @@ +#pragma once +#ifdef ENABLE_ABC + +#include "detail/foreach.hpp" +#include + +namespace abc { + typedef struct Gia_Man_t_ Gia_Man_t; + typedef struct Gia_Obj_t_ Gia_Obj_t; + typedef struct Abc_Frame_t_ Abc_Frame_t; + + inline int Abc_LitNot( int Lit ) { assert(Lit >= 0); return Lit ^ 1; } + inline int Abc_Lit2Var( int Lit ) { assert(Lit >= 0); return Lit >> 1; } + inline int Abc_LitRegular( int Lit ) { assert(Lit >= 0); return Lit & ~01; } + + extern Gia_Obj_t * Gia_Regular( Gia_Obj_t * p ); + extern Gia_Obj_t * Gia_Not( Gia_Obj_t * p ); + extern Gia_Man_t * Gia_ManStart(int nObjsMax); + extern void Gia_ManStop(Gia_Man_t * p); + extern int Gia_ManAppendCi(Gia_Man_t * p); + extern int Gia_ManAppendCo(Gia_Man_t * p, int iLit0); + extern int Gia_ManAppendAnd2(Gia_Man_t * p, int iLit0, int iLit1); + extern Gia_Obj_t * Gia_ManObj(Gia_Man_t * p, int v); + extern int Gia_ObjIsPi(Gia_Man_t * p, Gia_Obj_t * pObj); + extern int Gia_ObjIsAnd(Gia_Obj_t * pObj); + extern int Gia_IsComplement(Gia_Obj_t * p); + extern int Gia_ManPiNum(Gia_Man_t * p); + extern int Gia_ManPoNum(Gia_Man_t * p); + extern int Gia_ManAndNum(Gia_Man_t * p); + extern int Gia_ManObjNum(Gia_Man_t * p); + extern Gia_Obj_t * Gia_ManPi(Gia_Man_t * p, int v); + extern int Gia_Obj2Lit(Gia_Man_t * p, Gia_Obj_t * pObj); + extern Gia_Obj_t * Gia_ManCi(Gia_Man_t * p, int v); + extern Gia_Obj_t * Gia_ManCo(Gia_Man_t * p, int v); + extern Gia_Obj_t * Gia_ObjFanin0(Gia_Obj_t * pObj); + extern Gia_Obj_t * Gia_ObjFanin1(Gia_Obj_t * pObj); + extern int Gia_ObjFaninC0(Gia_Obj_t * pObj); + extern int Gia_ObjFaninC1(Gia_Obj_t * pObj); + extern int Gia_ManLevelNum( Gia_Man_t * p ); + extern Gia_Obj_t * Gia_ManConst0( Gia_Man_t * p ); + extern Gia_Obj_t * Gia_ManConst1( Gia_Man_t * p ); + extern int Gia_ObjId( Gia_Man_t * p, Gia_Obj_t * pObj ); + extern Gia_Man_t * Gia_ManDup( Gia_Man_t * p ); + + extern Abc_Frame_t * Abc_FrameGetGlobalFrame(); + extern void Abc_FrameUpdateGia( Abc_Frame_t * p, Gia_Man_t * pNew ); + extern Gia_Man_t * Abc_FrameGetGia( Abc_Frame_t * p ); + extern int Cmd_CommandExecute( Abc_Frame_t * pAbc, const char * sCommand ); +} + +#include + +namespace mockturtle { + +class gia_network; + +class gia_signal { + friend class gia_network; + +public: + explicit gia_signal() = default; + explicit gia_signal(abc::Gia_Obj_t * obj) : obj_(obj) {} + + gia_signal operator!() const { return gia_signal(abc::Gia_Not(obj_)); } + gia_signal operator+() const { return gia_signal(abc::Gia_Regular(obj_)); } + gia_signal operator-() const { return gia_signal(abc::Gia_Not(abc::Gia_Regular(obj_))); } + + bool operator==(const gia_signal& other) const { return obj_ == other.obj_; } + bool operator!=(const gia_signal& other) const { return !operator==(other); } + bool operator<(const gia_signal& other) const { return obj_ < other.obj_; } + + abc::Gia_Obj_t * obj() const { return obj_; } + +private: + abc::Gia_Obj_t * obj_; +}; + +class gia_network { +public: + static constexpr auto min_fanin_size = 2u; + static constexpr auto max_fanin_size = 2u; + + using base_type = gia_network; + using node = int; + using signal = gia_signal; + using storage = abc::Gia_Man_t*; + + gia_network(int size) + : gia_(abc::Gia_ManStart(size)) /* doesn't automatically resize? */ + {} + + /* network does not implement constant value */ + bool constant_value(node n) const { (void)n; return false; } + + /* each node implements AND function */ + kitty::dynamic_truth_table node_function(node n) const { (void)n; kitty::dynamic_truth_table tt(2); tt._bits[0] = 0x8; return tt; } + + + signal get_constant(bool value) const { + return value ? signal(abc::Gia_ManConst1(gia_)) : signal(abc::Gia_ManConst0(gia_)); + } + + signal create_pi() { + return signal(abc::Gia_ManObj(gia_, abc::Abc_Lit2Var(abc::Gia_ManAppendCi(gia_)))); + } + + void create_po(const signal& f) { + /* po_node = */abc::Gia_ManAppendCo(gia_, abc::Gia_Obj2Lit(gia_, f.obj())); + } + + signal create_not(const signal& f) { + return !f; + } + + signal create_and(const signal& f, const signal& g) { + return signal(abc::Gia_ManObj(gia_, abc::Abc_Lit2Var(abc::Gia_ManAppendAnd2(gia_, abc::Gia_Obj2Lit(gia_, f.obj()), abc::Gia_Obj2Lit(gia_, g.obj()))))); + } + + bool is_constant(node n) const { + return n == 0; + } + + node get_node(const signal& f) const { + return Gia_ObjId(gia_, f.obj()); + } + + bool is_pi(node const& n) const { + return abc::Gia_ObjIsPi(gia_, abc::Gia_ManObj(gia_, n)); + } + + bool is_complemented(const signal& f) const { + return Gia_IsComplement(f.obj()); + } + + template + void foreach_pi(Fn&& fn) const { + abc::Gia_Obj_t * pObj; + for (int i = 0; (i < abc::Gia_ManPiNum(gia_)) && ((pObj) = abc::Gia_ManCi(gia_, i)); ++i) { + fn(Gia_ObjId(gia_, pObj)); + } + } + + template + void foreach_po(Fn&& fn) const { + abc::Gia_Obj_t * pObj, * pFiObj; + for (int i = 0; (i < abc::Gia_ManPoNum(gia_)) && ((pObj) = abc::Gia_ManCo(gia_, i)); ++i) { + pFiObj = abc::Gia_ObjFanin0(pObj); + fn(abc::Gia_ObjFaninC0(pObj) ? !signal(pFiObj) : signal(pFiObj)); + } + } + + template + void foreach_gate(Fn&& fn) const { + abc::Gia_Obj_t * pObj; + for (int i = 0; i < abc::Gia_ManObjNum(gia_) && ((pObj) = abc::Gia_ManObj(gia_, i)); ++i) { + if (abc::Gia_ObjIsAnd(pObj)) { + fn(Gia_ObjId(gia_, pObj)); + } + } + } + + template + void foreach_node(Fn&& fn) const { + abc::Gia_Obj_t * pObj; + for (int i = 0; i < abc::Gia_ManObjNum(gia_) && ((pObj) = abc::Gia_ManObj(gia_, i)); ++i) { + fn(signal(pObj)); + } + } + + template + void foreach_fanin(node n, Fn&& fn) const { + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + if (n == 0 || is_pi(n)) { return; } + + abc::Gia_Obj_t * pObj = abc::Gia_ManObj(gia_, n); + if constexpr ( detail::is_callable_without_index_v ) + { + if (!fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj)))) { + return; + } + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj))); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if (!fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj))), 0) { + return; + } + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj)), 1); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj))); + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj))); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj)), 0); + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj)), 1); + } + } + + int literal(const signal& f) const + { + return (abc::Gia_ObjId(gia_, f.obj()) << 1) + abc::Gia_IsComplement(f.obj()); + } + + int node_to_index(node n) const + { + return n; + } + + auto num_pis() const { return abc::Gia_ManPiNum(gia_); } + auto num_pos() const { return abc::Gia_ManPoNum(gia_); } + auto num_gates() const { return abc::Gia_ManAndNum(gia_); } + auto num_levels() const { return abc::Gia_ManLevelNum(gia_); } + auto size() const { return abc::Gia_ManObjNum(gia_); } + + bool load_rc() { + abc::Abc_Frame_t * abc = abc::Abc_FrameGetGlobalFrame(); + const int success = abc::Cmd_CommandExecute(abc, default_rc); + if (success != 0) { + printf("syntax error in script\n"); + } + return success == 0; + } + + bool run_opt_script(const std::string &script) { + abc::Gia_Man_t * gia = abc::Gia_ManDup(gia_); + abc::Abc_Frame_t * abc = abc::Abc_FrameGetGlobalFrame(); + abc::Abc_FrameUpdateGia(abc, gia); + + const int success = abc::Cmd_CommandExecute(abc, script.c_str()); + if (success != 0) { + printf("syntax error in script\n"); + } + + abc::Gia_Man_t * new_gia = abc::Abc_FrameGetGia(abc); + abc::Gia_ManStop(gia_); + gia_ = new_gia; + + return success == 0; + } + +private: + const char * default_rc = + "alias b balance;\n" + "alias rw rewrite;\n" + "alias rwz rewrite -z;\n" + "alias rf refactor;\n" + "alias rfz refactor -z;\n" + "alias rs resub;\n" + "alias rsz resub -z;\n" + "alias &r2rs '&put; b; rs -K 6; rw; rs -K 6 -N 2; rf; rs -K 8; b; rs -K 8 -N 2; rw; rs -K 10; rwz; rs -K 10 -N 2; b; rs -K 12; rfz; rs -K 12 -N 2; rwz; b; &get';\n" + "alias &c2rs '&put; b -l; rs -K 6 -l; rw -l; rs -K 6 -N 2 -l; rf -l; rs -K 8 -l; b -l; rs -K 8 -N 2 -l; rw -l; rs -K 10 -l; rwz -l; rs -K 10 -N 2 -l; b -l; rs -K 12 -l; rfz -l; rs -K 12 -N 2 -l; rwz -l; b -l; &get';\n" + "alias compress2rs 'b -l; rs -K 6 -l; rw -l; rs -K 6 -N 2 -l; rf -l; rs -K 8 -l; b -l; rs -K 8 -N 2 -l; rw -l; rs -K 10 -l; rwz -l; rs -K 10 -N 2 -l; b -l; rs -K 12 -l; rfz -l; rs -K 12 -N 2 -l; rwz -l; b -l';\n" + "alias resyn2rs 'b; rs -K 6; rw; rs -K 6 -N 2; rf; rs -K 8; b; rs -K 8 -N 2; rw; rs -K 10; rwz; rs -K 10 -N 2; b; rs -K 12; rfz; rs -K 12 -N 2; rwz; b;'\n" + "alias resyn2rs2 'b; rs -K 6; rw; rs -K 6 -N 2; rs -K 8; b; rs -K 8 -N 2; rw; rs -K 10; rwz; rs -K 10 -N 2; b; rs -K 12; rs -K 12 -N 2; rwz; b;'\n"; + +private: + abc::Gia_Man_t *gia_; +}; // gia_network + +} // mockturtle + +#endif diff --git a/third-party/mockturtle/include/mockturtle/networks/klut.hpp b/third-party/mockturtle/include/mockturtle/networks/klut.hpp new file mode 100644 index 00000000000..96a7a6f81e7 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/klut.hpp @@ -0,0 +1,707 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file klut.hpp + \brief k-LUT logic network implementation + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct klut_storage_data +{ + truth_table_cache cache; +}; + +/*! \brief k-LUT node + * + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Function literal in truth table cache + * `data[1].h2`: Visited flags + */ +struct klut_storage_node : mixed_fanin_node<2> +{ + bool operator==( klut_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; + +/*! \brief k-LUT storage container + + ... +*/ +using klut_storage = storage; + +class klut_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + using base_type = klut_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = uint64_t; + + klut_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + klut_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + + klut_network clone() const + { + return { std::make_shared( *_storage ) }; + } + +protected: + inline void _init() + { + /* already initialized */ + if ( _storage->nodes.size() > 1 ) + return; + + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant( bool value = false ) const + { + return value ? 1 : 0; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + return index; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool constant_value( node const& n ) const + { + return n == 1; + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c].data[0].h1++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return index; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( klut_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } +#pragma endregion + +#pragma region Restructuring + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto i = 0u; i < _storage->nodes.size(); ++i ) + { + auto& n = _storage->nodes[i]; + for ( auto& child : n.children ) + { + if ( child == old_node ) + { + std::vector old_children( n.children.size() ); + std::transform( n.children.begin(), n.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( i, old_children ); + } + } + } + } + + /* check outputs */ + for ( auto& output : _storage->outputs ) + { + if ( output == old_node ) + { + output = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + } + } + + // reset fan-out of old node + _storage->nodes[old_node].data[0].h1 = 0; + } + + inline bool is_dead( node const& n ) const + { + return false; + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++; + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f; + } + + signal make_signal( node const& n ) const + { + return n; + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return f.index; }, fn ); + } +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector::value_type> tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/mig.hpp b/third-party/mockturtle/include/mockturtle/networks/mig.hpp new file mode 100644 index 00000000000..c8646ac2824 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/mig.hpp @@ -0,0 +1,1249 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig.hpp + \brief MIG logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Eleonora Testa + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +/*! \brief MIG storage container + + MIGs have nodes with fan-in 3. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + `data[0].h2`: Application-specific value + `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) +*/ +using mig_storage = storage>; + +class mig_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 3u; + + using base_type = mig_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( mig_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator mig_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( mig_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + mig_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + mig_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + mig_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0].data = node.children[1].data = node.children[2].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + signal create_maj( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + else + { + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, node_complement }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + const auto f = create_maj( a, !b, c ); + const auto g = create_maj( a, b, !c ); + return create_maj( !a, f, g ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( mig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 3u ); + return create_maj( children[0u], children[1u], children[2u] ); + } +#pragma endregion + +#pragma region Has node + std::optional has_maj( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + else + { + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, node_complement ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return std::nullopt; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + + // check for trivial cases? + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + return std::make_pair( n, diff_pol ? child2 : child0 ); + } + else if ( child1.index == child2.index ) + { + const auto diff_pol = child1.complement != child2.complement; + return std::make_pair( n, diff_pol ? child0 : child1 ); + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + _hash_obj.children[2] = child2; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + // update the reference counter of the old signal + _storage->nodes[old_node].data[0].h1--; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + // update the reference counter of the old signal + _storage->nodes[old_node].data[0].h1--; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + // increment fan-out of new node + _storage->nodes[new_signal.index].data[0].h1++; + // decrement fan-out of old node + _storage->nodes[old_node].data[0].h1--; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 3u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 3u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 3; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ); + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + (void)n; + kitty::dynamic_truth_table _maj( 3 ); + _maj._bits[0] = 0xe8; + return _maj; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + // we don't use foreach_element here to have better performance + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + return ( ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ) ) || ( ( v3 ^ c3.weight ) && ( v1 ^ c1.weight ) ) || ( ( v3 ^ c3.weight ) && ( v2 ^ c2.weight ) ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + return kitty::ternary_majority( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ) | + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ) | + ( ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ); + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::mig_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/muxig.hpp b/third-party/mockturtle/include/mockturtle/networks/muxig.hpp new file mode 100644 index 00000000000..7090eae0401 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/muxig.hpp @@ -0,0 +1,235 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file muxig.hpp + \brief Mux-inverter graph logic network implementation + + \author Dewmini Marakkalage + */ + +#pragma once + +#include "tig.hpp" + +namespace mockturtle +{ + +template<> +struct compute_function +{ + template + inline std::enable_if_t::value, T> operator()( T a, T b, T c ) + { + return ternary_operation( a, b, c, []( auto a, auto b, auto c ) { return ( a & b ) | ( ~a & c ); } ); + } + + template + inline std::enable_if_t::value, T> operator()( T a, T b, T c ) + { + return ( a & b ) | ( ~a & c ); + } +}; + +using muxig_signal = tig_network::signal; +using muxig_network = tig_network; + +template<> +inline muxig_network::normalization_result muxig_network::normalized_fanins_for_and( muxig_signal a, muxig_signal b ) +{ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + if ( a.index == 0 ) + { + return { false, { a.complement ? b : get_constant( false ) } }; + } + + if ( a.index == b.index ) + { + return { false, { ( a.complement == b.complement ) ? a : get_constant( false ) } }; + } + + return { false, { !a, get_constant( false ), b } }; +} + +template<> +inline muxig_network::normalization_result muxig_network::normalized_fanins_for_xor( muxig_signal a, muxig_signal b ) +{ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + if ( a.index == 0 ) + { + return { false, { a.complement ? !b : b } }; + } + + if ( a.index == b.index ) + { + return { false, { get_constant( a.complement != b.complement ) } }; + } + + if ( b.complement ) + { + // !(!a, !b, b) or (a, !b, b) + if ( a.complement ) + { + return { true, { !a, !b, b } }; + } + return { false, { a, !b, b } }; + } + + // (!a, b, !b) or !(a, b, !b) + if ( a.complement ) + { + return { false, { !a, b, !b } }; + } + return { true, { a, b, !b } }; +} + +template<> +inline muxig_network::normalization_result muxig_network::normalized_fanins( muxig_signal a, muxig_signal b, muxig_signal c ) +{ + /* mux(a b c) = a b + a' c */ + /* always make sure that b is never inverted */ + /* always make sure that b.index <= c.index */ + /* if possible, make sure a is not inverted */ + /* if possible, make sure that a.index < b.index */ + /* else, if possible, make sure that a.index < c.index */ + + if ( a.index == 0 ) + { // (1 b c) = b, (0 b c) = c, + return { false, { a.complement ? b : c } }; + } + + if ( b.index > c.index ) + { + std::swap( b, c ); + a = !a; + } + + if ( b.index == 0 ) + { + if ( b.complement ) + { + // (a 1 c) = a + a'c = a + c = (a'c')' + return complement( normalized_fanins_for_and( !a, !c ) ); + } + // (a 0 c) = a'c + return normalized_fanins_for_and( !a, c ); + } + + if ( a.index == b.index ) + { + if ( a.complement == b.complement ) + { + // (a a c) = aa + a'c = a + c = (a'c')' + return complement( normalized_fanins_for_and( !a, !c ) ); + } + // (a a' c) = aa' + a'c = a'c + return normalized_fanins_for_and( !a, c ); + } + + if ( a.index == c.index ) + { + if ( a.complement == c.complement ) + { + // (a b a) = ab + a'a = ab + return normalized_fanins_for_and( a, b ); + } + // (a b a') == ab + a'a' = ab + a' = a' + b = (ab')' + return complement( normalized_fanins_for_and( a, !b ) ); + } + + if ( b.index == c.index ) + { + if ( b.complement == c.complement ) + { + // (a, b, b) = a b + a' b + return { false, { b } }; + } + // (a, b, b') = a b + a' b' = a xnor b = (a xor b)' + return complement( normalized_fanins_for_xor( a, b ) ); + } + + // (a b c) = a b + a' c + if ( b.complement ) + { + return { true, { a, !b, !c } }; + } + return { false, { a, b, c } }; +} + +template<> +inline muxig_signal muxig_network::create_and( muxig_signal const& a, muxig_signal const& b ) +{ + return create_gate( a, b, get_constant( false ) ); +} + +template<> +inline muxig_signal muxig_network::create_xor( muxig_signal const& a, muxig_signal const& b ) +{ + return create_gate( a, !b, b ); +} + +template<> +inline muxig_signal muxig_network::create_maj( signal const& a, signal const& b, signal const& c ) +{ + auto sel = !create_xor( a, b ); + return create_gate( sel, a, c ); +} + +template<> +inline muxig_signal muxig_network::create_ite( signal a, signal b, signal c ) +{ + return create_gate( a, b, c ); +} + +template<> +inline bool muxig_network::is_ite( node const& n ) const +{ + return true; +} + +template<> +inline bool muxig_network::is_mux( node const& n ) const +{ + return true; +} + +template<> +inline kitty::dynamic_truth_table muxig_network::node_function( const node& n ) const +{ + (void)n; + kitty::dynamic_truth_table tt( 3 ); + tt._bits[0] = 0xd8; + return tt; +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/sequential.hpp b/third-party/mockturtle/include/mockturtle/networks/sequential.hpp new file mode 100644 index 00000000000..b854a86143b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/sequential.hpp @@ -0,0 +1,925 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sequential.hpp + \brief Sequential extension to networks + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "aig.hpp" +#include "aqfp.hpp" +#include "cover.hpp" +#include "detail/foreach.hpp" +#include "klut.hpp" +#include "mig.hpp" +#include "xag.hpp" +#include "xmg.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +template +struct is_aig_like : std::false_type +{ +}; + +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; + +template +inline constexpr bool is_aig_like_v = is_aig_like::value; + +} // namespace detail + +/*! \brief Register information */ +struct register_t +{ + /*! \brief Control (clocking or enabling) signal */ + std::string control = ""; + /*! \brief Initial (reset) value */ + uint8_t init = 3; + /*! \brief Type of register or latch (active high/low, rising/falling edge) */ + std::string type = ""; +}; + +template> +class sequential +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /*! \brief Creates a register output in the network. + * + * Each created register output is stored in a node and contributes + * to the size of the network. Register outputs must be created + * after all primary inputs have been created and must have a + * corresponding register input that is created with `create_ri`. + * + * Register outputs serve as inputs for the network. + * + * Register outputs and register inputs always have to be created in + * pairs; they are associated to each other by index, i.e., the + * first created register output corresponds to the first created + * register input, etc. + */ + signal create_ro(); + + /*! \brief Creates a register input in the network. + * + * A register input is not stored in terms of a node, and it also + * does not contribute to the size of the network. A register input + * is created for a signal in the network and it is possible that + * multiple register inputs point to the same signal. Register + * inputs must be created after all primary outputs have been + * created and must have a corresponding register output that is + * created with `create_ro`. + * + * Register inputs serve as outputs for the network. + * + * Register outputs and register inputs always have to be created in + * pairs; they are associated to each other by index, i.e., the + * first created register output corresponds to the first created + * register input, etc. + * + * \param f Signal that drives the created register input + */ + uint32_t create_ri( signal const& f ); + + /*! \brief Checks whether a node is a combinational input (PI or RO). */ + bool is_ci( node const& n ) const; + + /*! \brief Checks whether a node is a register output. */ + bool is_ro( node const& n ) const; + + /*! \brief Checks whether the network is combinational. + * + * Returns true if and only if the network has no registers (neither + * register outputs nor register inputs). + */ + bool is_combinational() const; + + /*! \brief Returns the number of combinational inputs (PIs and ROs). */ + auto num_cis() const; + + /*! \brief Returns the number of combinational outputs (POs and RIs). */ + auto num_cos() const; + + /*! \brief Returns the number of registers. + * + * This number is usually equal to the number of register outputs + * and register inputs because they have to appear in pairs. During + * the construction of a network, the number of register outputs and + * register inputs may diverge. + */ + auto num_registers() const; + + /*! \brief Returns the combinational input node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational inputs (exclusive). + */ + node ci_at( uint32_t index ) const; + + /*! \brief Returns the combinational output signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational outputs (exclusive). + */ + signal co_at( uint32_t index ) const; + + /*! \brief Returns the register output node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * register outputs (exclusive). + */ + node ro_at( uint32_t index ) const; + + /*! \brief Returns the register input signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * register inputs (exclusive). + */ + signal ri_at( uint32_t index ) const; + + /*! \brief Returns the index of a combinational input node. + * + * \param n A combinational input node. + * \return A value between 0 and num_cis()-1. + */ + uint32_t ci_index( node const& n ) const; + + /*! \brief Returns the index of a combinational output signal. + * + * \param n A combinational output signal. + * \return A value between 0 and num_cos()-1. + */ + uint32_t co_index( signal const& s ) const; + + /*! \brief Returns the index of a register output node. + * + * \param n A register output node. + * \return A value between 0 and num_cis()-num_pis()-1. + */ + uint32_t ro_index( node const& n ) const; + + /*! \brief Returns the index of a register input signal. + * + * \param n A register input signal. + * \return A value between 0 and num_cos()-num_pos()-1. + */ + uint32_t ri_index( signal const& s ) const; + + /*! \brief Returns the register input signal to a register output node. + * + * \param signal A signal of a register output. + */ + signal ro_to_ri( signal const& s ) const; + + /*! \brief Returns the register output node for a register input signal. + * + * \param signal A node of a register input. + */ + node ri_to_ro( signal const& s ) const; + + /*! \brief Calls ``fn`` on every combinational input node in the network. + * + * The order is in the same order as combinational inputs have been + * created with ``create_pi`` or ``create_ro``. The parameter + * ``fn`` is any callable that must have one of the following four + * signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_ci( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every combinational output signal in the network. + * + * The order is in the same order as combinational outputs have been + * created with ``create_po`` or ``create_ri``. The function is + * called on the signal that is driving the output and may occur + * more than once in the iteration, if it drives more than one + * output. The parameter ``fn`` is any callable that must have one + * of the following four + * signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_co( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every register output node in the network. + * + * The order is in the same order as register outputs have been created with + * ``create_ro``. The parameter ``fn`` is any callable that must have one of + * the following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_ro( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every register input signal in the network. + * + * The order is in the same order as register inputs have been created with + * ``create_ri``. The function is called on the signal that is driving the + * output and may occur more than once in the iteration, if it drives more + * than one output. The parameter ``fn`` is any callable that must have one + * of the following four signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_ri( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every pair of register input signal and + * register output node in the network. + * + * Calls each pair of a register input signal and the associated + * register output node. The parameter ``fn`` is any callable that + * must have one of the following four signatures. + * - ``void(std::pair const&)`` + * - ``void(std::pair const&, uint32_t)`` + * - ``bool(std::pair const&)`` + * - ``bool(std::pair const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_register( Fn&& fn ) const; + + /*! \brief Sets the register information for an index. + * + * \param index A value between 0 (inclusive) and the number of + * registers (exclusive). + * \param reg Register information to set. + */ + void set_register( uint32_t index, register_t reg ); + + /*! \brief Returns the register information for an index. + * + * \param index A value between 0 (inclusive) and the number of + * registers (exclusive). + * \return Register information (see ``register_t``). + */ + register_t register_at( uint32_t index ) const; +}; + +template +class sequential : public Ntk +{ +public: + using base_type = typename Ntk::base_type; + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + struct sequential_information + { + uint32_t num_pis{ 0 }; + uint32_t num_pos{ 0 }; + std::vector registers; + }; + + sequential() + : _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + sequential( storage base_storage ) + : Ntk( base_storage ), _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + signal create_pi() + { + ++_sequential_storage->num_pis; + return Ntk::create_pi(); + } + + uint32_t create_po( signal const& f ) + { + ++_sequential_storage->num_pos; + return Ntk::create_po( f ); + } + + signal create_ro() + { + _sequential_storage->registers.emplace_back(); + return Ntk::create_pi(); + } + + uint32_t create_ri( signal const& f ) + { + return Ntk::create_po( f ); + } + + bool is_combinational() const + { + return ( static_cast( this->_storage->inputs.size() ) == _sequential_storage->num_pis && + static_cast( this->_storage->outputs.size() ) == _sequential_storage->num_pos ); + } + + bool is_ci( node const& n ) const + { + return Ntk::is_pi( n ); + } + + bool is_pi( node const& n ) const + { + return Ntk::is_pi( n ) && this->_storage->nodes[n].children[0].data < static_cast( _sequential_storage->num_pis ); + } + + bool is_ro( node const& n ) const + { + return Ntk::is_pi( n ) && this->_storage->nodes[n].children[0].data >= static_cast( _sequential_storage->num_pis ); + } + + auto num_cis() const + { + return static_cast( this->_storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( this->_storage->outputs.size() ); + } + + auto num_pis() const + { + return _sequential_storage->num_pis; + } + + auto num_pos() const + { + return _sequential_storage->num_pos; + } + + auto num_registers() const + { + assert( static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ) == static_cast( this->_storage->outputs.size() - _sequential_storage->num_pos ) ); + return static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pos ); + return *( this->_storage->outputs.begin() + index ); + } + + node ci_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() ); + return *( this->_storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() ); + return *( this->_storage->outputs.begin() + index ); + } + + node ro_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() - _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + index ); + } + + signal ri_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() - _sequential_storage->num_pos ); + return *( this->_storage->outputs.begin() + _sequential_storage->num_pos + index ); + } + + void set_register( uint32_t index, register_t reg ) + { + assert( index < _sequential_storage->registers.size() ); + _sequential_storage->registers[index] = reg; + } + + register_t register_at( uint32_t index ) const + { + assert( index < _sequential_storage->registers.size() ); + return _sequential_storage->registers[index]; + } + + uint32_t pi_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data < _sequential_storage->num_pis ); + return Ntk::pi_index( n ); + } + + uint32_t ci_index( node const& n ) const + { + return Ntk::pi_index( n ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + uint32_t ro_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data >= _sequential_storage->num_pis ); + return Ntk::pi_index( n ) - _sequential_storage->num_pis; + } + + uint32_t ri_index( signal const& s ) const + { + uint32_t i = -1; + foreach_ri( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + signal ro_to_ri( signal const& s ) const + { + return *( this->_storage->outputs.begin() + _sequential_storage->num_pos + this->_storage->nodes[s.index].children[0].data - _sequential_storage->num_pis ); + } + + node ri_to_ro( signal const& s ) const + { + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + ri_index( s ) ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( this->_storage->outputs.begin(), this->_storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.begin() + _sequential_storage->num_pis, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( this->_storage->outputs.begin(), this->_storage->outputs.begin() + _sequential_storage->num_pos, fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin() + _sequential_storage->num_pis, this->_storage->inputs.end(), fn ); + } + + template + void foreach_ri( Fn&& fn ) const + { + detail::foreach_element( this->_storage->outputs.begin() + _sequential_storage->num_pos, this->_storage->outputs.end(), fn ); + } + + template + void foreach_register( Fn&& fn ) const + { + static_assert( detail::is_callable_with_index_v, void> || + detail::is_callable_without_index_v, void> || + detail::is_callable_with_index_v, bool> || + detail::is_callable_without_index_v, bool> ); + + assert( this->_storage->inputs.size() - _sequential_storage->num_pis == this->_storage->outputs.size() - _sequential_storage->num_pos ); + auto ro = this->_storage->inputs.begin() + _sequential_storage->num_pis; + auto ri = this->_storage->outputs.begin() + _sequential_storage->num_pos; + if constexpr ( detail::is_callable_without_index_v, bool> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ) ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v, bool> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ), index++ ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v, void> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ) ); + } + } + else if constexpr ( detail::is_callable_with_index_v, void> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ), index++ ); + } + } + } + +public: + std::shared_ptr _sequential_storage; +}; // class sequential + +template +class sequential : public Ntk +{ +public: + using base_type = typename Ntk::base_type; + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + struct sequential_information + { + uint32_t num_pis{ 0 }; + uint32_t num_pos{ 0 }; + std::vector registers; + }; + + sequential() + : _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + sequential( storage base_storage ) + : Ntk( base_storage ), _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + signal create_pi() + { + ++_sequential_storage->num_pis; + return Ntk::create_pi(); + } + + uint32_t create_po( signal const& f ) + { + ++_sequential_storage->num_pos; + return Ntk::create_po( f ); + } + + signal create_ro() + { + _sequential_storage->registers.emplace_back(); + return Ntk::create_pi(); + } + + uint32_t create_ri( signal const& f ) + { + return Ntk::create_po( f ); + } + + bool is_combinational() const + { + return ( static_cast( this->_storage->inputs.size() ) == _sequential_storage->num_pis && + static_cast( this->_storage->outputs.size() ) == _sequential_storage->num_pos ); + } + + bool is_ci( node const& n ) const + { + return std::find( this->_storage->inputs.begin(), this->_storage->inputs.end(), n ) != this->_storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + const auto end = this->_storage->inputs.begin() + _sequential_storage->num_pis; + return std::find( this->_storage->inputs.begin(), end, n ) != end; + } + + bool is_ro( node const& n ) const + { + return std::find( this->_storage->inputs.begin() + _sequential_storage->num_pis, this->_storage->inputs.end(), n ) != this->_storage->inputs.end(); + } + + auto num_cis() const + { + return static_cast( this->_storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( this->_storage->outputs.size() ); + } + + auto num_pis() const + { + return _sequential_storage->num_pis; + } + + auto num_pos() const + { + return _sequential_storage->num_pos; + } + + auto num_registers() const + { + assert( static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ) == static_cast( this->_storage->outputs.size() - _sequential_storage->num_pos ) ); + return static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pos ); + return ( this->_storage->outputs.begin() + index )->index; + } + + node ci_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() ); + return *( this->_storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() ); + return ( this->_storage->outputs.begin() + index )->index; + } + + node ro_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() - _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + index ); + } + + signal ri_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() - _sequential_storage->num_pos ); + return ( this->_storage->outputs.begin() + _sequential_storage->num_pos + index )->index; + } + + void set_register( uint32_t index, register_t reg ) + { + assert( index < _sequential_storage->registers.size() ); + _sequential_storage->registers[index] = reg; + } + + register_t register_at( uint32_t index ) const + { + assert( index < _sequential_storage->registers.size() ); + return _sequential_storage->registers[index]; + } + + uint32_t pi_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data < _sequential_storage->num_pis ); + return Ntk::pi_index( n ); + } + + uint32_t ci_index( node const& n ) const + { + return Ntk::pi_index( n ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + uint32_t ro_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data >= _sequential_storage->num_pis ); + return Ntk::pi_index( n ) - _sequential_storage->num_pis; + } + + uint32_t ri_index( signal const& s ) const + { + uint32_t i = -1; + foreach_ri( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + signal ro_to_ri( signal const& s ) const + { + return *( this->_storage->outputs.begin() + _sequential_storage->num_pos + this->_storage->nodes[s.index].children[0].data - _sequential_storage->num_pis ); + } + + node ri_to_ro( signal const& s ) const + { + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + ri_index( s ) ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( this->_storage->outputs.begin() ); + detail::foreach_element_transform( + this->_storage->outputs.begin(), this->_storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.begin() + _sequential_storage->num_pis, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( this->_storage->outputs.begin() ); + detail::foreach_element_transform( + this->_storage->outputs.begin(), this->_storage->outputs.begin() + _sequential_storage->num_pos, []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin() + _sequential_storage->num_pis, this->_storage->inputs.end(), fn ); + } + + template + void foreach_ri( Fn&& fn ) const + { + using IteratorType = decltype( this->_storage->outputs.begin() ); + detail::foreach_element_transform( + this->_storage->outputs.begin() + _sequential_storage->num_pos, this->_storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_register( Fn&& fn ) const + { + static_assert( detail::is_callable_with_index_v, void> || + detail::is_callable_without_index_v, void> || + detail::is_callable_with_index_v, bool> || + detail::is_callable_without_index_v, bool> ); + + assert( this->_storage->inputs.size() - _sequential_storage->num_pis == this->_storage->outputs.size() - _sequential_storage->num_pos ); + auto ro = this->_storage->inputs.begin() + _sequential_storage->num_pis; + auto ri = this->_storage->outputs.begin() + _sequential_storage->num_pos; + if constexpr ( detail::is_callable_without_index_v, bool> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ) ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v, bool> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ), index++ ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v, void> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ) ); + } + } + else if constexpr ( detail::is_callable_with_index_v, void> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ), index++ ); + } + } + } + +public: + std::shared_ptr _sequential_storage; +}; // class sequential + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/storage.hpp b/third-party/mockturtle/include/mockturtle/networks/storage.hpp new file mode 100644 index 00000000000..85a2b42f68e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/storage.hpp @@ -0,0 +1,259 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file storage.hpp + \brief Configurable storage container + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Bruno Schmitt + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace mockturtle +{ + +template +struct node_pointer +{ +private: + static constexpr auto _len = sizeof( uint64_t ) * 8; + +public: + node_pointer() = default; + node_pointer( uint64_t index, uint64_t weight ) : weight( weight ), index( index ) {} + node_pointer( uint64_t data ) : data( data ) {} + + union + { + struct + { + uint64_t weight : PointerFieldSize; + uint64_t index : _len - PointerFieldSize; + }; + uint64_t data; + }; + + bool operator==( node_pointer const& other ) const + { + return data == other.data; + } + + bool operator!=( node_pointer const& other ) const + { + return data != other.data; + } +}; + +template<> +struct node_pointer<0> +{ +public: + node_pointer() = default; + node_pointer( uint64_t index ) : index( index ) {} + + union + { + uint64_t index; + uint64_t data; + }; + + bool operator==( node_pointer<0> const& other ) const + { + return data == other.data; + } +}; + +union cauint64_t +{ + uint64_t n{ 0 }; + struct + { + uint64_t h1 : 32; + uint64_t h2 : 32; + }; + struct + { + uint64_t q1 : 16; + uint64_t q2 : 16; + uint64_t q3 : 16; + uint64_t q4 : 16; + }; +}; + +template +struct regular_node +{ + using pointer_type = node_pointer; + + std::array children; + std::array data; + + bool operator==( regular_node const& other ) const + { + return children == other.children; + } +}; + +template +struct mixed_fanin_node +{ + using pointer_type = node_pointer; + + std::vector children; + std::array data; + + bool operator==( mixed_fanin_node const& other ) const + { + return children == other.children; + } +}; + +template +struct block_fanin_node +{ + using pointer_type = node_pointer; + + std::vector children; + std::vector data; + + bool operator==( block_fanin_node const& other ) const + { + return children == other.children; + } +}; + +/*! \brief Hash function for 64-bit word */ +inline uint64_t hash_block( uint64_t word ) +{ + /* from boost::hash_detail::hash_value_unsigned */ + return word ^ ( word + ( word << 6 ) + ( word >> 2 ) ); +} + +/*! \brief Combines two hash values */ +inline void hash_combine( uint64_t& seed, uint64_t other ) +{ + /* from boost::hash_detail::hash_combine_impl */ + const uint64_t m = UINT64_C( 0xc6a4a7935bd1e995 ); + const int r = 47; + + other *= m; + other ^= other >> r; + other *= m; + + seed ^= other; + seed *= m; + + seed += 0xe6546b64; +} + +template +struct node_hash +{ + uint64_t operator()( const Node& n ) const + { + if ( n.children.size() == 0 ) + return 0; + + auto it = std::begin( n.children ); + auto seed = hash_block( it->data ); + ++it; + + while ( it != std::end( n.children ) ) + { + hash_combine( seed, hash_block( it->data ) ); + ++it; + } + + return seed; + } +}; + +struct empty_storage_data +{ +}; + +template> +struct storage +{ + storage() + { + nodes.reserve( 10000u ); + hash.reserve( 10000u ); + + /* we generally reserve the first node for a constant */ + nodes.emplace_back(); + } + + using node_type = Node; + + uint32_t trav_id = 0u; + + std::vector nodes; + std::vector inputs; + std::vector outputs; + + phmap::flat_hash_map hash; + + T data; +}; + +template +struct storage_no_hash +{ + storage_no_hash() + { + nodes.reserve( 10000u ); + + /* we generally reserve the first node for a constant */ + nodes.emplace_back(); + } + + using node_type = Node; + + uint32_t trav_id = 0u; + + std::vector nodes; + std::vector inputs; + std::vector outputs; + + T data; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/tig.hpp b/third-party/mockturtle/include/mockturtle/networks/tig.hpp new file mode 100644 index 00000000000..e023badc7a6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/tig.hpp @@ -0,0 +1,1133 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file tig.hpp + \brief Three-input-gate-inverter graph logic network implementation + + \author Dewmini Marakkalage + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ +enum class three_input_function +{ + dot, + onehot, + mux, + andxor, + xorand, + gamble, + orand, + majority, + and3, + xor3 +}; + +/* Remark: Must be specialized. */ +template +struct compute_function +{ + template + std::enable_if_t::value, T> operator()( T a, T b, T c ); + + template + std::enable_if_t::value, T> operator()( T a, T b, T c ); +}; + +using tig_storage = storage>; + +struct tig_signal +{ + tig_signal() = default; + + tig_signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit tig_signal( uint64_t data ) + : data( data ) + { + } + + tig_signal( tig_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + tig_signal operator!() const + { + return tig_signal( data ^ 1 ); + } + + tig_signal operator+() const + { + return { index, 0 }; + } + + tig_signal operator-() const + { + return { index, 1 }; + } + + tig_signal operator^( bool complement ) const + { + return tig_signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( tig_signal const& other ) const + { + return data == other.data; + } + + bool operator!=( tig_signal const& other ) const + { + return data != other.data; + } + + bool operator<( tig_signal const& other ) const + { + return data < other.data; + } + + operator tig_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( tig_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif +}; + +/*! \brief T-Inverter-Graph storage container + TIGs have nodes with fan-in 3. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + `data[0].h2`: Application-specific value + `data[1].h1`: Visited flag + */ + +template +class tig_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 3u; + + using base_type = tig_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = tig_signal; + + tig_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + tig_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + tig_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0].data = node.children[1].data = node.children[2].data = _storage->inputs.size(); + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + + struct normalization_result + { + bool output_compl; + std::vector fanins; + }; + + /*! + * \brief Normalizes fanins. If the gate degenerates to a single fanin, sets + * the first fanin in the return value to the corresponding degenerate signal. + * Remark: Must be specialized in each three-input-gate-network type. + */ + normalization_result normalized_fanins( signal a, signal b, signal c ); + + signal create_gate( signal a, signal b, signal c ) + { + auto norm_res = normalized_fanins( a, b, c ); + + if ( norm_res.fanins.size() == 1u ) + { + return norm_res.fanins[0]; + } + + storage::element_type::node_type node; + node.children[0] = norm_res.fanins[0]; + node.children[1] = norm_res.fanins[1]; + node.children[2] = norm_res.fanins[2]; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, norm_res.output_compl }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, norm_res.output_compl }; + } + + /* Remark: Must be specialized. */ + signal create_and( signal const& a, signal const& b ); + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + /* Remark: To be specialized. */ + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + /* Remark: To be specialized. */ + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + return create_or( create_and( a, b ), create_and( c, create_or( a, b ) ) ); + } + + /* Remark: To be specialized. */ + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + /* Remark: To be specialized. */ + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_xor( a, create_xor( b, c ) ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( tig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 3u ); + return create_gate( children[0u], children[1u], children[2u] ); + } +#pragma endregion + +#pragma region Restructuring + + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + std::array child; + + bool found = false; + for ( auto i = 0u; i < 3u; ++i ) + { + if ( node.children[i].index == old_node ) + { + found = true; + child[i] = { new_signal.index, new_signal.complement ^ node.children[i].weight }; + } + else + { + child[i] = node.children[i]; + } + } + + if ( !found ) + { + return std::nullopt; + } + + auto norm_res = normalized_fanins( child[0], child[1], child[2] ); + if ( norm_res.fanins.size() == 1u ) + { + return std::make_pair( n, norm_res.fanins[0] ); + } + + // node already in hash table and is alive + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child[0]; + _hash_obj.children[1] = child[1]; + _hash_obj.children[2] = child[2]; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child[0]; + node.children[1] = child[1]; + node.children[2] = child[2]; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + + return std::nullopt; + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + // increment fan-in of new node + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 3u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 3u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + //if ( get_node( new_signal ) == old_node && !is_complemented( new_signal ) ) + // return; + + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + //if ( get_node( _new ) == _old && !is_complemented( _new ) ) + // continue; + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 3; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + /* One of the is_{gate} functions for three-input gates needs to be specialized. */ + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + return is_mux( n ); + } + + bool is_mux( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_and3( node const& n ) const + { + (void)n; + return false; + } + + bool is_dot( node const& n ) const + { + (void)n; + return false; + } + + bool is_onehot( node const& n ) const + { + (void)n; + return false; + } + + bool is_orand( node const& n ) const + { + (void)n; + return false; + } + + bool is_xorand( node const& n ) const + { + (void)n; + return false; + } + + bool is_andxor( node const& n ) const + { + (void)n; + return false; + } + + bool is_gamble( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + /* Remark: Must be specialized */ + kitty::dynamic_truth_table node_function( const node& n ) const; +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + // we don't use foreach_element here to have better performance + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + return compute_function()( v1 ^ c1.weight, v2 ^ c2.weight, v3 ^ c3.weight ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + return compute_function()( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = compute_function()( + c1.weight ? ~tt1._bits.back() : tt1._bits.back(), + c2.weight ? ~tt2._bits.back() : tt2._bits.back(), + c3.weight ? ~tt3._bits.back() : tt3._bits.back() ); + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; + +private: + normalization_result normalized_fanins_for_and( signal a, signal b ); + normalization_result normalized_fanins_for_xor( signal a, signal b ); + normalization_result complement( normalization_result res ) + { + res.output_compl = !res.output_compl; + if ( res.fanins.size() != 1 ) + { + return res; + } + return { false, { res.output_compl ? !res.fanins[0] : res.fanins[0] } }; + } +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::tig_signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std diff --git a/third-party/mockturtle/include/mockturtle/networks/xag.hpp b/third-party/mockturtle/include/mockturtle/networks/xag.hpp new file mode 100644 index 00000000000..17606a28d75 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/xag.hpp @@ -0,0 +1,1288 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag.hpp + \brief Xor-And Graph (XAG) logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Eleonora Testa + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +/*! \brief Hash function for XAGs -- the same as for AIGs (from ABC) */ +template +struct xag_hash +{ + uint64_t operator()( Node const& n ) const + { + uint64_t seed = -2011; + seed += n.children[0].index * 7937; + seed += n.children[1].index * 2971; + seed += n.children[0].weight * 911; + seed += n.children[1].weight * 353; + return seed; + } +}; + +/*! \brief XAG storage container + + XAGs have nodes with fan-in 2. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + `data[0].h2`: Application-specific value + `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) +*/ +using xag_storage = storage, + empty_storage_data, + xag_hash>>; + +class xag_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 2u; + static constexpr auto max_fanin_size = 2u; + + using base_type = xag_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( xag_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator xag_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( xag_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + xag_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + xag_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + xag_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0].data = node.children[1].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary functions + signal _create_node( signal a, signal b ) + { + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, 0 }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_and( signal a, signal b ) + { + /* order inputs a < b it is a AND */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + if ( a.index == b.index ) + { + return a.complement == b.complement ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement == false ? get_constant( false ) : b; + } + return _create_node( a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return create_and( !a, !b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal a, signal b ) + { + /* order inputs a > b it is a XOR */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + + bool f_compl = a.complement != b.complement; + a.complement = b.complement = false; + + if ( a.index == b.index ) + { + return get_constant( f_compl ); + } + else if ( b.index == 0 ) + { + return a ^ f_compl; + } + + return _create_node( a, b ) ^ f_compl; + } + + signal create_xnor( signal const& a, signal const& b ) + { + return !create_xor( a, b ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_xor( create_and( !cond, create_xor( f_then, f_else ) ), f_then ) ^ f_compl; + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + auto c1 = create_xor( a, b ); + auto c2 = create_xor( a, c ); + auto c3 = create_and( c1, c2 ); + return create_xor( a, c3 ); + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_xor( create_xor( a, b ), c ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( xag_network const& other, node const& source, std::vector const& children ) + { + assert( children.size() == 2u ); + if ( other.is_and( source ) ) + { + return create_and( children[0u], children[1u] ); + } + else + { + return create_xor( children[0u], children[1u] ); + } + } +#pragma endregion + +#pragma region Has node + std::optional has_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement == false ? get_constant( false ) : b; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, 0 ); + } + + return {}; + } + + std::optional has_xor( signal a, signal b ) + { + /* order inputs */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + + bool f_compl = a.complement != b.complement; + a.complement = b.complement = false; + + /* trivial cases */ + if ( a.index == b.index ) + { + return get_constant( f_compl ); + } + else if ( b.index == 0 ) + { + return a ^ f_compl; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, f_compl ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return std::nullopt; + } + + // determine gate type of n + auto _is_and = node.children[0].index <= node.children[1].index; + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( ( _is_and && child0.index > child1.index ) || ( !_is_and && child0.index < child1.index ) ) + { + std::swap( child0, child1 ); + } + + // check for trivial cases? + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + if ( _is_and ) + { + return std::make_pair( n, diff_pol ? get_constant( false ) : child1 ); + } + else + { + return std::make_pair( n, get_constant( diff_pol ) ); + } + } + else if ( _is_and && child0.index == 0 ) /* constant child */ + { + return std::make_pair( n, child0.complement ? child1 : get_constant( false ) ); + } + else if ( !_is_and && child1.index == 0 ) + { + return std::make_pair( n, child0 ^ child1.complement ); + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return; + } + + // determine gate type of n + auto _is_and = node.children[0].index <= node.children[1].index; + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( ( _is_and && child0.index > child1.index ) || ( !_is_and && child0.index < child1.index ) ) + { + std::swap( child0, child1 ); + } + + // if a buffer is created adjust the polarities + if ( child0.index == child1.index && !_is_and ) + { + if ( child0.complement == child1.complement ) + { + child0.data = 0; // the buffer is a constant zero + child1.data = 0; // the buffer is a constant zero + } + else + { + child0.data = 1; // the buffer is a constant one + child1.data = 1; // the buffer is a constant zero + } + } + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 2u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 2; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + return n > 0 && !is_ci( n ) && ( _storage->nodes[n].children[0].index <= _storage->nodes[n].children[1].index ); + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + return n > 0 && !is_ci( n ) && ( _storage->nodes[n].children[0].index > _storage->nodes[n].children[1].index ); + } + + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + kitty::dynamic_truth_table _func( 2 ); + if ( _storage->nodes[n].children[0u].index <= _storage->nodes[n].children[1u].index ) + { + _func._bits[0] = 0x8; + return _func; + } + else + { + _func._bits[0] = 0x6; + return _func; + } + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto v1 = *begin++; + auto v2 = *begin++; + + if ( c1.index <= c2.index ) + { + return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); + } + else + { + return ( v1 ^ c1.weight ) ^ ( v2 ^ c2.weight ); + } + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + if ( c1.index <= c2.index ) + { + return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); + } + else + { + return ( c1.weight ? ~tt1 : tt1 ) ^ ( c2.weight ? ~tt2 : tt2 ); + } + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + if ( c1.index <= c2.index ) + { + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + } + else + { + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) ^ ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + } + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::xag_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/xmg.hpp b/third-party/mockturtle/include/mockturtle/networks/xmg.hpp new file mode 100644 index 00000000000..536b68e7d79 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/xmg.hpp @@ -0,0 +1,1493 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xmg.hpp + \brief XMG logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +/*! \brief XMG storage container + + XMGs have nodes with fan-in 3. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + `data[0].h2`: Application-specific value + `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) +*/ +using xmg_storage = storage>; + +class xmg_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 3u; + + using base_type = xmg_network; + using storage = std::shared_ptr; + using node = std::size_t; + + struct signal + { + signal() = default; + + signal( std::size_t index, std::size_t complement ) + : complement( complement ), index( index ) + { + } + + signal( std::size_t data ) + : data( data ) + { + } + + signal( xmg_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + std::size_t complement : 1; + std::size_t index : 63; + }; + std::size_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator xmg_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( xmg_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + xmg_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + xmg_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + xmg_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0].data = node.children[1].data = node.children[2].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + signal create_maj( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + if ( b.index > c.index ) + { + std::swap( b, c ); + } + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, node_complement }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415 * index ) ); + _storage->hash.reserve( static_cast( 3.1415 * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_xor3( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + if ( b.index < c.index ) + { + std::swap( b, c ); + } + if ( a.index < b.index ) + { + std::swap( a, b ); + } + + /* propagate complement edges */ + bool fcompl = ( a.complement != b.complement ) != c.complement; + a.complement = b.complement = c.complement = false; + + /* trivial cases */ + if ( a.index == b.index ) + { + return c ^ fcompl; + } + else if ( b.index == c.index ) + { + return a ^ fcompl; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, fcompl }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415 * index ) ); + _storage->hash.reserve( static_cast( 3.1415 * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, fcompl }; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + return create_xor3( get_constant( false ), a, b ); + } + + signal create_xnor( signal const& a, signal const& b ) + { + return create_xor3( get_constant( true ), a, b ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return ternary_tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b, auto const& c ) { return create_xor3( a, b, c ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( xmg_network const& other, node const& source, std::vector const& children ) + { + assert( children.size() == 3u ); + + if ( other.is_maj( source ) ) + { + return create_maj( children[0u], children[1u], children[2u] ); + } + else + { + return create_xor3( children[0u], children[1u], children[2u] ); + } + } +#pragma endregion + +#pragma region Has node + std::optional has_maj( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + if ( b.index > c.index ) + { + std::swap( b, c ); + } + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, node_complement ); + } + + return {}; + } + + std::optional has_xor3( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + if ( b.index < c.index ) + { + std::swap( b, c ); + } + if ( a.index < b.index ) + { + std::swap( a, b ); + } + + /* propagate complement edges */ + bool fcompl = ( a.complement != b.complement ) != c.complement; + a.complement = b.complement = c.complement = false; + + /* trivial cases */ + if ( a.index == b.index ) + { + return c ^ fcompl; + } + else if ( b.index == c.index ) + { + return a ^ fcompl; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, fcompl ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return std::nullopt; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + auto _is_maj = is_maj( n ); + + /* normalize order */ + if ( _is_maj ) + { + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + } + else + { + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index < child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index >= child1.index ); + assert( child1.index >= child2.index ); + } + + // normalize complemented edges + auto node_complement = false; + if ( _is_maj ) + { + if ( static_cast( child0.complement ) + static_cast( child1.complement ) + + static_cast( child2.complement ) >= + 2u ) + { + node_complement = true; + child0.complement = !child0.complement; + child1.complement = !child1.complement; + child2.complement = !child2.complement; + } + } + else + { + node_complement = ( child0.complement != child1.complement ) != child2.complement; + child0.complement = child1.complement = child2.complement = false; + } + + // check for trivial cases? + if ( _is_maj ) + { + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + return std::make_pair( n, ( diff_pol ? child2 : child0 ) ^ node_complement ); + } + else if ( child1.index == child2.index ) + { + const auto diff_pol = child1.complement != child2.complement; + return std::make_pair( n, ( diff_pol ? child0 : child1 ) ^ node_complement ); + } + } + else + { + if ( child0.index == child1.index ) + { + return std::make_pair( n, child2 ^ node_complement ); + } + else if ( child1.index == child2.index ) + { + return std::make_pair( n, child0 ^ node_complement ); + } + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + _hash_obj.children[2] = child2; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + auto _is_maj = is_maj( n ); + + /* normalize order */ + if ( _is_maj ) + { + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + } + else + { + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index < child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index >= child1.index ); + assert( child1.index >= child2.index ); + + // check same fanins: transform the XOR3 into a MAJ3 + if ( child0.index == child1.index ) + { + child0.index = child2.index; + child1.index = child2.index; + if ( child0.complement == child1.complement ) + { + child0.complement = child2.complement; + child1.complement = child2.complement; + } + else + { + child0.complement = !child2.complement; + child1.complement = !child2.complement; + } + + _is_maj = true; + } + } + + // normalize complemented edges + auto node_complement = false; + if ( _is_maj ) + { + if ( static_cast( child0.complement ) + static_cast( child1.complement ) + + static_cast( child2.complement ) >= + 2u ) + { + node_complement = true; + child0.complement = !child0.complement; + child1.complement = !child1.complement; + child2.complement = !child2.complement; + } + } + else + { + node_complement = ( child0.complement != child1.complement ) != child2.complement; + child0.complement = child1.complement = child2.complement = false; + } + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 3u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 3u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } +#pragma endregion + +#pragma region Structural properties + uint32_t size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + uint32_t num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 3; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ) && _storage->nodes[n].children[0].index <= _storage->nodes[n].children[1].index; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + return n > 0 && !is_ci( n ) && _storage->nodes[n].children[0].index > _storage->nodes[n].children[1].index; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + kitty::dynamic_truth_table _tt( 3 ); + _tt._bits[0] = is_xor3( n ) ? 0x96 : 0xe8; + return _tt; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && + _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + // we don't use foreach_element here to have better performance + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + if ( is_xor3( n ) ) + { + return ( ( v1 ^ c1.weight ) != ( v2 ^ c2.weight ) ) != ( v3 ^ c3.weight ); + } + else + { + return ( ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ) ) || ( ( v3 ^ c3.weight ) && ( v1 ^ c1.weight ) ) || ( ( v3 ^ c3.weight ) && ( v2 ^ c2.weight ) ); + } + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + if ( is_xor3( n ) ) + { + return ( c1.weight ? ~tt1 : tt1 ) ^ ( c2.weight ? ~tt2 : tt2 ) ^ ( c3.weight ? ~tt3 : tt3 ); + } + else + { + return kitty::ternary_majority( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + if ( is_xor3( n ) ) + { + result._bits.back() = + ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) ^ + ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ^ + ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ); + } + else + { + result._bits.back() = + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ) | + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ) | + ( ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ); + } + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::xmg_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/properties/aqfpcost.hpp b/third-party/mockturtle/include/mockturtle/properties/aqfpcost.hpp new file mode 100644 index 00000000000..dd1503ad69d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/properties/aqfpcost.hpp @@ -0,0 +1,237 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfpcost.hpp + \brief Cost functions for AQFP networks + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../utils/hash_functions.hpp" +#include "../views/fanout_view.hpp" + +#include "../algorithms/aqfp/aqfp_assumptions.hpp" + +namespace mockturtle +{ + +/*! \brief Cost function for computing the best splitter and buffer cost for a fanout net with given relative levels. */ +class fanout_net_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + fanout_net_cost( const std::unordered_map& splitters ) + : buffer_cost( splitters.at( 1u ) ), splitters( remove_buffer( splitters ) ) + { + } + + double operator()( const std::vector& config ) + { + return cost_for_config( config, false ); + } + + double operator()( const std::vector& config, bool ignore_initial_buffers ) + { + return cost_for_config( config, ignore_initial_buffers ); + } + +private: + using cache_key_t = std::tuple>; + + double buffer_cost; + std::unordered_map splitters; + std::unordered_map> cache; + + static std::unordered_map remove_buffer( std::unordered_map splitters ) + { + splitters.erase( 1u ); + return splitters; + } + + double cost_for_config( const std::vector config, bool ignore_initial_buffers ) + { + if ( config.size() == 1 ) + { + if ( config[0] >= 1 ) + { + return ignore_initial_buffers ? 0.0 : ( config[0] - 1 ) * buffer_cost; + } + else + { + return IMPOSSIBLE; + } + } + + std::tuple key = { ignore_initial_buffers, config }; + if ( cache.count( key ) ) + { + return cache[key]; + } + + auto result = IMPOSSIBLE; + + for ( const auto& s : splitters ) + { + for ( auto size = 2u; size <= std::min( s.first, uint32_t( config.size() ) ); size++ ) + { + auto sp_lev = config[config.size() - size] - 1; + if ( sp_lev == 0 ) + { + continue; + } + + auto temp = s.second; + + for ( auto i = config.size() - size; i < config.size(); i++ ) + { + temp += ( config[i] - config[config.size() - size] ) * buffer_cost; + } + + std::vector new_config( config.begin(), config.begin() + ( config.size() - size ) ); + new_config.push_back( sp_lev ); + std::stable_sort( new_config.begin(), new_config.end() ); + + temp += cost_for_config( new_config, ignore_initial_buffers ); + + if ( temp < result ) + { + result = temp; + } + } + } + + return ( cache[key] = result ); + } +}; + +/*! \brief Cost function for computing the cost of a path-balanced AQFP network with a given assignment of node levels. + * + * Assumes no path balancing or splitters are needed for primary inputs or register outputs. + */ +struct aqfp_network_cost +{ + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + aqfp_network_cost( const aqfp_assumptions& assume, const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : assume( assume ), gate_costs( gate_costs ), fanout_cc( splitters ) {} + + template + double operator()( const Ntk& ntk, const LevelMap& level_of_node, const PoLevelMap& po_level_of_node ) + { + /* Collect fanouts of each node. + Cannot use the fanout_view as duplicate fanis are not accounted with the correct multiplicity. */ + std::unordered_map, std::vector>> fanouts; + ntk.foreach_gate( [&]( auto n ) { ntk.foreach_fanin( n, [&]( auto fi ) { fanouts[ntk.get_node( fi )].push_back( n ); } ); } ); + + auto gate_cost = 0.0; + auto fanout_net_cost = 0.0; + + std::vector> nodes; + if ( assume.branch_pis ) + { + ntk.foreach_ci( [&]( auto n ) { nodes.push_back( n ); } ); + } + ntk.foreach_gate( [&]( auto n ) { nodes.push_back( n ); } ); + + if ( po_level_of_node.size() == 0 ) + { + std::cout << "[w] - the map po_level_of_node is empty!\n"; + } + + size_t critical_po_level = std::max_element( po_level_of_node.begin(), po_level_of_node.end(), []( auto n1, auto n2 ) { return n1.second < n2.second; } ) + ->second; + for ( auto n : nodes ) + { + if ( !ntk.is_ci( n ) ) // n must be a gate + { + gate_cost += gate_costs.at( ntk.fanin_size( n ) ); + } + + if ( ntk.fanout_size( n ) == 0 ) + { + std::cout << fmt::format( "[w] - dangling node {}\n", n ); + continue; + } + + std::vector rellev; + + for ( auto fo : fanouts[n] ) + { + assert( level_of_node.at( fo ) > level_of_node.at( n ) ); + rellev.push_back( level_of_node.at( fo ) - level_of_node.at( n ) ); + } + + uint32_t pos = 0u; + while ( rellev.size() < ntk.fanout_size( n ) ) + { + pos++; + if ( assume.balance_pos ) + { + rellev.push_back( critical_po_level + 1 - level_of_node.at( n ) ); + } + else + { + rellev.push_back( po_level_of_node.at( n ) + 1 - level_of_node.at( n ) ); + } + } + + if ( rellev.size() > 1u || ( rellev.size() == 1u && rellev[0] > 0 ) ) + { + std::stable_sort( rellev.begin(), rellev.end() ); + auto net_cost = fanout_cc( rellev, ntk.is_ci( n ) && !assume.balance_pis ); + if ( net_cost == std::numeric_limits::infinity() ) + { + std::cerr << fmt::format( "[e] impossible to synthesize fanout net of node {} for relative levels [{}]\n", n, fmt::join( rellev, " " ) ); + std::abort(); + } + fanout_net_cost += net_cost; + } + else + { + std::cerr << fmt::format( "[e] invalid level assignment for node {} with levels [{}]\n", n, fmt::join( rellev, " " ) ); + std::abort(); + } + } + + return gate_cost + fanout_net_cost; + } + +private: + aqfp_assumptions assume; + std::unordered_map gate_costs; + fanout_net_cost fanout_cc; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/properties/litcost.hpp b/third-party/mockturtle/include/mockturtle/properties/litcost.hpp new file mode 100644 index 00000000000..5e2fad34038 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/properties/litcost.hpp @@ -0,0 +1,270 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file litcost.hpp + \brief Cost function based on the factored literal cost + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include "../utils/sop_utils.hpp" + +namespace mockturtle +{ + +namespace detail +{ +uint32_t count_literals_rec( std::vector&, uint32_t const ); + +uint32_t count_term_literals( uint64_t const term, uint32_t const num_lit ) +{ + uint32_t lit = 0; + + for ( auto i = 0u; i < num_lit; ++i ) + { + if ( cube_has_lit( term, i ) ) + ++lit; + } + + return lit; +} + +uint32_t lit_factor_count_rec( std::vector const& sop, uint64_t const c_sop, uint32_t const num_lit ) +{ + using sop_t = std::vector; + + sop_t divisor, quotient, reminder; + + /* extract the best literal */ + sop_best_literal( sop, divisor, c_sop, num_lit ); + + /* divide SOP by the literal */ + sop_divide_by_cube( sop, divisor, quotient, reminder ); + + /* count literals in the divisor: cube */ + uint32_t div_lit = count_term_literals( divisor[0], num_lit ); + + /* factor the quotient */ + uint32_t quot_lit = count_literals_rec( quotient, num_lit ); + + /* factor the reminder */ + if ( reminder.size() != 0 ) + { + return div_lit + quot_lit + count_literals_rec( reminder, num_lit ); + } + + return div_lit + quot_lit; +} + +uint32_t count_literals_rec( std::vector& sop, uint32_t const num_lit ) +{ + using sop_t = std::vector; + + assert( sop.size() ); + + sop_t divisor, quotient, reminder; + + /* compute the divisor */ + if ( !sop_quick_divisor( sop, divisor, num_lit ) ) + { + /* count_literals of the current SOP */ + return sop_count_literals( sop ); + } + + /* divide the SOP by the divisor */ + sop_divide( sop, divisor, quotient, reminder ); + + assert( quotient.size() > 0 ); + + if ( quotient.size() == 1 ) + { + return lit_factor_count_rec( sop, quotient[0], num_lit ); + } + + sop_make_cube_free( quotient ); + + /* divide the SOP by the quotient */ + sop_divide( sop, quotient, divisor, reminder ); + + if ( sop_is_cube_free( divisor ) ) + { + uint32_t div_lit = count_literals_rec( divisor, num_lit ); + uint32_t quot_lit = count_literals_rec( quotient, num_lit ); + + if ( reminder.size() ) + { + return div_lit + quot_lit + count_literals_rec( reminder, num_lit ); + } + + return div_lit + quot_lit; + } + + /* get the common cube */ + uint64_t cube = UINT64_MAX; + for ( auto const& c : divisor ) + { + cube &= c; + } + + return lit_factor_count_rec( sop, cube, num_lit ); +} + +} // namespace detail + +/*! \brief Counts number of literals of the factored form of a SOP. + * + * This method computes the factored form of the SOP and + * returns its number of factored form literals. + * + * \param sop Sum-of-products + * \param num_vars Number of variables + */ +uint32_t factored_literal_cost( std::vector const& sop, uint32_t num_vars ) +{ + /* trivial cases: constant 0 or 1 */ + if ( sop.size() == 0 || sop.size() == 1 && sop[0]._mask == 0 ) + return 0; + + using sop_t = std::vector; + sop_t lit_sop = cubes_to_sop( sop, num_vars ); + + return detail::count_literals_rec( lit_sop, num_vars * 2 ); +} + +/*! \brief Counts number of literals of the factored form of a SOP. + * + * This method computes the factored form of a completely specified + * function given as a truth table and returns its number of + * factored form literals. + * + * \param tt function as truth table + * \param try_both_polarities factoring is also tried for the negated TT + */ +uint32_t factored_literal_cost( kitty::dynamic_truth_table const& tt, bool try_both_polarities = false ) +{ + if ( kitty::is_const0( tt ) || kitty::is_const0( ~tt ) ) + { + /* constant */ + return 0; + } + + std::vector cubes = kitty::isop( tt ); + + if ( try_both_polarities ) + { + std::vector n_cubes = kitty::isop( ~tt ); + + if ( n_cubes.size() < cubes.size() ) + { + cubes = n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + cubes = n_cubes; + } + } + } + + return factored_literal_cost( cubes, tt.num_vars() ); +} + +/*! \brief Counts number of literals of the factored form of a SOP. + * + * This method computes the factored form of an incompletely specified + * function given as a truth table and its don't care set and returns + * its number of factored form literals. + * + * \param tt function as truth table + * \param dc don't care set + * \param try_both_polarities factoring is also tried for the negated TT + */ +uint32_t factored_literal_cost( kitty::dynamic_truth_table const& tt, kitty::dynamic_truth_table const& dc, bool try_both_polarities = false ) +{ + if ( kitty::is_const0( tt & ( ~dc ) ) || kitty::is_const0( ~( tt | dc ) ) ) + { + /* constant */ + return 0; + } + + std::vector cubes; + kitty::detail::isop_rec( tt & ~dc, tt | dc, tt.num_vars(), cubes ); + + if ( try_both_polarities ) + { + std::vector n_cubes; + kitty::detail::isop_rec( ~tt & ~dc, ~tt | dc, tt.num_vars(), n_cubes ); + + if ( n_cubes.size() < cubes.size() ) + { + cubes = n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + cubes = n_cubes; + } + } + } + + return factored_literal_cost( cubes, tt.num_vars() ); +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/properties/mccost.hpp b/third-party/mockturtle/include/mockturtle/properties/mccost.hpp new file mode 100644 index 00000000000..59622c653ab --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/properties/mccost.hpp @@ -0,0 +1,301 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mccost.hpp + \brief Cost functions based on multiplicative-complexity + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Computes the multiplicative complexity + * + * Computes and sums the multiplicative complexity of each gate in the network. + * Returns `std::nullopt`, if multiplicative complexity cannot be determined + * for some gate. + * + * \param ntk Network + */ +template +std::optional multiplicative_complexity( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + uint32_t total{ 0u }; + bool valid{ true }; + + ntk.foreach_gate( [&]( auto const& n ) { + if constexpr ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + return true; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_ite_v ) + { + if ( ntk.is_ite( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + return true; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + if ( ntk.fanin_size( n ) > 1u ) + { + total += ntk.fanin_size( n ) - 1u; + } + return true; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + if ( ntk.fanin_size( n ) > 1u ) + { + total += ntk.fanin_size( n ) - 1u; + } + return true; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + return true; + } + } + + valid = false; + return false; /* break */ + } ); + + if ( valid ) + { + return total; + } + else + { + return std::nullopt; + } +} + +/*! \brief Computes the multiplicative complexity depth + * + * Computes multiplicative complexity of each gate and the sum of them on the + * critical path in the network. Returns `std::nullopt`, if multiplicative + * complexity cannot be determined for some gate. + * + * \param ntk Network + */ +template +std::optional multiplicative_complexity_depth( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + + bool valid{ true }; + + node_map level( ntk, 0u ); + topo_view topo{ ntk }; + + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + { + return true; + } + + /* compute the maximum MC of children */ + uint32_t max_level{ 0u }; + ntk.foreach_fanin( n, [&]( const auto& f ) { + if ( level[f] > max_level ) + { + max_level = level[f]; + } + } ); + + if ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + level[n] = max_level; + return true; + } + } + + if ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_ite_v ) + { + if ( ntk.is_ite( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + level[n] = max_level; + return true; + } + } + + if ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + level[n] = max_level + static_cast( std::ceil( std::log2( ntk.fanin_size( n ) ) ) ); + return true; + } + } + + if ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + level[n] = max_level + static_cast( std::ceil( std::log2( ntk.fanin_size( n ) ) ) ); + return true; + } + } + + if ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + level[n] = max_level; + return true; + } + } + + valid = false; + return false; /* break */ + } ); + + if ( valid ) + { + uint32_t max_level{ 0u }; + ntk.foreach_po( [&]( const auto& f ) { + if ( level[f] > max_level ) + { + max_level = level[f]; + } + } ); + return max_level; + } + else + { + return std::nullopt; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/properties/migcost.hpp b/third-party/mockturtle/include/mockturtle/properties/migcost.hpp new file mode 100644 index 00000000000..82342b14bfd --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/properties/migcost.hpp @@ -0,0 +1,117 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file migcost.hpp + \brief Cost functions for majority-based technologies + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Counts number of inverters. + * + * This number counts all nodes that need to be inverted. Multiple signals + * with complements to the same node are counted once. + * + * \param ntk Network + */ +template +uint32_t num_inverters( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + + std::unordered_set> inverted_nodes; + + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + inverted_nodes.insert( ntk.get_node( f ) ); + } + } ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + inverted_nodes.insert( ntk.get_node( f ) ); + } + } ); + + return static_cast( inverted_nodes.size() ); +} + +/*! \brief Counts fanins which are primary inputs. + * + * \param ntk Network + */ +template +uint32_t num_dangling_inputs( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + + uint32_t costs{ 0u }; + + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_pi( ntk.get_node( f ) ) ) + { + costs++; + } + } ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_pi( ntk.get_node( f ) ) ) + { + costs++; + } + } ); + + return costs; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/properties/xmgcost.hpp b/third-party/mockturtle/include/mockturtle/properties/xmgcost.hpp new file mode 100644 index 00000000000..a6a44097c4b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/properties/xmgcost.hpp @@ -0,0 +1,129 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xmgcost.hpp + \brief Cost functions for xmg-based networks + + \author Heinz Riener + \author Shubham Rai +*/ + +#pragma once + +#include "../traits.hpp" + +#include + +namespace mockturtle +{ + +struct xmg_gate_stats +{ + /*! \brief Total number of XOR3 gates (structurally). */ + uint32_t total_xor3{ 0 }; + + /*! \brief Number of XOR3 (functionally). */ + uint32_t xor3{ 0 }; + + /*! \brief Number of XOR2 (functionally). */ + uint32_t xor2{ 0 }; + + /*! \brief Total number of MAJ gates (structurally). */ + uint32_t total_maj{ 0 }; + + /*! \brief Number of MAJ gates. */ + uint32_t maj{ 0 }; + + /*! \brief Number of AND/OR gates. */ + uint32_t and_or{ 0 }; + + void report() const + { + fmt::print( "XOR3: {} = {} XOR3 + {} XOR2 / MAJ: {} = {} MAJ3 + {} AND/OR\n", + total_xor3, xor2, xor3, total_maj, maj, and_or ); + } +}; + +/*! \brief Profile gates + * + * Counts the numbers of MAJ and XOR nodes in an XMG. + * + * \param ntk Network + * \param stats Statistics + */ +template +void xmg_profile_gates( Ntk const& ntk, xmg_gate_stats& stats ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_xor3 method" ); + + ntk.foreach_gate( [&]( auto const& node ) { + bool has_const_fanin = false; + + /* Check if all of the fanin nodes are not constant */ + ntk.foreach_fanin( node, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + has_const_fanin = true; + return false; + } + return true; + } ); + + if ( ntk.is_maj( node ) ) + { + if ( has_const_fanin ) + { + ++stats.and_or; + } + else + { + ++stats.maj; + } + } + else if ( ntk.is_xor3( node ) ) + { + if ( has_const_fanin ) + { + ++stats.xor2; + } + else + { + ++stats.xor3; + } + } + } ); + + stats.total_xor3 = stats.xor2 + stats.xor3; + stats.total_maj = stats.and_or + stats.maj; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/traits.hpp b/third-party/mockturtle/include/mockturtle/traits.hpp new file mode 100644 index 00000000000..28087332182 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/traits.hpp @@ -0,0 +1,2674 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file traits.hpp + \brief Type traits and checkers for the network interface + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +template +using signal = typename Ntk::signal; + +template +using node = typename Ntk::node; + +template +struct is_network_type : std::false_type +{ +}; + +template +struct is_network_type, node>, + std::void_t, + node, + typename Ntk::storage, + decltype( Ntk::max_fanin_size ), + decltype( Ntk::min_fanin_size )>>> : std::true_type +{ +}; + +template +inline constexpr bool is_network_type_v = is_network_type::value; + +#pragma region is_aig_network_type +template +struct is_aig_network_type : std::false_type +{ +}; + +template +struct is_aig_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_aig_network_type_v = is_aig_network_type::value; +#pragma endregion + +#pragma region is_buffered_network_type +template +struct is_buffered_network_type : std::false_type +{ +}; + +template +struct is_buffered_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_buffered_network_type_v = is_buffered_network_type::value; +#pragma endregion + +#pragma region is_crossed_network_type +template +struct is_crossed_network_type : std::false_type +{ +}; + +template +struct is_crossed_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_crossed_network_type_v = is_crossed_network_type::value; +#pragma endregion + +#pragma region has_clone +template +struct has_clone : std::false_type +{ +}; + +template +struct has_clone().clone() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clone_v = has_clone::value; +#pragma endregion + +#pragma region is_topologically_sorted +template +struct is_topologically_sorted : std::false_type +{ +}; + +template +struct is_topologically_sorted>> : std::true_type +{ +}; + +template +inline constexpr bool is_topologically_sorted_v = is_topologically_sorted::value; +#pragma endregion + +#pragma region has_get_constant +template +struct has_get_constant : std::false_type +{ +}; + +template +struct has_get_constant().get_constant( bool() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_constant_v = has_get_constant::value; +#pragma endregion + +#pragma region has_create_pi +template +struct has_create_pi : std::false_type +{ +}; + +template +struct has_create_pi().create_pi() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_pi_v = has_create_pi::value; +#pragma endregion + +#pragma region has_create_po +template +struct has_create_po : std::false_type +{ +}; + +template +struct has_create_po().create_po( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_po_v = has_create_po::value; +#pragma endregion + +#pragma region has_create_ro +template +struct has_create_ro : std::false_type +{ +}; + +template +struct has_create_ro().create_ro() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ro_v = has_create_ro::value; +#pragma endregion + +#pragma region has_create_ri +template +struct has_create_ri : std::false_type +{ +}; + +template +struct has_create_ri().create_ri( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ri_v = has_create_ri::value; +#pragma endregion + +#pragma region has_is_combinational +template +struct has_is_combinational : std::false_type +{ +}; + +template +struct has_is_combinational().is_combinational() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_combinational_v = has_is_combinational::value; +#pragma endregion + +#pragma region has_is_constant +template +struct has_is_constant : std::false_type +{ +}; + +template +struct has_is_constant().is_constant( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_constant_v = has_is_constant::value; +#pragma endregion + +#pragma region has_is_ci +template +struct has_is_ci : std::false_type +{ +}; + +template +struct has_is_ci().is_ci( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ci_v = has_is_ci::value; +#pragma endregion + +#pragma region has_is_pi +template +struct has_is_pi : std::false_type +{ +}; + +template +struct has_is_pi().is_pi( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_pi_v = has_is_pi::value; +#pragma endregion + +#pragma region has_is_ro +template +struct has_is_ro : std::false_type +{ +}; + +template +struct has_is_ro().is_ro( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ro_v = has_is_ro::value; +#pragma endregion + +#pragma region has_is_ro +template +struct has_is_multioutput : std::false_type +{ +}; + +template +struct has_is_multioutput().is_multioutput( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_multioutput_v = has_is_multioutput::value; +#pragma endregion + +#pragma region has_constant_value +template +struct has_constant_value : std::false_type +{ +}; + +template +struct has_constant_value().constant_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_constant_value_v = has_constant_value::value; +#pragma endregion + +#pragma region has_create_buf +template +struct has_create_buf : std::false_type +{ +}; + +template +struct has_create_buf().create_buf( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_buf_v = has_create_buf::value; +#pragma endregion + +#pragma region has_create_not +template +struct has_create_not : std::false_type +{ +}; + +template +struct has_create_not().create_not( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_not_v = has_create_not::value; +#pragma endregion + +#pragma region has_create_and +template +struct has_create_and : std::false_type +{ +}; + +template +struct has_create_and().create_and( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_and_v = has_create_and::value; +#pragma endregion + +#pragma region has_create_nand +template +struct has_create_nand : std::false_type +{ +}; + +template +struct has_create_nand().create_nand( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nand_v = has_create_nand::value; +#pragma endregion + +#pragma region has_create_or +template +struct has_create_or : std::false_type +{ +}; + +template +struct has_create_or().create_or( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_or_v = has_create_or::value; +#pragma endregion + +#pragma region has_create_nor +template +struct has_create_nor : std::false_type +{ +}; + +template +struct has_create_nor().create_nor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nor_v = has_create_nor::value; +#pragma endregion + +#pragma region has_create_lt +template +struct has_create_lt : std::false_type +{ +}; + +template +struct has_create_lt().create_lt( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_lt_v = has_create_lt::value; +#pragma endregion + +#pragma region has_create_le +template +struct has_create_le : std::false_type +{ +}; + +template +struct has_create_le().create_le( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_le_v = has_create_le::value; +#pragma endregion + +#pragma region has_create_gt +template +struct has_create_gt : std::false_type +{ +}; + +template +struct has_create_gt().create_gt( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_gt_v = has_create_gt::value; +#pragma endregion + +#pragma region has_create_ge +template +struct has_create_ge : std::false_type +{ +}; + +template +struct has_create_ge().create_ge( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ge_v = has_create_ge::value; +#pragma endregion + +#pragma region has_create_xor +template +struct has_create_xor : std::false_type +{ +}; + +template +struct has_create_xor().create_xor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xor_v = has_create_xor::value; +#pragma endregion + +#pragma region has_create_xnor +template +struct has_create_xnor : std::false_type +{ +}; + +template +struct has_create_xnor().create_xnor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xnor_v = has_create_xnor::value; +#pragma endregion + +#pragma region has_create_maj +template +struct has_create_maj : std::false_type +{ +}; + +template +struct has_create_maj().create_maj( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_maj_v = has_create_maj::value; +#pragma endregion + +#pragma region has_create_maj_odd +template +struct has_create_maj_odd : std::false_type +{ +}; + +template +struct has_create_maj_odd().create_maj( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_maj_odd_v = has_create_maj_odd::value; +#pragma endregion + +#pragma region has_create_ite +template +struct has_create_ite : std::false_type +{ +}; + +template +struct has_create_ite().create_ite( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ite_v = has_create_ite::value; +#pragma endregion + +#pragma region has_create_xor3 +template +struct has_create_xor3 : std::false_type +{ +}; + +template +struct has_create_xor3().create_xor3( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xor3_v = has_create_xor3::value; +#pragma endregion + +#pragma region has_create_nary_and +template +struct has_create_nary_and : std::false_type +{ +}; + +template +struct has_create_nary_and().create_nary_and( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_and_v = has_create_nary_and::value; +#pragma endregion + +#pragma region has_create_nary_or +template +struct has_create_nary_or : std::false_type +{ +}; + +template +struct has_create_nary_or().create_nary_or( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_or_v = has_create_nary_or::value; +#pragma endregion + +#pragma region has_create_nary_xor +template +struct has_create_nary_xor : std::false_type +{ +}; + +template +struct has_create_nary_xor().create_nary_xor( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_xor_v = has_create_nary_xor::value; +#pragma endregion + +#pragma region has_create_node +template +struct has_create_node : std::false_type +{ +}; + +template +struct has_create_node().create_node( std::declval>>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_node_v = has_create_node::value; +#pragma endregion + +#pragma region has_create_cover_node +template +struct has_create_cover_node : std::false_type +{ +}; + +template +struct has_create_cover_node().create_cover_node( std::declval>>(), std::declval, bool>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_cover_node_v = has_create_cover_node::value; +#pragma endregion + +#pragma region has_create_crossing +template +struct has_create_crossing : std::false_type +{ +}; + +template +struct has_create_crossing().create_crossing( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_crossing_v = has_create_crossing::value; +#pragma endregion + +#pragma region has_insert_crossing +template +struct has_insert_crossing : std::false_type +{ +}; + +template +struct has_insert_crossing().insert_crossing( std::declval>(), std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_insert_crossing_v = has_insert_crossing::value; +#pragma endregion + +#pragma region has_merge_into_crossing +template +struct has_merge_into_crossing : std::false_type +{ +}; + +template +struct has_merge_into_crossing().merge_into_crossing( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_merge_into_crossing_v = has_merge_into_crossing::value; +#pragma endregion + +#pragma region has_clone_node +template +struct has_clone_node : std::false_type +{ +}; + +template +struct has_clone_node().clone_node( std::declval(), std::declval>(), std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clone_node_v = has_clone_node::value; +#pragma endregion + +#pragma region has_has_and +template +struct has_has_and : std::false_type +{ +}; + +template +struct has_has_and().has_and( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_and_v = has_has_and::value; +#pragma endregion + +#pragma region has_has_xor +template +struct has_has_xor : std::false_type +{ +}; + +template +struct has_has_xor().has_xor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_xor_v = has_has_xor::value; +#pragma endregion + +#pragma region has_has_maj +template +struct has_has_maj : std::false_type +{ +}; + +template +struct has_has_maj().has_maj( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_maj_v = has_has_maj::value; +#pragma endregion + +#pragma region has_has_xor3 +template +struct has_has_xor3 : std::false_type +{ +}; + +template +struct has_has_xor3().has_xor3( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_xor3_v = has_has_xor3::value; +#pragma endregion + +#pragma region has_substitute_node +template +struct has_substitute_node : std::false_type +{ +}; + +template +struct has_substitute_node().substitute_node( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_substitute_node_v = has_substitute_node::value; +#pragma endregion + +#pragma region has_substitute_nodes +template +struct has_substitute_nodes : std::false_type +{ +}; + +template +struct has_substitute_nodes().substitute_nodes( std::declval, signal>>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_substitute_nodes_v = has_substitute_nodes::value; +#pragma endregion + +#pragma region has_replace_in_node +template +struct has_replace_in_node : std::false_type +{ +}; + +template +struct has_replace_in_node().replace_in_node( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_replace_in_node_v = has_replace_in_node::value; +#pragma endregion + +#pragma region has_replace_in_outputs +template +struct has_replace_in_outputs : std::false_type +{ +}; + +template +struct has_replace_in_outputs().replace_in_outputs( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_replace_in_outputs_v = has_replace_in_outputs::value; +#pragma endregion + +#pragma region has_take_out_node +template +struct has_take_out_node : std::false_type +{ +}; + +template +struct has_take_out_node().take_out_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_take_out_node_v = has_take_out_node::value; +#pragma endregion + +#pragma region is_dead +template +struct has_is_dead : std::false_type +{ +}; + +template +struct has_is_dead().is_dead( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_dead_v = has_is_dead::value; +#pragma endregion + +#pragma region has_size +template +struct has_size : std::false_type +{ +}; + +template +struct has_size().size() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_size_v = has_size::value; +#pragma endregion + +#pragma region has_num_cis +template +struct has_num_cis : std::false_type +{ +}; + +template +struct has_num_cis().num_cis() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cis_v = has_num_cis::value; +#pragma endregion + +#pragma region has_num_cos +template +struct has_num_cos : std::false_type +{ +}; + +template +struct has_num_cos().num_cos() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cos_v = has_num_cos::value; +#pragma endregion + +#pragma region has_num_pis +template +struct has_num_pis : std::false_type +{ +}; + +template +struct has_num_pis().num_pis() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_pis_v = has_num_pis::value; +#pragma endregion + +#pragma region has_num_pos +template +struct has_num_pos : std::false_type +{ +}; + +template +struct has_num_pos().num_pos() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_pos_v = has_num_pos::value; +#pragma endregion + +#pragma region has_num_gates +template +struct has_num_gates : std::false_type +{ +}; + +template +struct has_num_gates().num_gates() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_gates_v = has_num_gates::value; +#pragma endregion + +#pragma region has_num_registers +template +struct has_num_registers : std::false_type +{ +}; + +template +struct has_num_registers().num_registers() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_registers_v = has_num_registers::value; +#pragma endregion + +#pragma region has_fanin_size +template +struct has_fanin_size : std::false_type +{ +}; + +template +struct has_fanin_size().fanin_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_fanin_size_v = has_fanin_size::value; +#pragma endregion + +#pragma region has_num_outputs +template +struct has_num_outputs : std::false_type +{ +}; + +template +struct has_num_outputs().num_outputs( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_outputs_v = has_num_outputs::value; +#pragma endregion + +#pragma region has_fanout_size +template +struct has_fanout_size : std::false_type +{ +}; + +template +struct has_fanout_size().fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_fanout_size_v = has_fanout_size::value; +#pragma endregion + +#pragma region has_incr_fanout_size +template +struct has_incr_fanout_size : std::false_type +{ +}; + +template +struct has_incr_fanout_size().incr_fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_fanout_size_v = has_incr_fanout_size::value; +#pragma endregion + +#pragma region has_decr_fanout_size +template +struct has_decr_fanout_size : std::false_type +{ +}; + +template +struct has_decr_fanout_size().decr_fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_decr_fanout_size_v = has_decr_fanout_size::value; +#pragma endregion + +#pragma region has_cost +template +struct has_cost : std::false_type +{ +}; + +template +struct has_cost().get_cost() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_cost_v = has_cost::value; +#pragma endregion + +#pragma region has_depth +template +struct has_depth : std::false_type +{ +}; + +template +struct has_depth().depth() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_depth_v = has_depth::value; +#pragma endregion + +#pragma region has_level +template +struct has_level : std::false_type +{ +}; + +template +struct has_level().level( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_level_v = has_level::value; +#pragma endregion + +#pragma region has_update_levels +template +struct has_update_levels : std::false_type +{ +}; + +template +struct has_update_levels().update_levels() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_levels_v = has_update_levels::value; +#pragma endregion + +#pragma region has_rank_position +template +struct has_rank_position : std::false_type +{ +}; + +template +struct has_rank_position().rank_position( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_rank_position_v = has_rank_position::value; +#pragma endregion + +#pragma region has_at_rank_position +template +struct has_at_rank_position : std::false_type +{ +}; + +template +struct has_at_rank_position().at_rank_position( std::declval(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_at_rank_position_v = has_at_rank_position::value; +#pragma endregion + +#pragma region has_width +template +struct has_width : std::false_type +{ +}; + +template +struct has_width().width() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_width_v = has_width::value; +#pragma endregion + +#pragma region has_swap +template +struct has_swap : std::false_type +{ +}; + +template +struct has_swap().swap( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_swap_v = has_swap::value; +#pragma endregion + +#pragma region has_sort_rank +template +struct has_sort_rank : std::false_type +{ +}; + +template +struct has_sort_rank().sort_rank( std::declval(), std::declval, node )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_sort_rank_v = has_sort_rank::value; +#pragma endregion + +#pragma region has_foreach_node_in_rank +template +struct has_foreach_node_in_rank : std::false_type +{ +}; + +template +struct has_foreach_node_in_rank().foreach_node_in_rank( std::declval(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_node_in_rank_v = has_foreach_node_in_rank::value; +#pragma endregion + +#pragma region has_foreach_gate_in_rank +template +struct has_foreach_gate_in_rank : std::false_type +{ +}; + +template +struct has_foreach_gate_in_rank().foreach_gate_in_rank( std::declval(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_gate_in_rank_v = has_foreach_gate_in_rank::value; +#pragma endregion + +#pragma region has_update_mffcs +template +struct has_update_mffcs : std::false_type +{ +}; + +template +struct has_update_mffcs().update_mffcs() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_mffcs_v = has_update_mffcs::value; +#pragma endregion + +#pragma region has_update_topo +template +struct has_update_topo : std::false_type +{ +}; + +template +struct has_update_topo().update_topo() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_topo_v = has_update_topo::value; +#pragma endregion + +#pragma region has_update_fanout +template +struct has_update_fanout : std::false_type +{ +}; + +template +struct has_update_fanout().update_fanout() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_fanout_v = has_update_fanout::value; +#pragma endregion + +#pragma region has_is_on_critical_path +template +struct has_is_on_critical_path : std::false_type +{ +}; + +template +struct has_is_on_critical_path().is_on_critical_path( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_on_critical_path_v = has_is_on_critical_path::value; +#pragma endregion + +#pragma region has_is_buf +template +struct has_is_buf : std::false_type +{ +}; + +template +struct has_is_buf().is_buf( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_buf_v = has_is_buf::value; +#pragma endregion + +#pragma region has_is_not +template +struct has_is_not : std::false_type +{ +}; + +template +struct has_is_not().is_not( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_not_v = has_is_not::value; +#pragma endregion + +#pragma region has_is_crossing +template +struct has_is_crossing : std::false_type +{ +}; + +template +struct has_is_crossing().is_crossing( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_crossing_v = has_is_crossing::value; +#pragma endregion + +#pragma region has_is_and +template +struct has_is_and : std::false_type +{ +}; + +template +struct has_is_and().is_and( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_and_v = has_is_and::value; +#pragma endregion + +#pragma region has_is_or +template +struct has_is_or : std::false_type +{ +}; + +template +struct has_is_or().is_or( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_or_v = has_is_or::value; +#pragma endregion + +#pragma region has_is_xor +template +struct has_is_xor : std::false_type +{ +}; + +template +struct has_is_xor().is_xor( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_xor_v = has_is_xor::value; +#pragma endregion + +#pragma region has_is_maj +template +struct has_is_maj : std::false_type +{ +}; + +template +struct has_is_maj().is_maj( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_maj_v = has_is_maj::value; +#pragma endregion + +#pragma region has_is_ite +template +struct has_is_ite : std::false_type +{ +}; + +template +struct has_is_ite().is_ite( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ite_v = has_is_ite::value; +#pragma endregion + +#pragma region has_is_xor3 +template +struct has_is_xor3 : std::false_type +{ +}; + +template +struct has_is_xor3().is_xor3( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_xor3_v = has_is_xor3::value; +#pragma endregion + +#pragma region has_is_nary_and +template +struct has_is_nary_and : std::false_type +{ +}; + +template +struct has_is_nary_and().is_nary_and( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_and_v = has_is_nary_and::value; +#pragma endregion + +#pragma region has_is_nary_or +template +struct has_is_nary_or : std::false_type +{ +}; + +template +struct has_is_nary_or().is_nary_or( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_or_v = has_is_nary_or::value; +#pragma endregion + +#pragma region has_is_nary_xor +template +struct has_is_nary_xor : std::false_type +{ +}; + +template +struct has_is_nary_xor().is_nary_xor( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_xor_v = has_is_nary_xor::value; +#pragma endregion + +#pragma region has_is_function +template +struct has_is_function : std::false_type +{ +}; + +template +struct has_is_function().is_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_function_v = has_is_function::value; +#pragma endregion + +#pragma region has_node_function +template +struct has_node_function : std::false_type +{ +}; + +template +struct has_node_function().node_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_function_v = has_node_function::value; +#pragma endregion + +#pragma region has_node_function_pin +template +struct has_node_function_pin : std::false_type +{ +}; + +template +struct has_node_function_pin().node_function_pin( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_function_pin_v = has_node_function_pin::value; +#pragma endregion + +#pragma region has_get_node +template +struct has_get_node : std::false_type +{ +}; + +template +struct has_get_node().get_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_node_v = has_get_node::value; +#pragma endregion + +#pragma region has_make_signal +template +struct has_make_signal : std::false_type +{ +}; + +template +struct has_make_signal().make_signal( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_make_signal_v = has_make_signal::value; +#pragma endregion + +#pragma region has_get_output_pin +template +struct has_get_output_pin : std::false_type +{ +}; + +template +struct has_get_output_pin().get_output_pin( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_output_pin_v = has_get_output_pin::value; +#pragma endregion + +#pragma region has_is_complemented +template +struct has_is_complemented : std::false_type +{ +}; + +template +struct has_is_complemented().is_complemented( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_complemented_v = has_is_complemented::value; +#pragma endregion + +#pragma region has_node_to_index +template +struct has_node_to_index : std::false_type +{ +}; + +template +struct has_node_to_index().node_to_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_to_index_v = has_node_to_index::value; +#pragma endregion + +#pragma region has_index_to_node +template +struct has_index_to_node : std::false_type +{ +}; + +template +struct has_index_to_node().index_to_node( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_index_to_node_v = has_index_to_node::value; +#pragma endregion + +#pragma region has_ci_at +template +struct has_ci_at : std::false_type +{ +}; + +template +struct has_ci_at().ci_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ci_at_v = has_ci_at::value; +#pragma endregion + +#pragma region has_co_at +template +struct has_co_at : std::false_type +{ +}; + +template +struct has_co_at().co_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_co_at_v = has_co_at::value; +#pragma endregion + +#pragma region has_pi_at +template +struct has_pi_at : std::false_type +{ +}; + +template +struct has_pi_at().pi_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_pi_at_v = has_pi_at::value; +#pragma endregion + +#pragma region has_po_at +template +struct has_po_at : std::false_type +{ +}; + +template +struct has_po_at().po_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_po_at_v = has_po_at::value; +#pragma endregion + +#pragma region has_ro_at +template +struct has_ro_at : std::false_type +{ +}; + +template +struct has_ro_at().ro_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ro_at_v = has_ro_at::value; +#pragma endregion + +#pragma region has_ri_at +template +struct has_ri_at : std::false_type +{ +}; + +template +struct has_ri_at().ri_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ri_at_v = has_ri_at::value; +#pragma endregion + +#pragma region ci_index +template +struct ci_index : std::false_type +{ +}; + +template +struct ci_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ci_index_v = ci_index::value; +#pragma endregion + +#pragma region co_index +template +struct co_index : std::false_type +{ +}; + +template +struct co_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool co_index_v = co_index::value; +#pragma endregion + +#pragma region pi_index +template +struct pi_index : std::false_type +{ +}; + +template +struct pi_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool pi_index_v = pi_index::value; +#pragma endregion + +#pragma region po_index +template +struct po_index : std::false_type +{ +}; + +template +struct po_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool po_index_v = po_index::value; +#pragma endregion + +#pragma region ro_index +template +struct ro_index : std::false_type +{ +}; + +template +struct ro_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ro_index_v = ro_index::value; +#pragma endregion + +#pragma region ri_index +template +struct ri_index : std::false_type +{ +}; + +template +struct ri_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ri_index_v = ri_index::value; +#pragma endregion + +#pragma region has_ro_to_ri +template +struct has_ro_to_ri : std::false_type +{ +}; + +template +struct has_ro_to_ri().ro_to_ri( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ro_to_ri_v = has_ro_to_ri::value; +#pragma endregion + +#pragma region has_ri_to_ro +template +struct has_ri_to_ro : std::false_type +{ +}; + +template +struct has_ri_to_ro().ri_to_ro( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ri_to_ro_v = has_ri_to_ro::value; +#pragma endregion + +#pragma region has_foreach_node +template +struct has_foreach_node : std::false_type +{ +}; + +template +struct has_foreach_node().foreach_node( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_node_v = has_foreach_node::value; +#pragma endregion + +#pragma region has_foreach_ci +template +struct has_foreach_ci : std::false_type +{ +}; + +template +struct has_foreach_ci().foreach_ci( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ci_v = has_foreach_ci::value; +#pragma endregion + +#pragma region has_foreach_co +template +struct has_foreach_co : std::false_type +{ +}; + +template +struct has_foreach_co().foreach_co( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_co_v = has_foreach_co::value; +#pragma endregion + +#pragma region has_foreach_pi +template +struct has_foreach_pi : std::false_type +{ +}; + +template +struct has_foreach_pi().foreach_pi( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_pi_v = has_foreach_pi::value; +#pragma endregion + +#pragma region has_foreach_po +template +struct has_foreach_po : std::false_type +{ +}; + +template +struct has_foreach_po().foreach_po( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_po_v = has_foreach_po::value; +#pragma endregion + +#pragma region has_foreach_ro +template +struct has_foreach_ro : std::false_type +{ +}; + +template +struct has_foreach_ro().foreach_ro( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ro_v = has_foreach_ro::value; +#pragma endregion + +#pragma region has_foreach_ri +template +struct has_foreach_ri : std::false_type +{ +}; + +template +struct has_foreach_ri().foreach_ri( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ri_v = has_foreach_ri::value; +#pragma endregion + +#pragma region has_foreach_gate +template +struct has_foreach_gate : std::false_type +{ +}; + +template +struct has_foreach_gate().foreach_gate( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_gate_v = has_foreach_gate::value; +#pragma endregion + +#pragma region has_foreach_register +template +struct has_foreach_register : std::false_type +{ +}; + +template +struct has_foreach_register().foreach_register( std::declval, signal>, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_register_v = has_foreach_register::value; +#pragma endregion + +#pragma region has_foreach_fanin +template +struct has_foreach_fanin : std::false_type +{ +}; + +template +struct has_foreach_fanin().foreach_fanin( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_fanin_v = has_foreach_fanin::value; +#pragma endregion + +#pragma region has_foreach_fanout +template +struct has_foreach_fanout : std::false_type +{ +}; + +template +struct has_foreach_fanout().foreach_fanout( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_fanout_v = has_foreach_fanout::value; +#pragma endregion + +#pragma region has_foreach_choice +template +struct has_foreach_choice : std::false_type +{ +}; + +template +struct has_foreach_choice().foreach_choice( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_choice_v = has_foreach_choice::value; +#pragma endregion + +#pragma region has_compute +template +struct has_compute : std::false_type +{ +}; + +template +struct has_compute().compute( std::declval>(), std::begin( std::vector() ), std::end( std::vector() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_compute_v = has_compute::value; +#pragma endregion + +#pragma region has_compute_inplace +template +struct has_compute_inplace : std::false_type +{ +}; + +template +struct has_compute_inplace().compute( std::declval>(), std::declval(), std::begin( std::vector() ), std::end( std::vector() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_compute_inplace_v = has_compute_inplace::value; +#pragma endregion + +#pragma region has_has_mapping +template +struct has_has_mapping : std::false_type +{ +}; + +template +struct has_has_mapping().has_mapping() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_mapping_v = has_has_mapping::value; +#pragma endregion + +#pragma region has_is_cell_root +template +struct has_is_cell_root : std::false_type +{ +}; + +template +struct has_is_cell_root().is_cell_root( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_cell_root_v = has_is_cell_root::value; +#pragma endregion + +#pragma region has_clear_mapping +template +struct has_clear_mapping : std::false_type +{ +}; + +template +struct has_clear_mapping().clear_mapping() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_mapping_v = has_clear_mapping::value; +#pragma endregion + +#pragma region has_num_cells +template +struct has_num_cells : std::false_type +{ +}; + +template +struct has_num_cells().num_cells() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cells_v = has_num_cells::value; +#pragma endregion + +#pragma region has_add_to_mapping +template +struct has_add_to_mapping : std::false_type +{ +}; + +template +struct has_add_to_mapping().add_to_mapping( std::declval>(), std::begin( std::vector>() ), std::end( std::vector>() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_to_mapping_v = has_add_to_mapping::value; +#pragma endregion + +#pragma region has_remove_from_mapping +template +struct has_remove_from_mapping : std::false_type +{ +}; + +template +struct has_remove_from_mapping().remove_from_mapping( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_remove_from_mapping_v = has_remove_from_mapping::value; +#pragma endregion + +#pragma region has_cell_function +template +struct has_cell_function : std::false_type +{ +}; + +template +struct has_cell_function().cell_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_cell_function_v = has_cell_function::value; +#pragma endregion + +#pragma region has_set_cell_function +template +struct has_set_cell_function : std::false_type +{ +}; + +template +struct has_set_cell_function().set_cell_function( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_cell_function_v = has_set_cell_function::value; +#pragma endregion + +#pragma region has_foreach_cell_fanin +template +struct has_foreach_cell_fanin : std::false_type +{ +}; + +template +struct has_foreach_cell_fanin().foreach_cell_fanin( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_cell_fanin_v = has_foreach_cell_fanin::value; +#pragma endregion + +#pragma region has_has_binding +template +struct has_has_binding : std::false_type +{ +}; + +template +struct has_has_binding().has_binding( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_binding_v = has_has_binding::value; +#pragma endregion + +#pragma region has_has_cell +template +struct has_has_cell : std::false_type +{ +}; + +template +struct has_has_cell().has_cell( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_cell_v = has_has_cell::value; +#pragma endregion + +#pragma region has_get_binding_index +template +struct has_get_binding_index : std::false_type +{ +}; + +template +struct has_get_binding_index().get_binding_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_binding_index_v = has_get_binding_index::value; +#pragma endregion + +#pragma region has_get_cell_index +template +struct has_get_cell_index : std::false_type +{ +}; + +template +struct has_get_cell_index().get_cell_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_cell_index_v = has_get_cell_index::value; +#pragma endregion + +#pragma region has_add_binding +template +struct has_add_binding : std::false_type +{ +}; + +template +struct has_add_binding().add_binding( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_binding_v = has_add_binding::value; +#pragma endregion + +#pragma region has_select_dont_touch +template +struct has_select_dont_touch : std::false_type +{ +}; + +template +struct has_select_dont_touch().select_dont_touch( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_select_dont_touch_v = has_select_dont_touch::value; +#pragma endregion + +#pragma region has_is_dont_touch +template +struct has_is_dont_touch : std::false_type +{ +}; + +template +struct has_is_dont_touch().is_dont_touch( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_dont_touch_v = has_is_dont_touch::value; +#pragma endregion + +#pragma region has_clear_values +template +struct has_clear_values : std::false_type +{ +}; + +template +struct has_clear_values().clear_values() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_values_v = has_clear_values::value; +#pragma endregion + +#pragma region has_value +template +struct has_value : std::false_type +{ +}; + +template +struct has_value().value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_value_v = has_value::value; +#pragma endregion + +#pragma region set_value +template +struct has_set_value : std::false_type +{ +}; + +template +struct has_set_value().set_value( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_value_v = has_set_value::value; +#pragma endregion + +#pragma region incr_value +template +struct has_incr_value : std::false_type +{ +}; + +template +struct has_incr_value().incr_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_value_v = has_incr_value::value; +#pragma endregion + +#pragma region decr_value +template +struct has_decr_value : std::false_type +{ +}; + +template +struct has_decr_value().decr_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_decr_value_v = has_decr_value::value; +#pragma endregion + +#pragma region has_get_fanin0 +template +struct has_get_fanin0 : std::false_type +{ +}; + +template +struct has_get_fanin0().get_fanin0( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_fanin0_v = has_get_fanin0::value; +#pragma endregion + +#pragma region has_clear_visited +template +struct has_clear_visited : std::false_type +{ +}; + +template +struct has_clear_visited().clear_visited() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_visited_v = has_clear_visited::value; +#pragma endregion + +#pragma region has_visited +template +struct has_visited : std::false_type +{ +}; + +template +struct has_visited().visited( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_visited_v = has_visited::value; +#pragma endregion + +#pragma region set_visited +template +struct has_set_visited : std::false_type +{ +}; + +template +struct has_set_visited().set_visited( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_visited_v = has_set_visited::value; +#pragma endregion + +#pragma region trav_id +template +struct has_trav_id : std::false_type +{ +}; + +template +struct has_trav_id().trav_id() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_trav_id_v = has_trav_id::value; +#pragma endregion + +#pragma region incr_trav_id +template +struct has_incr_trav_id : std::false_type +{ +}; + +template +struct has_incr_trav_id().incr_trav_id() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_trav_id_v = has_incr_trav_id::value; +#pragma endregion + +#pragma region has_get_network_name +template +struct has_get_network_name : std::false_type +{ +}; + +template +struct has_get_network_name().get_network_name() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_network_name_v = has_get_network_name::value; +#pragma endregion + +#pragma region has_set_network_name +template +struct has_set_network_name : std::false_type +{ +}; + +template +struct has_set_network_name().set_network_name( std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_network_name_v = has_set_network_name::value; +#pragma endregion + +#pragma region has_get_name +template +struct has_get_name : std::false_type +{ +}; + +template +struct has_get_name().get_name( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_name_v = has_get_name::value; +#pragma endregion + +#pragma region has_set_name +template +struct has_set_name : std::false_type +{ +}; + +template +struct has_set_name().set_name( std::declval>(), std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_name_v = has_set_name::value; +#pragma endregion + +#pragma region has_has_name +template +struct has_has_name : std::false_type +{ +}; + +template +struct has_has_name().has_name( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_name_v = has_has_name::value; +#pragma endregion + +#pragma region has_get_output_name +template +struct has_get_output_name : std::false_type +{ +}; + +template +struct has_get_output_name().get_output_name( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_output_name_v = has_get_output_name::value; +#pragma endregion + +#pragma region has_set_output_name +template +struct has_set_output_name : std::false_type +{ +}; + +template +struct has_set_output_name().set_output_name( uint32_t(), std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_output_name_v = has_set_output_name::value; +#pragma endregion + +#pragma region has_has_output_name +template +struct has_has_output_name : std::false_type +{ +}; + +template +struct has_has_output_name().has_output_name( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_output_name_v = has_has_output_name::value; +#pragma endregion + +#pragma region has_new_color +template +struct has_new_color : std::false_type +{ +}; + +template +struct has_new_color().new_color() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_new_color_v = has_new_color::value; +#pragma endregion + +#pragma region has_current_color +template +struct has_current_color : std::false_type +{ +}; + +template +struct has_current_color().current_color() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_current_color_v = has_current_color::value; +#pragma endregion + +#pragma region has_clear_colors +template +struct has_clear_colors : std::false_type +{ +}; + +template +struct has_clear_colors().clear_colors( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_colors_v = has_clear_colors::value; +#pragma endregion + +#pragma region has_color +template +struct has_color : std::false_type +{ +}; + +template +struct has_color().color( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_color_v = has_color::value; +#pragma endregion + +#pragma region has_paint +template +struct has_paint : std::false_type +{ +}; + +template +struct has_paint().paint( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_paint_v = has_paint::value; +#pragma endregion + +#pragma region has_eval_color +template +struct has_eval_color : std::false_type +{ +}; + +template +struct has_eval_color().eval_color( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_eval_color_v = has_eval_color::value; +#pragma endregion + +#pragma region has_eval_fanins_color +template +struct has_eval_fanins_color : std::false_type +{ +}; + +template +struct has_eval_fanins_color().eval_fanins_color( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_eval_fanins_color_v = has_eval_fanins_color::value; +#pragma endregion + +#pragma region has_EXCDC_interface +template +struct has_EXCDC_interface : std::false_type +{ +}; + +template +struct has_EXCDC_interface>> : std::true_type +{ +}; + +template +inline constexpr bool has_EXCDC_interface_v = has_EXCDC_interface::value; +#pragma endregion + +#pragma region has_EXODC_interface +template +struct has_EXODC_interface : std::false_type +{ +}; + +template +struct has_EXODC_interface>> : std::true_type +{ +}; + +template +inline constexpr bool has_EXODC_interface_v = has_EXODC_interface::value; +#pragma endregion + +/*! \brief SFINAE based on iterator type (for compute functions). + */ +template +using iterates_over_t = std::enable_if_t::value_type, T>, T>; + +/*! \brief SFINAE based on iterator type for truth tables (for compute functions). + */ +template +using iterates_over_truth_table_t = std::enable_if_t::value_type>::value, typename std::iterator_traits::value_type>; + +template +inline constexpr bool iterates_over_v = std::is_same_v; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/abc.hpp b/third-party/mockturtle/include/mockturtle/utils/abc.hpp new file mode 100644 index 00000000000..5988a1e2cf3 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/abc.hpp @@ -0,0 +1,118 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file abc.hpp + \brief Utility functions for interfacing with ABC + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once +#ifdef ENABLE_ABC + +#include "../networks/aig.hpp" +#include "../networks/gia.hpp" + +namespace mockturtle +{ + +void aig_to_gia(gia_network &gia, aig_network aig) { + using aig_node = aig_network::node; + using aig_signal = aig_network::signal; + + std::vector a_to_g(aig.size()); + + /* constant */ + a_to_g[0] = gia.get_constant(false); + + /* pis */ + aig.foreach_pi([&](aig_node n) { + a_to_g[n] = gia.create_pi(); + }); + + /* ands */ + aig.foreach_gate([&](aig_node n){ + std::array fis; + aig.foreach_fanin(n, [&](aig_signal fi, int index){ + fis[index] = aig.is_complemented(fi) ? !a_to_g[aig.get_node(fi)] : a_to_g[aig.get_node(fi)]; + }); + a_to_g[n] = gia.create_and(fis[0], fis[1]); + }); + + /* pos */ + aig.foreach_po([&](aig_signal f){ + gia.create_po(aig.is_complemented(f) ? !a_to_g[aig.get_node(f)] : a_to_g[aig.get_node(f)]); + }); +} + +void gia_to_aig(aig_network aig, const gia_network &gia) { + using gia_node = gia_network::node; + using gia_signal = gia_network::signal; + + std::vector g_to_a(gia.size()); + + /* constant */ + g_to_a[0] = aig.get_constant(false); + + /* pis */ + gia.foreach_pi([&](gia_network::node n){ + g_to_a[n] = aig.create_pi(); + }); + + /* ands */ + gia.foreach_gate([&](gia_network::node n){ + std::array fis; + gia.foreach_fanin(n, [&](gia_signal fi, int index){ + fis[index] = gia.is_complemented(fi) ? !g_to_a[gia.get_node(fi)] : g_to_a[gia.get_node(fi)]; + }); + + g_to_a[n] = aig.create_and(fis[0], fis[1]); + }); + + /* pos */ + gia.foreach_po([&](gia_network::signal f){ + aig.create_po(gia.is_complemented(f) ? !g_to_a[gia.get_node(f)] : g_to_a[gia.get_node(f)]); + }); +} + +aig_network call_abc_script( aig_network const& aig, std::string const& script ) +{ + gia_network gia( aig.size() << 1 ); + aig_to_gia( gia, aig ); + + gia.load_rc(); + gia.run_opt_script( script ); + + aig_network new_aig; + gia_to_aig( new_aig, gia ); + + new_aig = cleanup_dangling( new_aig ); + return new_aig; +} + +} + +#endif \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/algorithm.hpp b/third-party/mockturtle/include/mockturtle/utils/algorithm.hpp new file mode 100644 index 00000000000..6397fff25c6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/algorithm.hpp @@ -0,0 +1,230 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file algorithm.hpp + \brief STL-like algorithm extensions + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken +*/ + +#pragma once + +#include + +namespace mockturtle +{ + +template +T tree_reduce( Iterator first, Iterator last, T const& init, BinaryOperation&& op ) +{ + const auto len = std::distance( first, last ); + + switch ( len ) + { + case 0u: + return init; + case 1u: + return *first; + case 2u: + return op( *first, *( first + 1 ) ); + default: + { + const auto m = len / 2; + return op( tree_reduce( first, first + m, init, op ), tree_reduce( first + m, last, init, op ) ); + } + break; + } +} + +template +T ternary_tree_reduce( Iterator first, Iterator last, T const& init, TernaryOperation&& op ) +{ + const auto len = std::distance( first, last ); + + switch ( len ) + { + case 0u: + return init; + case 1u: + return *first; + case 2u: + return op( init, *first, *( first + 1 ) ); + case 3u: + return op( *first, *( first + 1 ), *( first + 2 ) ); + default: + { + const auto m1 = len / 3; + const auto m2 = ( len - m1 ) / 2; + return op( ternary_tree_reduce( first, first + m1, init, op ), + ternary_tree_reduce( first + m1, first + m1 + m2, init, op ), + ternary_tree_reduce( first + m1 + m2, last, init, op ) ); + } + break; + } +} + +template +Iterator max_element_unary( Iterator first, Iterator last, UnaryOperation&& fn, T const& init ) +{ + auto best = last; + auto max = init; + for ( ; first != last; ++first ) + { + if ( const auto v = fn( *first ) > max ) + { + max = v; + best = first; + } + } + return best; +} + +template>> +constexpr auto range( T begin, T end ) +{ + struct iterator + { + using value_type = T; + + value_type curr_; + bool operator!=( iterator const& other ) const { return curr_ != other.curr_; } + iterator& operator++() + { + ++curr_; + return *this; + } + iterator operator++( int ) + { + auto copy = *this; + ++( *this ); + return copy; + } + value_type operator*() const { return curr_; } + }; + struct iterable_wrapper + { + T begin_; + T end_; + auto begin() { return iterator{ begin_ }; } + auto end() { return iterator{ end_ }; } + }; + return iterable_wrapper{ begin, end }; +} + +template>> +constexpr auto range( T end ) +{ + return range( {}, end ); +} + +/*! \brief Performs the set union of two sorted sets. + * + * Compared to std::set_union, limits the copy to `limit`. + * Moreover, it returns the number of elements copied if the + * union operation is successful. Else, it returns -1. + * + */ +template +int32_t set_union_safe( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, uint32_t limit ) +{ + /* special case: sets are at the limit */ + if ( std::distance( first1, last1 ) == limit && std::distance( first2, last2 ) == limit ) + { + while ( first1 != last1 ) + { + if ( *first1 != *first2 ) + return -1; + + *result = *first1; + ++first1; + ++first2; + ++result; + } + + return static_cast( limit ); + } + + uint32_t size = 0; + while ( size < limit ) + { + if ( first1 == last1 ) + { + size += std::distance( first2, last2 ); + if ( size <= limit ) + { + std::copy( first2, last2, result ); + return static_cast( size ); + } + else + { + return -1; + } + } + else if ( first2 == last2 ) + { + size += std::distance( first1, last1 ); + if ( size <= limit ) + { + std::copy( first1, last1, result ); + return static_cast( size ); + } + else + { + return -1; + } + } + + if ( *first1 < *first2 ) + { + *result = *first1; + ++first1; + } + else if ( *first2 < *first1 ) + { + *result = *first2; + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + } + + ++result; + ++size; + } + + if ( std::distance( first1, last1 ) + std::distance( first2, last2 ) > 0 ) + return -1; + + return static_cast( size ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/cost_functions.hpp b/third-party/mockturtle/include/mockturtle/utils/cost_functions.hpp new file mode 100644 index 00000000000..6b5315bc18a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/cost_functions.hpp @@ -0,0 +1,146 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cost_functions.hpp + \brief Various cost functions for (optimization) algorithms + + \author Heinz Riener + \author Mathias Soeken + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +template +struct unit_cost +{ + uint32_t operator()( Ntk const& ntk, node const& node ) const + { + (void)ntk; + (void)node; + return 1u; + } +}; + +template +struct mc_cost +{ + uint32_t operator()( Ntk const& ntk, node const& node ) const + { + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( node ) ) + { + return 0u; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( node ) ) + { + return 0u; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( node ) ) + { + if ( ntk.fanin_size( node ) > 1u ) + { + return ntk.fanin_size( node ) - 1u; + } + return 0u; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( node ) ) + { + if ( ntk.fanin_size( node ) > 1u ) + { + return ntk.fanin_size( node ) - 1u; + } + return 0u; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( node ) ) + { + return 0u; + } + } + + // TODO (Does not take into account general node functions) + return 1u; + } +}; + +struct lut_unitary_cost +{ + std::pair operator()( uint32_t num_leaves ) const + { + if ( num_leaves < 2u ) + return { 0u, 0u }; + return { 1u, 1u }; /* area, delay */ + } + + std::pair operator()( kitty::dynamic_truth_table const& tt ) const + { + if ( tt.num_vars() < 2u ) + return { 0u, 0u }; + return { 1u, 1u }; /* area, delay */ + } +}; + +template> +uint32_t costs( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + uint32_t total{ 0u }; + NodeCostFn cost_fn{}; + ntk.foreach_gate( [&]( auto const& n ) { + total += cost_fn( ntk, n ); + } ); + return total; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/cuts.hpp b/third-party/mockturtle/include/mockturtle/utils/cuts.hpp new file mode 100644 index 00000000000..5a9c4404fc0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/cuts.hpp @@ -0,0 +1,591 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cuts.hpp + \brief Data structure for cuts + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "algorithm.hpp" + +namespace mockturtle +{ + +struct empty_cut_data +{ +}; + +/*! \brief A data-structure to hold a cut. + * + * The cut class is specialized via two template arguments, `MaxLeaves` and `T`. + * `MaxLeaves` controls the maximum number of leaves a cut can hold. To + * guarantee an efficient implementation, this value should be \f$k \cdot l\f$, + * where \f$k\f$ is the maximum cut size and \f$l\f$ is the maximum fanin size + * of a gate in the logic network. The second template argument `T` can be a + * type for which a data entry is created in the cut to store additional data, + * e.g., to compute the cost of a cut. It defaults to `empty_cut_data`, which + * is an empty struct that does not consume memory. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector data{1, 2, 3, 4}; + + cut<10> cut1; + cut1.set_leaves( data.begin(), data.end() ); + + cut<10, uint32_t> cut2; + cut2.set_leaves( data.begin(), data.end() ); + cut2.data() = 42u; + + struct cut_data { uint32_t costs; }; + cut<20, cut_data> c3; + c3.set_leaves( std::vector{1, 2, 3} ); + c3->costs = 37u; + \endverbatim + */ +template +class cut +{ +public: + /*! \brief Default constructor. + */ + cut() = default; + + /*! \brief Copy constructor. + * + * Copies leaves, length, signature, and data. + * + * \param other Other cut + */ + cut( cut const& other ) + { + _cend = _end = std::copy( other.begin(), other.end(), _leaves.begin() ); + _length = other._length; + _signature = other._signature; + _data = other._data; + } + + /*! \brief Assignment operator. + * + * Copies leaves, length, signature, and data. + * + * \param other Other cut + */ + cut& operator=( cut const& other ); + + /*! \brief Sets leaves (using iterators). + * + * \param begin Begin iterator to leaves + * \param end End iterator to leaves (exclusive) + */ + template + void set_leaves( Iterator begin, Iterator end ); + + /*! \brief Sets leaves (using container). + * + * Convenience function, which extracts the begin and end iterators from the + * container. + */ + template + void set_leaves( Container const& c ); + + /*! \brief Add leaves (using iterators). + * + * \param begin Begin iterator to leaves + * \param end End iterator to leaves (exclusive) + */ + template + void add_leaves( Iterator begin, Iterator end ); + + /*! \brief Signature of the cut. */ + auto signature() const { return _signature; } + + /*! \brief Returns the size of the cut (number of leaves). */ + auto size() const { return _length; } + + /*! \brief Begin iterator (constant). */ + auto begin() const { return _leaves.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _cend; } + + /*! \brief Begin iterator (mutable). */ + auto begin() { return _leaves.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _end; } + + /*! \brief Access to data (mutable). */ + T* operator->() { return &_data; } + + /*! \brief Access to data (constant). */ + T const* operator->() const { return &_data; } + + /*! \brief Access to data (mutable). */ + T& data() { return _data; } + + /*! \brief Access to data (constant). */ + T const& data() const { return _data; } + + /*! \brief Checks whether the cut is a subset of another cut. + * + * If \f$L_1\f$ are the leaves of the current cut and \f$L_2\f$ are the leaves + * of `that`, then this method returns true if and only if + * \f$L_1 \subseteq L_2\f$. + * + * \param that Other cut + */ + bool dominates( cut const& that ) const; + + /*! \brief Merges two cuts. + * + * This method merges two cuts and stores the result in `res`. The merge of + * two cuts is the union \f$L_1 \cup L_2\f$ of the two leaf sets \f$L_1\f$ of + * the cut and \f$L_2\f$ of `that`. The merge is only successful if the + * union has not more than `cut_size` elements. In that case, the function + * returns `false`, otherwise `true`. + * + * \param that Other cut + * \param res Resulting cut + * \param cut_size Maximum cut size + * \return True, if resulting cut is small enough + */ + bool merge( cut const& that, cut& res, uint32_t cut_size ) const; + +private: + std::array _leaves; + uint32_t _length; + uint64_t _signature; + typename std::array::const_iterator _cend; + typename std::array::iterator _end; + + T _data; +}; + +/*! \brief Compare two cuts. + * + * Default comparison function for two cuts. A cut is smaller than another + * cut, if it has fewer leaves. + * + * This function should be specialized for custom cuts, if additional data + * changes the cost of a cut. + */ +template +bool operator<( cut const& c1, cut const& c2 ) +{ + return c1.size() < c2.size(); +} + +/*! \brief Prints a cut. + */ +template +std::ostream& operator<<( std::ostream& os, cut const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}"; + return os; +} + +template +cut& cut::operator=( cut const& other ) +{ + if ( &other != this ) + { + _cend = _end = std::copy( other.begin(), other.end(), _leaves.begin() ); + _length = other._length; + _signature = other._signature; + _data = other._data; + } + return *this; +} + +template +template +void cut::set_leaves( Iterator begin, Iterator end ) +{ + _cend = _end = std::copy( begin, end, _leaves.begin() ); + _length = static_cast( std::distance( begin, end ) ); + _signature = 0; + + while ( begin != end ) + { + _signature |= UINT64_C( 1 ) << ( *begin++ & 0x3f ); + } +} + +template +template +void cut::set_leaves( Container const& c ) +{ + set_leaves( std::begin( c ), std::end( c ) ); +} + +template +template +void cut::add_leaves( Iterator begin, Iterator end ) +{ + _cend = _end = std::copy( begin, end, _end ); + _length = static_cast( std::distance( _leaves.begin(), _end ) ); + + while ( begin != end ) + { + _signature |= UINT64_C( 1 ) << ( *begin++ & 0x3f ); + } +} + +template +bool cut::dominates( cut const& that ) const +{ + /* quick check for counter example */ + if ( _length > that._length || ( _signature & that._signature ) != _signature ) + { + return false; + } + + if ( _length == that._length ) + { + return std::equal( begin(), end(), that.begin() ); + } + + if ( _length == 0 ) + { + return true; + } + + // this is basically + // return std::includes( that.begin(), that.end(), begin(), end() ) + // but it turns out that this code is faster compared to the standard + // implementation. + for ( auto it2 = that.begin(), it1 = begin(); it2 != that.end(); ++it2 ) + { + if ( *it2 > *it1 ) + { + return false; + } + if ( ( *it2 == *it1 ) && ( ++it1 == end() ) ) + { + return true; + } + } + + return false; +} + +template +bool cut::merge( cut const& that, cut& res, uint32_t cut_size ) const +{ + if ( _length + that._length > cut_size ) + { + const auto sign = _signature + that._signature; + if ( uint32_t( __builtin_popcount( static_cast( sign & 0xffffffff ) ) ) + uint32_t( __builtin_popcount( static_cast( sign >> 32 ) ) ) > cut_size ) + { + return false; + } + } + + int32_t length = set_union_safe( begin(), end(), that.begin(), that.end(), res.begin(), cut_size ); + if ( length >= 0 ) + { + res._cend = res._end = res.begin() + length; + res._length = static_cast( length ); + res._signature = _signature | that._signature; + return true; + } + return false; +} + +/*! \brief A data-structure to hold a set of cuts. + * + * The aim of a cut set is to contain cuts and maintain two properties. First, + * all cuts are ordered according to the `<` operator, and second, all cuts + * are irredundant, i.e., no cut in the set dominates another cut in the set. + * + * The cut set is defined using the `CutType` of cuts it should hold and a + * maximum number of cuts it can hold. No check is performed whether a cut set + * is full, and therefore the caller must not insert cuts into a full set. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + cut_set, 30> cuts; + + cut<10> c1, c2, c3, c4; + + c1.set_leaves( {1, 2, 3} ); + c2.set_leaves( {4, 5} ); + c3.set_leaves( {1, 2} ); + c4.set_leaves( {1, 3, 4} ); + + cuts.insert( c1 ); + cuts.insert( c2 ); + cuts.insert( c3 ); + cuts.insert( c4 ); + + assert( cuts.size() == 3 ); + + std::cout << cuts << std::endl; + + // will print: + // { 4, 5 } + // { 1, 2 } + // { 1, 3, 4 } + \endverbatim + */ +template +class cut_set +{ +public: + /*! \brief Standard constructor. + */ + cut_set(); + + /*! \brief Clears a cut set. + */ + void clear(); + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ); + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const; + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + */ + void insert( CutType const& cut ); + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto const& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ); + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ); + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; +}; + +template +cut_set::cut_set() +{ + clear(); +} + +template +void cut_set::clear() +{ + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } +} + +template +template +CutType& cut_set::add_cut( Iterator begin, Iterator end ) +{ + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; +} + +template +bool cut_set::is_dominated( CutType const& cut ) const +{ + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; +} + +template +void cut_set::insert( CutType const& cut ) +{ + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( _pcuts.begin(), _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + auto ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return *a < *b; } ); + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; +} + +template +void cut_set::update_best( uint32_t index ) +{ + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; +} + +template +void cut_set::limit( uint32_t size ) +{ + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/utils/debugging_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/debugging_utils.hpp new file mode 100644 index 00000000000..0a53b79642d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/debugging_utils.hpp @@ -0,0 +1,551 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file debugging_utils.hpp + \brief Network debugging utilities + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../algorithms/simulation.hpp" +#include "../traits.hpp" +#include "../views/topo_view.hpp" + +#include + +#include +#include + +namespace mockturtle +{ + +/*! \brief Prints information of all nodes in a network + * + * This utility function prints the following information for all + * nodes in the network: + * - ID + * - Fanin signals, if any + * - Level, if `level` is provided for the network type + * - Whether the node is dead + * - Reference count (fanout size) + * - Visited marker + * - Custom value data + * + * It also prints the outputs of the network. + */ +template +inline void print( Ntk const& ntk ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + for ( uint32_t n = 0; n < ntk.size(); ++n ) + { + std::cout << n; + + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + { + std::cout << std::endl; + continue; + } + + std::cout << " = "; + + ntk.foreach_fanin( n, [&]( signal const& fi ) { + std::cout << ( ntk.is_complemented( fi ) ? "~" : "" ) << ntk.get_node( fi ) << " "; + } ); + std::cout << " ;"; + if constexpr ( has_level_v ) + { + std::cout << " [level = " << int32_t( ntk.level( n ) ) << "]"; + } + std::cout << " [dead = " << ntk.is_dead( n ) << "]"; + std::cout << " [ref = " << ntk.fanout_size( n ) << "]"; + std::cout << " [visited = " << ntk.visited( n ) << "]"; + std::cout << " [value = " << ntk.value( n ) << "]"; + std::cout << std::endl; + } + + ntk.foreach_co( [&]( signal const& s ) { + std::cout << "o " << ( ntk.is_complemented( s ) ? "~" : "" ) << ntk.get_node( s ) << std::endl; + } ); +} + +/*! \brief Counts dead nodes in a network + * + * This utility function counts how many nodes in the network are + * said to be dead (i.e., `is_dead` returns true). + */ +template +inline uint64_t count_dead_nodes( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + + uint64_t counter{ 0 }; + for ( uint64_t n = 0; n < ntk.size(); ++n ) + { + if ( ntk.is_dead( n ) ) + { + ++counter; + } + } + return counter; +} + +/*! \brief Counts dangling roots in a network + * + * This utility function counts how many nodes in the network have + * a fanout size of zero. Note that it does not skip the nodes which + * are marked as dead, which are normally skipped when using `foreach` + * functions. + */ +template +inline uint64_t count_dangling_roots( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size function" ); + + uint64_t counter{ 0 }; + for ( uint64_t n = 0; n < ntk.size(); ++n ) + { + if ( ntk.fanout_size( n ) == 0 ) + { + ++counter; + } + } + return counter; +} + +namespace detail +{ + +template +void count_reachable_dead_nodes_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using signal = typename Ntk::signal; + + if ( ntk.current_color() == ntk.color( n ) ) + { + return; + } + + if ( ntk.is_dead( n ) ) + { + if ( std::find( std::begin( nodes ), std::end( nodes ), n ) == std::end( nodes ) ) + { + nodes.push_back( n ); + } + } + + ntk.paint( n ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + count_reachable_dead_nodes_recur( ntk, ntk.get_node( fi ), nodes ); + } ); +} + +} /* namespace detail */ + +/*! \brief Counts reachable dead nodes in a network + * + * This utility function counts how many nodes in the network are + * said to be dead (i.e., `is_dead` returns true) and are reachable + * from an output. + * + * This function requires the `paint` by the network (provided by + * wrapping with `color_view`). + */ +template +inline uint64_t count_reachable_dead_nodes( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + + std::vector dead_nodes; + ntk.foreach_co( [&]( signal const& po ) { + detail::count_reachable_dead_nodes_recur( ntk, ntk.get_node( po ), dead_nodes ); + } ); + + return dead_nodes.size(); +} + +namespace detail +{ + +template +void count_reachable_dead_nodes_from_node_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using node = typename Ntk::node; + + if ( ntk.current_color() == ntk.color( n ) ) + { + return; + } + + if ( ntk.is_dead( n ) ) + { + if ( std::find( std::begin( nodes ), std::end( nodes ), n ) == std::end( nodes ) ) + { + nodes.push_back( n ); + } + } + + ntk.paint( n ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + count_reachable_dead_nodes_from_node_recur( ntk, ntk.get_node( f ), nodes ); + } ); +} + +} /* namespace detail */ + +/*! \brief Counts dead nodes that are reachable from a given node + * + * This utility function counts how many nodes in the network are + * said to be dead (i.e., `is_dead` returns true) and are reachable + * from a given node (i.e., in the transitive fanin cone of this node). + * + * This function requires `paint` of the network (provided by + * wrapping with `color_view`). + */ +template +inline uint64_t count_reachable_dead_nodes_from_node( Ntk const& ntk, typename Ntk::node const& n ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + + std::vector dead_nodes; + detail::count_reachable_dead_nodes_from_node_recur( ntk, n, dead_nodes ); + + return dead_nodes.size(); +} + +/*! \brief Counts nodes with dead fanin(s) in a network + * + * This utility function counts how many (not-dead) nodes in the + * network have at least one fanin being dead. + */ +template +uint64_t count_nodes_with_dead_fanins( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + uint64_t counter{ 0u }; + ntk.foreach_node( [&]( node const& n ) { + ntk.foreach_fanin( n, [&]( signal const& s ) { + if ( ntk.is_dead( ntk.get_node( s ) ) ) + { + counter++; + return false; + } + return true; + } ); + } ); + + return counter; +} + +namespace detail +{ + +template +bool network_is_acyclic_recur( Ntk const& ntk, typename Ntk::node const& n ) +{ + using signal = typename Ntk::signal; + + if ( ntk.color( n ) == ntk.current_color() ) + { + return true; + } + + if ( ntk.color( n ) == ntk.current_color() - 1 ) + { + /* cycle detected at node n */ + return false; + } + + ntk.paint( n, ntk.current_color() - 1 ); + + bool result{ true }; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + if ( !network_is_acyclic_recur( ntk, ntk.get_node( fi ) ) ) + { + result = false; + return false; + } + return true; + } ); + ntk.paint( n, ntk.current_color() ); + + return result; +} + +} /* namespace detail */ + +/*! \brief Check if a network is acyclic + * + * This utility function checks if the network is acyclic, i.e., there + * is no path from a node to itself. + * + * This function requires `paint` of the network (provided by + * wrapping with `color_view`). + */ +template +bool network_is_acyclic( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + ntk.new_color(); + + ntk.paint( ntk.get_node( ntk.get_constant( false ) ) ); + ntk.foreach_ci( [&]( node const& n ) { + ntk.paint( n ); + } ); + + bool result{ true }; + ntk.foreach_co( [&]( signal const& o ) { + if ( !detail::network_is_acyclic_recur( ntk, ntk.get_node( o ) ) ) + { + result = false; + return false; + } + return true; + } ); + + return result; +} + +/*! \brief Check the level information of a network + * + * This utility function checks if the levels of each node in the + * network and the depth of the network are correct. + * + * This function requires `level` of the network (provided by + * wrapping with `depth_view`). + */ +template +bool check_network_levels( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size function" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant function" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_level_v, "Ntk does not implement the level function" ); + static_assert( has_depth_v, "Ntk does not implement the depth function" ); + + using signal = typename Ntk::signal; + + uint32_t max = 0; + for ( uint32_t i = 0u; i < ntk.size(); ++i ) + { + if ( ntk.is_constant( i ) || ntk.is_ci( i ) || ntk.is_dead( i ) ) + { + continue; + } + + uint32_t max_fanin_level = 0; + ntk.foreach_fanin( i, [&]( signal fi ) { + if ( ntk.level( ntk.get_node( fi ) ) > max_fanin_level ) + { + max_fanin_level = ntk.level( ntk.get_node( fi ) ); + } + } ); + + /* the node's level has not been correctly computed */ + if ( ntk.level( i ) != max_fanin_level + 1 ) + { + return false; + } + + if ( ntk.level( i ) > max ) + { + max = ntk.level( i ); + } + } + + /* the network's depth has not been correctly computed */ + if ( ntk.depth() != max ) + { + return false; + } + + return true; +} + +/*! \brief Check the fanout information of a network + * + * This utility function checks if the fanouts of each node in the + * network are correct. + * + * This function requires `foreach_fanout` of the network (provided by + * wrapping with `fanout_view`). + */ +template +bool check_fanouts( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + for ( auto i = 0u; i < ntk.size(); ++i ) + { + uint32_t fanout_counter{ 0 }; + + bool fanout_error = false; + ntk.foreach_fanout( i, [&]( node fo ) { + ++fanout_counter; + + /* check the fanins of the fanout */ + bool found = false; + ntk.foreach_fanin( fo, [&]( signal fi ) { + if ( ntk.get_node( fi ) == i ) + { + found = true; + return false; + } + return true; + } ); + + /* if errors have been detected, then terminate */ + if ( !found ) + { + fanout_error = true; + return false; + } + + return true; + } ); + + /* report error */ + if ( fanout_error ) + { + return false; + } + + /* update the fanout counter by considering outputs */ + ntk.foreach_co( [&]( signal f ) { + if ( ntk.get_node( f ) == i ) + { + ++fanout_counter; + } + } ); + + /* report error fanout_size does not match with the counter */ + if ( fanout_counter != ntk.fanout_size( i ) ) + { + return false; + } + } + + return true; +} + +/*! \brief Check functional equivalence between a window in a network + * and a stand-alone window. + * + * This utility function checks if a window in a network, defined by + * a set of inputs, a set of gates (internal nodes), and a set of + * outputs, is functionally equivalent to an extracted window represented + * as a stand-alone network. + * + */ +template +bool check_window_equivalence( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates, NtkWin const& win_opt ) +{ + NtkWin win; + clone_subnetwork( ntk, inputs, outputs, gates, win ); + topo_view topo_win{ win_opt }; + assert( win.num_pis() == win_opt.num_pis() ); + assert( win.num_pos() == win_opt.num_pos() ); + + default_simulator sim( inputs.size() ); + auto const tts1 = simulate( win, sim ); + auto const tts2 = simulate>( topo_win, sim ); + for ( auto i = 0u; i < tts1.size(); ++i ) + { + if ( tts1[i] != tts2[i] ) + { + return false; + } + } + return true; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/hash_functions.hpp b/third-party/mockturtle/include/mockturtle/utils/hash_functions.hpp new file mode 100644 index 00000000000..a4d9b17d3dd --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/hash_functions.hpp @@ -0,0 +1,156 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file hash_functions.hpp + \brief hash specializations for collections. + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +template +struct hash +{ + std::hash h; + size_t operator()( const T& t ) const { return h( t ); } +}; + +template +struct hash>; + +template +struct hash>; + +template +struct hash>; + +template +struct hash>; + +template +struct hash>; + +template +struct hash> +{ +public: + size_t operator()( const std::tuple& key ) const + { + size_t seed = ha( std::get<0>( key ) ); + seed ^= hb( std::get<1>( key ) ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + return seed; + } + +private: + hash ha; + hash hb; +}; + +template +struct hash> +{ +public: + size_t operator()( const std::tuple& key ) const + { + size_t seed = ha( std::get<0>( key ) ); + seed ^= hb( std::get<1>( key ) ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + seed ^= hc( std::get<2>( key ) ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + return seed; + } + +private: + hash ha; + hash hb; + hash hc; +}; + +template +struct hash> +{ +public: + size_t operator()( const std::vector& key ) const + { + std::size_t seed = key.size(); + for ( auto& i : key ) + { + seed ^= ha( i ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + } + return seed; + } + +private: + hash ha; +}; + +template +struct hash>> +{ +public: + size_t operator()( const std::multiset>& key ) const + { + size_t seed = key.size(); + for ( auto& x : key ) + { + seed ^= ha( x ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + } + return seed; + } + +private: + hash ha; +}; + +template +struct hash> +{ +public: + size_t operator()( const std::map& key ) const + { + size_t seed = key.size(); + for ( auto it = key.begin(); it != key.end(); it++ ) + { + seed ^= ha( it->first ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + seed ^= hb( it->second ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + } + return seed; + } + +private: + hash ha; + hash hb; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/include/percy.hpp b/third-party/mockturtle/include/mockturtle/utils/include/percy.hpp new file mode 100644 index 00000000000..da2576d4102 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/include/percy.hpp @@ -0,0 +1,51 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file percy.hpp + \brief Include percy, disable warnings for Windows + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4018 ) +#pragma warning( disable : 4068 ) +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4267 ) +#pragma warning( disable : 4334 ) +#pragma warning( disable : 4477 ) +#pragma warning( disable : 4566 ) +#pragma warning( disable : 4805 ) +#pragma warning( disable : 4996 ) +#include +#pragma warning( pop ) +#else +#include +#endif \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/include/supergate.hpp b/third-party/mockturtle/include/mockturtle/utils/include/supergate.hpp new file mode 100644 index 00000000000..2ff55c000d9 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/include/supergate.hpp @@ -0,0 +1,92 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file supergate.hpp + \brief Defines the composed gate and supergate data structure. + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../io/genlib_reader.hpp" + +#include + +namespace mockturtle +{ + +template +struct composed_gate +{ + /* unique ID */ + uint32_t id; + + /* gate is a supergate */ + bool is_super{ false }; + + /* pointer to the root library gate */ + gate const* root{ nullptr }; + + /* support of the composed gate */ + uint32_t num_vars{ 0 }; + + /* function */ + kitty::dynamic_truth_table function; + + /* area */ + double area{ 0.0 }; + + /* pin-to-pin delays */ + std::array tdelay{}; + + /* fanin gates */ + std::vector*> fanin{}; +}; + +template +struct supergate +{ + /* pointer to the root gate */ + composed_gate const* root{}; + + /* area */ + double area{ 0.0 }; + + /* pin-to-pin delay */ + std::array tdelay{}; + + /* np permutation vector */ + std::vector permutation{}; + + /* pin negations */ + uint16_t polarity{ 0 }; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/index_list/index_list.hpp b/third-party/mockturtle/include/mockturtle/utils/index_list/index_list.hpp new file mode 100644 index 00000000000..e1379d6fcda --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/index_list/index_list.hpp @@ -0,0 +1,1124 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file index_list.hpp + \brief List of indices to represent small networks. + + \author Andrea Costamagna + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../traits.hpp" + +#include + +#include +#include + +namespace mockturtle +{ + +/*! \brief Index list for mux-inverter graphs. + * + * Small network consisting of mux gates and inverters + * represented as a list of literals. + * + * Example: The following index list creates the output function + * `< ? x2 : x4>` with 4 inputs, 1 output, and 2 gates: + * `{4 | 1 << 8 | 2 << 16, 2, 4, 6, 4, 8, 10, 12}` + */ +struct muxig_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit muxig_index_list( uint32_t num_pis = 0 ) + : values( { num_pis } ) + { + } + + explicit muxig_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_gate( Fn&& fn ) const + { + assert( ( values.size() - 1u - num_pos() ) % 3 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 3 ) + { + fn( values.at( i ), values.at( i + 1 ), values.at( i + 2 ) ); + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void clear() + { + values.clear(); + values.emplace_back( 0 ); + } + + void add_inputs( uint32_t n = 1u ) + { + assert( num_pis() + n <= 0xff ); + values.at( 0u ) += n; + } + + element_type add_mux( element_type lit0, element_type lit1, element_type lit2 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + values.push_back( lit0 ); + values.push_back( lit1 ); + values.push_back( lit2 ); + return ( num_gates() + num_pis() ) << 1; + } + + void add_output( element_type lit ) + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + values.push_back( lit ); + } + +private: + std::vector values; +}; + +/*! \brief Inserts a muxig_index_list into an existing network + * + * **Required network functions:** + * - `get_constant` + * - `create_ite` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, muxig_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_maj method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + if constexpr ( useSignal ) + { + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + } + else + { + static_assert( std::is_same_v::value_type>, node>, "BeginIter value_type must be Ntk node type" ); + static_assert( std::is_same_v::value_type>, node>, "EndIter value_type must be Ntk node type" ); + } + + assert( uint64_t( std::distance( begin, end ) ) == indices.num_pis() ); + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + if constexpr ( useSignal ) + { + signals.push_back( *it ); + } + else + { + signals.emplace_back( ntk.make_signal( *it ) ); + } + } + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + signal const s0 = ( lit0 % 2 ) ? !signals.at( lit0 >> 1 ) : signals.at( lit0 >> 1 ); + signal const s1 = ( lit1 % 2 ) ? !signals.at( lit1 >> 1 ) : signals.at( lit1 >> 1 ); + signal const s2 = ( lit2 % 2 ) ? !signals.at( lit2 >> 1 ) : signals.at( lit2 >> 1 ); + signals.push_back( ntk.create_ite( s0, s1, s2 ) ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? !signals.at( i ) : signals.at( i ) ); + } ); +} + +/*! \brief Converts an mig_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( muxig_index_list const& indices ) +{ + auto s = fmt::format( "{{{} pis | {} pos | {} gates", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + s += fmt::format( ", ({} ? {} : {})", lit0, lit1, lit2 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +/*! \brief Index list for majority-inverter graphs. + * + * Small network consisting of majority gates and inverters + * represented as a list of literals. + * + * Example: The following index list creates the output function + * `<, x2, x4>` with 4 inputs, 1 output, and 2 gates: + * `{4 | 1 << 8 | 2 << 16, 2, 4, 6, 4, 8, 10, 12}` + */ +struct mig_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit mig_index_list( uint32_t num_pis = 0 ) + : values( { num_pis } ) + { + } + + explicit mig_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_gate( Fn&& fn ) const + { + assert( ( values.size() - 1u - num_pos() ) % 3 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 3 ) + { + fn( values.at( i ), values.at( i + 1 ), values.at( i + 2 ) ); + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void clear() + { + values.clear(); + values.emplace_back( 0 ); + } + + void add_inputs( uint32_t n = 1u ) + { + assert( num_pis() + n <= 0xff ); + values.at( 0u ) += n; + } + + element_type add_maj( element_type lit0, element_type lit1, element_type lit2 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + values.push_back( lit0 ); + values.push_back( lit1 ); + values.push_back( lit2 ); + return ( num_gates() + num_pis() ) << 1; + } + + void add_output( element_type lit ) + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + values.push_back( lit ); + } + +private: + std::vector values; +}; + +/*! \brief Generates a mig_index_list from a network + * + * The function requires `ntk` to consist of majority gates. + * + * **Required network functions:** + * - `foreach_fanin` + * - `foreach_gate` + * - `get_node` + * - `is_complemented` + * - `is_maj` + * - `node_to_index` + * - `num_gates` + * - `num_pis` + * - `num_pos` + * + * \param indices An index list + * \param ntk A logic network + */ +template +void encode( mig_index_list& indices, Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.foreach_pi( [&]( node const& n, uint64_t index ) { + if ( ntk.node_to_index( n ) != index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", index + 1 ); + std::abort(); + } + } ); + + /* inputs */ + indices.add_inputs( ntk.num_pis() ); + + /* gates */ + ntk.foreach_gate( [&]( node const& n, uint64_t index ) { + assert( ntk.is_maj( n ) ); + if ( ntk.node_to_index( n ) != ntk.num_pis() + index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); + std::abort(); + } + + std::array lits; + ntk.foreach_fanin( n, [&]( signal const& fi, uint64_t index ) { + if ( ntk.node_to_index( ntk.get_node( fi ) ) > ntk.node_to_index( n ) ) + { + fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); + std::abort(); + } + lits[index] = 2 * ntk.node_to_index( ntk.get_node( fi ) ) + ntk.is_complemented( fi ); + } ); + indices.add_maj( lits[0u], lits[1u], lits[2u] ); + } ); + + /* outputs */ + ntk.foreach_po( [&]( signal const& f ) { + indices.add_output( 2 * ntk.node_to_index( ntk.get_node( f ) ) + ntk.is_complemented( f ) ); + } ); + + assert( indices.size() == 1u + 3u * ntk.num_gates() + ntk.num_pos() ); +} + +/*! \brief Inserts a mig_index_list into an existing network + * + * **Required network functions:** + * - `get_constant` + * - `create_maj` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, mig_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + if constexpr ( useSignal ) + { + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + } + else + { + static_assert( std::is_same_v::value_type>, node>, "BeginIter value_type must be Ntk node type" ); + static_assert( std::is_same_v::value_type>, node>, "EndIter value_type must be Ntk node type" ); + } + + assert( uint64_t( std::distance( begin, end ) ) == indices.num_pis() ); + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + if constexpr ( useSignal ) + { + signals.push_back( *it ); + } + else + { + signals.emplace_back( ntk.make_signal( *it ) ); + } + } + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + signal const s0 = ( lit0 % 2 ) ? !signals.at( lit0 >> 1 ) : signals.at( lit0 >> 1 ); + signal const s1 = ( lit1 % 2 ) ? !signals.at( lit1 >> 1 ) : signals.at( lit1 >> 1 ); + signal const s2 = ( lit2 % 2 ) ? !signals.at( lit2 >> 1 ) : signals.at( lit2 >> 1 ); + signals.push_back( ntk.create_maj( s0, s1, s2 ) ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? !signals.at( i ) : signals.at( i ) ); + } ); +} + +/*! \brief Converts an mig_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( mig_index_list const& indices ) +{ + auto s = fmt::format( "{{{} | {} << 8 | {} << 16", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + s += fmt::format( ", {}, {}, {}", lit0, lit1, lit2 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +/*! \brief Index list for xor-and graphs. + * + * Small network represented as a list of literals. Supports XOR and + * AND gates. The list has the following 32-bit unsigned integer + * elements. It starts with a signature whose partitioned into `| + * num_gates | num_pos | num_pis |`, where `num_gates` accounts for + * the most-significant 16 bits, `num_pos` accounts for 8 bits, and + * `num_pis` accounts for the least-significant 8 bits. Afterwards, + * gates are defined as literal indexes `(2 * i + c)`, where `i` is an + * index, with 0 indexing the constant 0, 1 to `num_pis` indexing the + * primary inputs, and all successive indexes for the gates. Gate + * literals come in pairs. If the first literal has a smaller value + * than the second one, an AND gate is created, otherwise, an XOR gate + * is created. Afterwards, all outputs are defined in terms of + * literals. + * + * Example: The following index list creates the output function `(x1 + * AND x2) XOR (x3 AND x4)` with 4 inputs, 1 output, and 3 gates: + * `{4 | 1 << 8 | 3 << 16, 2, 4, 6, 8, 12, 10, 14}` + * + * Note: if `separate_header = true`, the header will be split into 3 + * elements to support networks with larger number of PIs. + */ +template +struct xag_index_list +{ +public: + using element_type = uint32_t; + /* Literal used for the first primary input */ + static constexpr uint32_t offset = 1; + +public: + explicit xag_index_list( uint32_t num_pis = 0 ) + : values( { num_pis } ) + { + if constexpr ( separate_header ) + { + values.emplace_back( 0 ); + values.emplace_back( 0 ); + } + } + + explicit xag_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + if constexpr ( separate_header ) + { + return values.at( 2 ); + } + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + if constexpr ( separate_header ) + { + return values.at( 0 ); + } + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + if constexpr ( separate_header ) + { + return values.at( 1 ); + } + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_gate( Fn&& fn ) const + { + if constexpr ( separate_header ) + { + assert( ( values.size() - 3u - num_pos() ) % 2 == 0 ); + for ( uint64_t i = 3u; i < values.size() - num_pos(); i += 2 ) + { + fn( values.at( i ), values.at( i + 1 ) ); + } + } + else + { + assert( ( values.size() - 1u - num_pos() ) % 2 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 2 ) + { + fn( values.at( i ), values.at( i + 1 ) ); + } + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void clear() + { + values.clear(); + values.emplace_back( 0 ); + if constexpr ( separate_header ) + { + values.emplace_back( 0 ); + values.emplace_back( 0 ); + } + } + + void add_inputs( uint32_t n = 1u ) + { + if constexpr ( !separate_header ) + { + assert( num_pis() + n <= 0xff ); + } + values.at( 0u ) += n; + } + + element_type add_and( element_type lit0, element_type lit1 ) + { + if constexpr ( separate_header ) + { + values.at( 2u ) += 1; + } + else + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + } + + values.push_back( lit0 < lit1 ? lit0 : lit1 ); + values.push_back( lit0 < lit1 ? lit1 : lit0 ); + return ( num_gates() + num_pis() ) << 1; + } + + element_type add_xor( element_type lit0, element_type lit1 ) + { + if constexpr ( separate_header ) + { + values.at( 2u ) += 1; + } + else + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + } + + values.push_back( lit0 > lit1 ? lit0 : lit1 ); + values.push_back( lit0 > lit1 ? lit1 : lit0 ); + return ( num_gates() + num_pis() ) << 1; + } + + void add_output( element_type lit ) + { + if constexpr ( separate_header ) + { + values.at( 1u ) += 1; + } + else + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + } + + values.push_back( lit ); + } + + /*! \brief Shift right the literal, removing the complementation bit */ + inline uint32_t get_index( element_type const& lit ) const + { + return lit >> 1; + } + + /*! \brief Returns the node index excluding the constants and the inputs */ + inline uint32_t get_node_index( element_type const& lit ) const + { + return get_index( lit ) - num_pis() - offset; + } + + /*! \brief Returns index of an input literal */ + inline uint32_t get_pi_index( element_type const& lit ) const + { + return get_index( lit ) - offset; + } + + /*! \brief When the literal is even ( bit0 = 0 ), it is not complemented */ + inline bool is_complemented( element_type const& lit ) const + { + return lit % 2; + } + + /*! \brief The AND gate is distinguished by the EXOR gate based on the order of the input literals */ + inline bool is_and( element_type const& lit_lhs, element_type const& lit_rhs ) const + { + return lit_lhs < lit_rhs; + } + + /*! \brief Check if a literal is a PI + * + * The index of the literal is shifted based on the usage of the separate_header. + * There are two cases identifying that the literal is not an input: + * - The shifted index overflows : the literal was a constant 0.aig_hash + * - The shifted integer is larger than the number of inputs. + */ + inline bool is_pi( element_type const& lit ) const + { + uint32_t index = get_index( lit ); + return index < num_pis() + 1; + } + + inline bool is_constant( element_type const& lit ) const + { + return lit <= 1; + } + + inline element_type po_at( uint32_t index ) + { + assert( index < num_pos() ); + return *( values.end() - num_pos() + index ); + } + +private: + std::vector values; +}; + +using large_xag_index_list = xag_index_list; + +/*! \brief Generates a xag_index_list from a network + * + * The function requires `ntk` to consist of XOR and AND gates. + * + * **Required network functions:** + * - `foreach_fanin` + * - `foreach_gate` + * - `get_node` + * - `is_and` + * - `is_complemented` + * - `is_xor` + * - `node_to_index` + * - `num_gates` + * - `num_pis` + * - `num_pos` + * + * \param indices An index list + * \param ntk A logic network + */ +template +void encode( xag_index_list& indices, Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.foreach_pi( [&]( node const& n, uint64_t index ) { + if ( ntk.node_to_index( n ) != index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", index + 1 ); + std::abort(); + } + } ); + + /* inputs */ + indices.add_inputs( ntk.num_pis() ); + + /* gates */ + ntk.foreach_gate( [&]( node const& n, uint64_t index ) { + assert( ntk.is_and( n ) || ntk.is_xor( n ) ); + if ( ntk.node_to_index( n ) != ntk.num_pis() + index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); + std::abort(); + } + + std::array lits; + ntk.foreach_fanin( n, [&]( signal const& fi, uint64_t index ) { + if ( ntk.node_to_index( ntk.get_node( fi ) ) > ntk.node_to_index( n ) ) + { + fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); + std::abort(); + } + lits[index] = 2 * ntk.node_to_index( ntk.get_node( fi ) ) + ntk.is_complemented( fi ); + } ); + + if ( ntk.is_and( n ) ) + { + indices.add_and( lits[0u], lits[1u] ); + } + else if ( ntk.is_xor( n ) ) + { + indices.add_xor( lits[0u], lits[1u] ); + } + } ); + + /* outputs */ + ntk.foreach_po( [&]( signal const& f ) { + indices.add_output( 2 * ntk.node_to_index( ntk.get_node( f ) ) + ntk.is_complemented( f ) ); + } ); + + if constexpr ( separate_header ) + { + assert( indices.size() == 3u + 2u * ntk.num_gates() + ntk.num_pos() ); + } + else + { + assert( indices.size() == 1u + 2u * ntk.num_gates() + ntk.num_pos() ); + } +} + +/*! \brief Inserts a xag_index_list into an existing network + * + * **Required network functions:** + * - `create_and` + * - `create_xor` + * - `get_constant` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, xag_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + if constexpr ( useSignal ) + { + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + } + else + { + static_assert( std::is_same_v::value_type>, node>, "BeginIter value_type must be Ntk node type" ); + static_assert( std::is_same_v::value_type>, node>, "EndIter value_type must be Ntk node type" ); + } + + assert( uint64_t( std::distance( begin, end ) ) == indices.num_pis() ); + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + if constexpr ( useSignal ) + { + signals.push_back( *it ); + } + else + { + signals.emplace_back( ntk.make_signal( *it ) ); + } + } + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1 ) { + assert( lit0 != lit1 ); + uint32_t const i0 = indices.get_index( lit0 ); + uint32_t const i1 = indices.get_index( lit1 ); + signal const s0 = indices.is_complemented( lit0 ) ? ntk.create_not( signals.at( i0 ) ) : signals.at( i0 ); + signal const s1 = indices.is_complemented( lit1 ) ? ntk.create_not( signals.at( i1 ) ) : signals.at( i1 ); + signals.push_back( lit0 > lit1 ? ntk.create_xor( s0, s1 ) : ntk.create_and( s0, s1 ) ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + uint32_t const i = indices.get_index( lit ); + fn( indices.is_complemented( lit ) ? ntk.create_not( signals.at( i ) ) : signals.at( i ) ); + } ); +} + +/*! \brief Converts an xag_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( xag_index_list const& indices ) +{ + auto s = fmt::format( "{{{} | {} << 8 | {} << 16", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1 ) { + s += fmt::format( ", {}, {}", lit0, lit1 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +inline std::string to_index_list_string( xag_index_list const& indices ) +{ + auto s = fmt::format( "{{{}, {}, {}", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1 ) { + s += fmt::format( ", {}, {}", lit0, lit1 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +/*! \brief Generates a network from an index_list + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * + * \param ntk A logic network + * \param indices An index list + */ +template +void decode( Ntk& ntk, IndexList const& indices ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + + using signal = typename Ntk::signal; + + std::vector signals( indices.num_pis() ); + std::generate( std::begin( signals ), std::end( signals ), + [&]() { return ntk.create_pi(); } ); + + insert( ntk, std::begin( signals ), std::end( signals ), indices, + [&]( signal const& s ) { ntk.create_po( s ); } ); +} + +/*! \brief Enumerate structured index_lists + * + * Enumerate concrete `xag_index_list`s from an abstract index list + * specification. The specifiation is provided in an extended index + * list format, where a `-1` indicates an unspecified input. + * + * The algorithm concretizes unspecified inputs and negates nodes. + * + * The enumerator generates the following concrete index lists + * {2 | 1 << 8 | 1 << 16, 2, 4, 6} + * {2 | 1 << 8 | 1 << 16, 2, 4, 7} + * {2 | 1 << 8 | 1 << 16, 3, 4, 6} + * {2 | 1 << 8 | 1 << 16, 3, 4, 7} + * {2 | 1 << 8 | 1 << 16, 3, 5, 6} + * {2 | 1 << 8 | 1 << 16, 3, 5, 7} + * {2 | 1 << 8 | 1 << 16, 2, 5, 6} + * {2 | 1 << 8 | 1 << 16, 2, 5, 7} + * from the abstract index list specification `{ -1, -1, 6 }`. + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_index_list_enumerator e( { -1, -1, -1, 6, 8 }, 2u, 2u, 1u ); + e.run( [&]( xag_index_list const& il ) { + aig_network aig; + decode( aig, il ); + } ); + \endverbatim + */ +class aig_index_list_enumerator +{ +public: + explicit aig_index_list_enumerator( std::vector const& values, uint32_t num_pis, uint32_t num_gates, uint32_t num_pos ) + : values_( values ), num_pis( num_pis ), num_gates( num_gates ), num_pos( num_pos ) + {} + + template + void run( Fn&& fn ) + { + recurse( values_, 0u, fn ); + } + +protected: + template + void recurse( std::vector values, uint32_t pos, Fn&& fn ) + { + /* process gate */ + if ( pos < 2 * num_gates ) + { + auto& a = values.at( pos ); + auto& b = values.at( pos + 1 ); + if ( a == -1 && b == -1 ) + { + for ( uint32_t i = 0u; i < num_pis; ++i ) + { + a = ( i + 1 ) << 1; + for ( uint32_t j = i + 1; j < num_pis; ++j ) + { + b = ( j + 1 ) << 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + } + } + return; + } + else if ( a == -1 ) + { + for ( uint32_t i = 0u; i < num_pis; ++i ) + { + a = ( i + 1 ) << 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + } + return; + } + else if ( b == -1 ) + { + for ( uint32_t i = 0u; i < num_pis; ++i ) + { + b = ( i + 1 ) << 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + } + return; + } + + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + return; + } + + /* process output */ + if ( pos < values.size() ) + { + auto& o = values.at( pos ); + recurse( values, pos + 1u, fn ); + o = o ^ 1; + recurse( values, pos + 1u, fn ); + return; + } + + /* finished processing values */ + std::vector index_list; + index_list.emplace_back( num_pis | ( num_pos << 8 ) | ( num_gates << 16 ) ); + for ( int32_t const& v : values ) + { + index_list.emplace_back( v ); + } + fn( xag_index_list( index_list ) ); + } + +protected: + std::vector values_; + uint32_t num_pis; + uint32_t num_gates; + uint32_t num_pos; +}; + +template +struct is_index_list : std::false_type +{ +}; + +template<> +struct is_index_list> : std::true_type +{ +}; + +template<> +struct is_index_list> : std::true_type +{ +}; + +template<> +struct is_index_list : std::true_type +{ +}; + +template +inline constexpr bool is_index_list_v = is_index_list::value; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/index_list/list_simulator.hpp b/third-party/mockturtle/include/mockturtle/utils/index_list/list_simulator.hpp new file mode 100644 index 00000000000..926b1cc1d49 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/index_list/list_simulator.hpp @@ -0,0 +1,179 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file list_simulator.hpp + \brief Simulator engine for index lists. + + \author Andrea Costamagna +*/ + +#pragma once + +#include "index_list.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Simulator engine for XAIG-index lists. + * + * This engine can be used to efficiently simulate many index lists. + * In this context, a simulation pattern is a truth table corresponding + * to a node’s Boolean behavior under the given input assignments. + * It pre-allocates the memory necessary to store the simulation patterns + * of an index list, and extends this memory when the evaluator is required + * to perform Boolean evaluation of a list that is larger than the current capacity + * of the simulator. To avoid unnecessary copies, the input simulation patterns + * must be passed as a vector of raw pointers. + * + * \tparam TT Truth table type. + * \tparam separate_header + * + * \verbatim embed:rst + + Example + + .. code-block:: c++ + + xag_list_simulator> sim; + sim( list1, inputs1 ); + sim( list2, inputs2 ); + \endverbatim + */ +template +class xag_list_simulator +{ +public: + /* Type of the literals of the index list */ + using element_type = typename xag_index_list::element_type; + + xag_list_simulator() + : const0( TT().construct() ) + { + /* The value 20 is chosen as it allows us to store most practical XAIG index lists */ + sims.resize( 20u ); + } + + /*! \brief Extract the simulation of a literal + * + * Inline specifier used to ensure manual inlining. + * + * \param res Truth table where to store the result. + * \param list Index list to be simulated. + * \param inputs Vector of pointers to the input truth tables. + * \param lit Literal whose simulation we want to extract. + */ + template + inline void get_simulation_inline( TT& res, xag_index_list const& list, std::vector const& inputs, element_type const& lit ) + { + if ( list.is_constant( lit ) ) + { + res = complement( const0, list.is_complemented( lit ) ); + return; + } + if ( list.is_pi( lit ) ) + { + uint32_t index = list.get_pi_index( lit ); + TT const& sim = *inputs[index]; + res = complement( sim, list.is_complemented( lit ) ); + return; + } + uint32_t index = list.get_node_index( lit ); + TT const& sim = sims[index]; + res = complement( sim, list.is_complemented( lit ) ); + } + + /*! \brief Simulate the list in topological order. + * + * This method updates the internal state of the simulator by + * storing in `sims` the simulation patterns of the nodes in the list. + * + * \param list Index list to be simulated. + * \param inputs Vector of TT references corresponding to the input truth tables + */ + template + void operator()( xag_index_list const& list, std::vector const& inputs ) + { + /* update the allocated memory */ + if ( sims.size() < list.num_gates() ) + sims.resize( std::max( sims.size(), list.num_gates() ) ); + /* traverse the list in topological order and simulate each node */ + size_t i = 0; + list.foreach_gate( [&]( element_type const& lit_lhs, element_type const& lit_rhs ) { + auto const [tt_lhs_ptr, is_lhs_compl] = get_simulation( list, inputs, lit_lhs ); + auto const [tt_rhs_ptr, is_rhs_compl] = get_simulation( list, inputs, lit_rhs ); + sims[i++] = list.is_and( lit_lhs, lit_rhs ) + ? complement( *tt_lhs_ptr, is_lhs_compl ) & complement( *tt_rhs_ptr, is_rhs_compl ) + : complement( *tt_lhs_ptr, is_lhs_compl ) ^ complement( *tt_rhs_ptr, is_rhs_compl ); + } ); + } + +private: + inline TT complement( TT const& tt, bool c ) + { + return c ? ~tt : tt; + } + + /*! \brief Return the simulation associated to the literal + * + * \param list An XAIG index list, with or without separated header. + * \param inputs A vector of pointers to the input simulation patterns. + * \param lit The literal whose simulation we want to extract. + * + * The size of `inputs` should be equal to the number of inputs of the list. + * Keep private to avoid giving external access to memory that could be later corrupted. + * + * \return A tuple containing a pointer to the simulation pattern and a flag for complementation. + */ + template + [[nodiscard]] std::tuple get_simulation( xag_index_list const& list, std::vector const& inputs, element_type const& lit ) + { + if ( list.num_pis() != inputs.size() ) + throw std::invalid_argument( "Mismatch between number of PIs and input simulations." ); + if ( list.is_constant( lit ) ) + { + return { &const0, list.is_complemented( lit ) }; + } + if ( list.is_pi( lit ) ) + { + uint32_t index = list.get_pi_index( lit ); + TT const& sim = *inputs[index]; + return { &sim, list.is_complemented( lit ) }; + } + uint32_t index = list.get_node_index( lit ); + TT const& sim = sims[index]; + return { &sim, list.is_complemented( lit ) }; + } + +private: + std::vector sims; + TT const0; + +}; /* list simulator */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/json_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/json_utils.hpp new file mode 100644 index 00000000000..b19f11b631d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/json_utils.hpp @@ -0,0 +1,53 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file json_utils.hpp + \brief JSON utils + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace kitty +{ + +inline void to_json( nlohmann::json& j, const dynamic_truth_table& tt ) +{ + j = nlohmann::json{ { "_bits", tt._bits }, { "_num_vars", tt._num_vars } }; +} + +inline void from_json( const nlohmann::json& j, dynamic_truth_table& tt ) +{ + j.at( "_bits" ).get_to( tt._bits ); + j.at( "_num_vars" ).get_to( tt._num_vars ); +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/mixed_radix.hpp b/third-party/mockturtle/include/mockturtle/utils/mixed_radix.hpp new file mode 100644 index 00000000000..1660a49f9d4 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/mixed_radix.hpp @@ -0,0 +1,113 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mixed_radix.hpp + \brief Mixed radix loop + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle +{ + +/*! \brief Mixed radix enumeration. + * + * The iterator pair `begin` and `end` represent a list of radixes, which are + * used to enumerate all combinations of indexes. + * + * For example if the radixes are \f$2, 3, 3\f$, then the callable `fn` is + * called on the index lists \f$0, 0, 0\f$, \f$0, 0, 1\f$, \f$0, 0, 2\f$, + * \f$\dots\f$, \f$1, 2, 1\f$, \f$1, 2, 2\f$. + * + * The callable `fn` expects two parameters which are an iterator pair of the + * indexes. If it returns a `bool`, the iteration is stopped, if the return + * value is `false`. + * + * \param begin Begin iterator of radixes + * \param end End iterator of radixes + * \param fn Callable + */ +template +void foreach_mixed_radix_tuple( Iterator begin, Iterator end, Fn&& fn ) +{ + constexpr auto is_bool_f = std::is_invocable_r_v::iterator, std::vector::iterator>; + constexpr auto is_void_f = std::is_invocable_r_v::iterator, std::vector::iterator>; + + static_assert( is_bool_f || is_void_f ); + + std::vector positions( std::distance( begin, end ), 0u ); + + while ( true ) + { + if constexpr ( is_bool_f ) + { + if ( !fn( positions.begin(), positions.end() ) ) + { + return; + } + } + else + { + fn( positions.begin(), positions.end() ); + } + + auto itm = end - 1; + auto itp = positions.end() - 1; + auto ret = false; + while ( !ret && *itp == ( *itm - 1 ) ) + { + *itp = 0; + if ( itp != positions.begin() ) + { + --itp; + } + + if ( itm == begin ) + { + ret = true; + } + else + { + --itm; + } + } + + if ( ret ) + { + break; + } + + ( *itp )++; + } +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/name_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/name_utils.hpp new file mode 100644 index 00000000000..19e4d9d66c5 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/name_utils.hpp @@ -0,0 +1,159 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file name_utils.hpp + \brief Utility functions to restore network names after optimization. + + \author Marcel Walter + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "node_map.hpp" + +namespace mockturtle +{ + +/*! \brief Restores the network name that might have been given to network's former incarnation. + * + * \param ntk_src The source logic network, which potentially has a name + * \param ntk_dest The destination logic network, whose name is to be restored + */ +template +void restore_network_name( const NtkSrc& ntk_src, NtkDest& ntk_dest ) noexcept +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + if constexpr ( has_get_network_name_v && has_set_network_name_v ) + { + ntk_dest.set_network_name( ntk_src.get_network_name() ); + } +} + +/*! \brief Restores all names that might have been given to a network's former incarnation. + * + * **Required network functions for the NtkSrc:** + * - `foreach_node` + * - `foreach_fanin` + * - `foreach_po` + * - `get_node` + * + * \param ntk_src The source logic network, which potentially has named signals + * \param ntk_dest The destination logic network, whose names are to be restored + * \param old2new Mapping of nodes from ntk_src to signals of ntk_dest + */ +template +void restore_names( const NtkSrc& ntk_src, NtkDest& ntk_dest, node_map, NtkSrc>& old2new ) noexcept +{ + restore_network_name( ntk_src, ntk_dest ); + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node function" ); + static_assert( has_foreach_fanin_v, "NtkSrc does not implement the foreach_fanin function" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po function" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node function" ); + + const auto restore_signal_name = [&ntk_src, &ntk_dest, &old2new]( const auto& f ) { + if ( ntk_src.has_name( f ) ) + { + const auto name = ntk_src.get_name( f ); + + ntk_dest.set_name( old2new[ntk_src.get_node( f )], name ); + } + }; + + const auto restore_output_name = [&ntk_src, &ntk_dest]( [[maybe_unused]] const auto& po, const auto i ) { + if ( ntk_src.has_output_name( i ) ) + { + const auto name = ntk_src.get_output_name( i ); + + ntk_dest.set_output_name( i, name ); + } + }; + + ntk_src.foreach_node( [&ntk_src, &restore_signal_name]( const auto& n ) { ntk_src.foreach_fanin( n, restore_signal_name ); } ); + + ntk_src.foreach_po( restore_output_name ); + } +} + +/*! \brief Restore PI and PO names, matching by order. + * + * **Required network functions for NtkSrc:** + * - `foreach_pi` + * - `foreach_po` + * - `num_pis` + * - `num_pos` + * - `has_name` + * - `get_name` + * - `make_signal` + * - `has_output_name` + * - `get_output_name` + * + * **Required network functions for NtkDest:** + * - `foreach_pi` + * - `num_pis` + * - `num_pos` + * - `set_name` + * - `make_signal` + * - `set_output_name` + * + * \param ntk_src The source logic network, which potentially has named signals + * \param ntk_dest The destination logic network, whose names are to be restored + */ +template +void restore_pio_names_by_order( const NtkSrc& ntk_src, NtkDest& ntk_dest ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_has_name_v && has_get_name_v, "NtkSrc does not implement the has_name and/or get_name functions" ); + static_assert( has_has_output_name_v && has_get_output_name_v, "NtkSrc does not implement the has_output_name and/or get_output_name functions" ); + static_assert( has_set_name_v && has_set_output_name_v, "NtkDest does not implement the set_name and/or set_output_name functions" ); + + assert( ntk_src.num_pis() == ntk_dest.num_pis() ); + assert( ntk_src.num_pos() == ntk_dest.num_pos() ); + + std::vector pi_names( ntk_src.num_pis(), "" ); + ntk_src.foreach_pi( [&]( auto const& n, auto i ) { + if ( ntk_src.has_name( ntk_src.make_signal( n ) ) ) + pi_names[i] = ntk_src.get_name( ntk_src.make_signal( n ) ); + } ); + ntk_dest.foreach_pi( [&]( auto const& n, auto i ) { + if ( pi_names[i] != "" ) + ntk_dest.set_name( ntk_dest.make_signal( n ), pi_names[i] ); + } ); + + ntk_src.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk_src.has_output_name( i ) ) + ntk_dest.set_output_name( i, ntk_src.get_output_name( i ) ); + } ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/network_cache.hpp b/third-party/mockturtle/include/mockturtle/utils/network_cache.hpp new file mode 100644 index 00000000000..26e28d31885 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/network_cache.hpp @@ -0,0 +1,173 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file network_cache.hpp + \brief Network cache + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../algorithms/simulation.hpp" +#include "../io/verilog_reader.hpp" +#include "../io/write_verilog.hpp" +#include "../traits.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Network cache. + * + * ... + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + ... + \endverbatim + */ +template> +class network_cache +{ +public: + explicit network_cache( uint32_t num_vars ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + + ensure_pis( num_vars ); + } + + Ntk& network() + { + return _db; + } + + std::vector> const& pis() const + { + return _pis; + } + + void ensure_pis( uint32_t count ) + { + if ( count > _pis.size() ) + { + for ( auto i = _pis.size(); i < count; ++i ) + { + _pis.emplace_back( _db.create_pi() ); + } + } + } + + auto size() const + { + return _db.num_pos(); + } + + bool has( Key const& key ) const + { + return _map.find( key ) != _map.end(); + } + + template + bool insert( Key const& key, _Ntk const& ntk ) + { + /* ntk must have one primary output and not too many primary inputs */ + if ( ntk.num_pos() != 1u || ntk.num_pis() > _pis.size() ) + { + return false; + } + + /* insert ntk into _db, create an output and return the index of the output */ + const auto f = cleanup_dangling( ntk, _db, _pis.begin(), _pis.begin() + ntk.num_pis() ).front(); + insert_signal( key, f ); + return true; + } + + void insert_signal( Key const& key, signal const& f ) + { + _map.emplace( key, f ); + _db.create_po( f ); + _output_functions.push_back( key ); + } + + signal get( Key const& key ) const + { + return _map.at( key ).first; + } + + auto get_view( Key const& key ) const + { + return topo_view( _db, _map.at( key ) ); + } + + void insert_json( nlohmann::json const& data ) + { + ensure_pis( data["num_pis"].get() ); + + std::istringstream sstr( data["db"].get() ); + Ntk read_ntk; + lorina::read_verilog( sstr, verilog_reader( read_ntk ) ); + const auto pos = cleanup_dangling( read_ntk, _db, _pis.begin(), _pis.end() ); + + auto cntr = 0u; + for ( auto const& tt : data["output_functions"].get>() ) + { + insert_signal( tt, pos[cntr++] ); + } + } + + nlohmann::json to_json() const + { + std::stringstream sstr; + write_verilog( _db, sstr ); + + return nlohmann::json{ { "num_pis", _pis.size() }, { "output_functions", _output_functions }, { "db", sstr.str() } }; + } + +private: + Ntk _db; + std::vector> _pis; + std::unordered_map, Hash> _map; + std::vector _output_functions; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/network_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/network_utils.hpp new file mode 100644 index 00000000000..0a6bddaa0f8 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/network_utils.hpp @@ -0,0 +1,237 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file network_utils.hpp + \brief Utility functions to insert a network into another network. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "node_map.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +auto clone_node_topologically( NtkSrc const& ntk, NtkDest& subntk, unordered_node_map& node_to_signal, typename NtkSrc::node n ) +{ + if ( node_to_signal.has( n ) ) + { + return node_to_signal[n]; + } + + std::vector children; + ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto s = clone_node_topologically( ntk, subntk, node_to_signal, ntk.get_node( fi ) ); + children.emplace_back( ntk.is_complemented( fi ) ? !s : s ); + } ); + + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v && "NtkDest does not implement the create_and method" ); + if ( ntk.is_and( n ) ) + { + assert( children.size() == 2u ); + return node_to_signal[n] = subntk.create_and( children[0], children[1] ); + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v && "NtkDest does not implement the create_xor method" ); + if ( ntk.is_xor( n ) ) + { + assert( children.size() == 2u ); + return node_to_signal[n] = subntk.create_xor( children[0], children[1] ); + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v && "NtkDest does not implement the create_maj method" ); + if ( ntk.is_maj( n ) ) + { + assert( children.size() == 3u ); + return node_to_signal[n] = subntk.create_maj( children[0], children[1], children[2] ); + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v && "NtkDest does not implement the create_xor3 method" ); + if ( ntk.is_xor3( n ) ) + { + assert( children.size() == 3u ); + return node_to_signal[n] = subntk.create_xor3( children[0], children[1], children[2] ); + } + } + + assert( false && "[e] unsupported node type" ); + return subntk.get_constant( false ); +} + +} // namespace detail + +/*! \brief Constructs a (sub-)network from a window of another network. + * + * The window is specified by three parameters: + * 1.) `inputs` are the common support of all window nodes, they do + * not overlap with `gates` (i.e., the intersection of `inputs` and + * `gates` is the empty set). + * 2.) `gates` are the nodes in the window, supported by the + * `inputs` (i.e., `gates` are in the transitive fanout of the + * `inputs`). + * 3.) `outputs` are signals (regular or complemented nodes) + * pointing to nodes in `gates` or `inputs`. Not all fanouts + * of an output node are already part of the window. + * + * **Required network functions for the source Ntk:** + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_complemented` + * + * **Required network functions for the cloned SubNtk:** + * - `create_pi` + * - `create_po` + * - `create_not` + * - `get_constant` + * + * \param ntk A logic network + * \param subntk An empty network to be constructed + */ +template +void clone_subnetwork( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates, SubNtk& subntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + + static_assert( is_network_type_v, "SubNtk is not a network type" ); + static_assert( has_create_pi_v, "SubNtk does not implement the create_pi method" ); + static_assert( has_create_po_v, "SubNtk does not implement the create_po method" ); + static_assert( has_create_not_v, "SubNtk does not implement the create_not method" ); + static_assert( has_get_constant_v, "SubNtk does not implement the get_constant method" ); + + /* map from nodes in ntk to signals in subntk */ + unordered_node_map node_to_signal( ntk ); + + /* constant */ + node_to_signal[ntk.get_node( ntk.get_constant( false ) )] = subntk.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_signal[ntk.get_node( ntk.get_constant( true ) )] = subntk.get_constant( true ); + } + + /* inputs */ + for ( auto const& i : inputs ) + { + node_to_signal[i] = subntk.create_pi(); + } + + /* create gates topologically */ + for ( auto const& g : gates ) + { + detail::clone_node_topologically( ntk, subntk, node_to_signal, g ); + } + + /* outputs */ + for ( auto const& o : outputs ) + { + subntk.create_po( ntk.is_complemented( o ) ? subntk.create_not( node_to_signal[ntk.get_node( o )] ) : node_to_signal[ntk.get_node( o )] ); + } +} + +/*! \brief Inserts a network into another network + * + * **Required network functions for the host Ntk:** + * - `get_constant` + * - `create_not` + * + * **Required network functions for the subnetwork SubNtk:** + * - `num_pis` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_gate` + * - `foreach_fanin` + * - `get_node` + * - `is_complemented` + * + * \param ntk The host logic network + * \param begin Begin iterator of signal inputs in the host network + * \param end End iterator of signal inputs in the host network + * \param subntk The sub-network + * \param fn Callback function + */ +template +void insert_ntk( Ntk& ntk, BeginIter begin, EndIter end, SubNtk const& subntk, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + static_assert( is_network_type_v, "SubNtk is not a network type" ); + static_assert( has_num_pis_v, "SubNtk does not implement the num_pis method" ); + static_assert( has_foreach_pi_v, "SubNtk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "SubNtk does not implement the foreach_po method" ); + static_assert( has_foreach_gate_v, "SubNtk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "SubNtk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "SubNtk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "SubNtk does not implement the is_complemented method" ); + + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + + assert( uint64_t( std::distance( begin, end ) ) == subntk.num_pis() ); + + /* map from nodes in subntk to signals in ntk */ + unordered_node_map node_to_signal( subntk ); + + /* inputs */ + auto it = begin; + subntk.foreach_pi( [&]( auto const& n ) { + node_to_signal[n] = *( it++ ); + } ); + + /* create gates topologically */ + subntk.foreach_gate( [&]( auto const& n ) { + detail::clone_node_topologically( subntk, ntk, node_to_signal, n ); + } ); + + /* outputs */ + subntk.foreach_po( [&]( auto const& f ) { + fn( subntk.is_complemented( f ) ? ntk.create_not( node_to_signal[subntk.get_node( f )] ) : node_to_signal[subntk.get_node( f )] ); + } ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/node_map.hpp b/third-party/mockturtle/include/mockturtle/utils/node_map.hpp new file mode 100644 index 00000000000..6fb4e1279a1 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/node_map.hpp @@ -0,0 +1,555 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file node_map.hpp + \brief Map indexed by network nodes + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Associative container network nodes + * + * This container helps to store and access values associated to nodes + * in a network. + * + * Two implementations are provided, one using std::vector and another + * using std::unordered_map as internal storage. The former + * implementation can be pre-allocated and provides a fast way to + * access the data. The later implementation offers a way to + * associate values to a subset of nodes and to check whether a value + * is available. + */ +template> +class node_map; + +/*! \brief Vector node map + * + * This container is initialized with a network to derive the size + * according to the number of nodes. The container can be accessed + * via nodes, or indirectly via signals, from which the corresponding + * node is derived. + * + * The implementation uses a vector as underlying data structure which + * is indexed by the node's index. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `node_to_index` + * + */ +template +class node_map> +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::vector; + using reference = typename container_type::reference; + using const_reference = typename container_type::const_reference; + +public: + /*! \brief Default constructor. */ + explicit node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared( ntk.size() ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Constructor with default value. + * + * Initializes all values in the container to `init_value`. + */ + node_map( Ntk const& ntk, T const& init_value ) + : ntk( &ntk ), + data( std::make_shared( ntk.size(), init_value ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Deep copy. */ + node_map copy() const + { + node_map copy( ntk ); + *( copy.data ) = *data; + return copy; + } + + /*! \brief Mutable access to value by node. */ + reference operator[]( node const& n ) + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Constant access to value by node. */ + const_reference operator[]( node const& n ) const + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + reference operator[]( signal const& f ) + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + const_reference operator[]( signal const& f ) const + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with `init_value`. + * + * \param init_value Initialization value after resize + */ + void reset( T const& init_value = {} ) + { + data->clear(); + data->resize( ntk->size(), init_value ); + } + + /*! \brief Resizes the map. + * + * This function should be called, if the node_map's size needs to + * be changed without clearing its data. + * + * \param init_value Initialization value after resize + */ + void resize( T const& init_value = {} ) + { + if ( ntk->size() > data->size() ) + { + data->resize( ntk->size(), init_value ); + } + } + +private: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Unordered node map + * + * This implementation of the container is initialized with a network. + * The map entries are constructed on the fly. The container + * can be accessed via ndoes, or indirectly via signals, from which + * the corresponding node is derived. + * + * The implementation uses an std::unordered_map as underlying data + * structure which is indexed by the node's index. + * + * This implementation is aliased as `unordered_node_map`. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * + */ +template +class node_map> +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::unordered_map; + using reference = T&; + using const_reference = const T&; + +public: + /*! \brief Default constructor. */ + explicit node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared() ) + { + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Deep copy. */ + node_map copy() const + { + node_map copy( ntk ); + *( copy.data ) = *data; + return copy; + } + + /*! \brief Check if a key is already defined. */ + bool has( node const& n ) const + { + return data->find( ntk->node_to_index( n ) ) != data->end(); + } + + /*! \brief Check if a key is already defined. */ + template>> + bool has( signal const& f ) const + { + return data->find( ntk->node_to_index( ntk->get_node( f ) ) ) != data->end(); + } + + /*! \brief Erase a key (if it exists). */ + void erase( node const& n ) + { + if ( has( n ) ) + { + data->erase( ntk->node_to_index( n ) ); + } + } + + /*! \brief Erase a key (if it exists). */ + template>> + void erase( signal const& f ) + { + if ( has( ntk->get_node( f ) ) ) + { + data->erase( ntk->node_to_index( ntk->get_node( f ) ) ); + } + } + + /*! \brief Mutable access to value by node. */ + reference operator[]( node const& n ) + { + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Constant access to value by node. */ + const_reference operator[]( node const& n ) const + { + assert( has( n ) && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + reference operator[]( signal const& f ) + { + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + const_reference operator[]( signal const& f ) const + { + assert( has( ntk->get_node( f ) ) && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Clear all entries of the map. + * + * All data in the map is cleared. + */ + void reset() + { + data->clear(); + } + + void resize() + { + } + +protected: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Template alias `unordered_node_map` */ +template +using unordered_node_map = node_map>; + +/*! \brief Vector-based node map with validity query + * + * This container is initialized with a network to derive the size + * according to the number of nodes. The container can be accessed + * via nodes, or indirectly via signals, from which the corresponding + * node is derived. + * + * The implementation uses a vector as underlying data structure, so + * that it benefits from fast access. It is supplemented with an + * additional validity field such that it can be used like an + * `unordered_node_map`. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `node_to_index` + * + */ +template +class incomplete_node_map +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::vector>; + +public: + /*! \brief Default constructor. */ + explicit incomplete_node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared( ntk.size() ) ) + { + static_assert( !std::is_same_v, "T cannot be std::monostate" ); + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Constructor with default value. + * + * Initializes all values in the container to `init_value`. + */ + incomplete_node_map( Ntk const& ntk, T const& init_value ) + : ntk( &ntk ), + data( std::make_shared( ntk.size(), init_value ) ) + { + static_assert( !std::is_same_v, "T cannot be std::monostate" ); + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Check if a key is already defined. */ + bool has( node const& n ) const + { + return std::holds_alternative( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Check if a key is already defined. */ + template>> + bool has( signal const& f ) const + { + return std::holds_alternative( ( *data )[ntk->node_to_index( ntk->get_node( f ) )] ); + } + + /*! \brief Erase a key (if it exists). */ + void erase( node const& n ) + { + ( *data )[ntk->node_to_index( n )] = std::monostate(); + } + + /*! \brief Erase a key (if it exists). */ + template>> + void erase( signal const& f ) + { + ( *data )[ntk->node_to_index( ntk->get_node( f ) )] = std::monostate(); + } + + /*! \brief Mutable access to value by node. */ + T& operator[]( node const& n ) + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + if ( !has( n ) ) + { + ( *data )[ntk->node_to_index( n )] = T(); + } + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Constant access to value by node. */ + T const& operator[]( node const& n ) const + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + assert( has( n ) ); + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + T& operator[]( signal const& f ) + { + auto n = ntk->get_node( f ); + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + if ( !has( n ) ) + { + ( *data )[ntk->node_to_index( n )] = T(); + } + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + T const& operator[]( signal const& f ) const + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + assert( has( ntk->get_node( f ) ) ); + return std::get( ( *data )[ntk->node_to_index( ntk->get_node( f ) )] ); + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with the place holder (empty) element. + */ + void reset() + { + data->clear(); + data->resize( ntk->size() ); + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with `init_value`. + * + * \param init_value Initialization value after resize + */ + void reset( T const& init_value ) + { + data->clear(); + data->resize( ntk->size(), init_value ); + } + + /*! \brief Resizes the map. + * + * This function should be called, if the node_map's size needs to + * be changed without clearing its data. + */ + void resize() + { + if ( ntk->size() > data->size() ) + { + data->resize( ntk->size() ); + } + } + +private: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Initializes a network for copying together with node map. + * + * This utility function is helpful when creating a network from another one, + * a very common task. It creates the network of type `NtkDest` and already + * creates a node map to map nodes from the source network, of type `NtkSrc` to + * nodes of the new network. The function map constant inputs and creates and + * maps primary inputs. + */ +template +std::pair, NtkSrc>> initialize_copy_network( NtkSrc const& src ) +{ + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + + node_map, NtkSrc> old2new( src ); + NtkDest dest; + old2new[src.get_constant( false )] = dest.get_constant( false ); + if ( src.get_node( src.get_constant( true ) ) != src.get_node( src.get_constant( false ) ) ) + { + old2new[src.get_constant( true )] = dest.get_constant( true ); + } + src.foreach_pi( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + } ); + return { dest, old2new }; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/null_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/null_utils.hpp new file mode 100644 index 00000000000..e031564ef81 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/null_utils.hpp @@ -0,0 +1,46 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file null_utils.hpp + \brief Placeholder empty data structures for interfacing purposes + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +namespace mockturtle +{ + +struct null_params +{ +}; +struct null_stats +{ + void report() const {} +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/progress_bar.hpp b/third-party/mockturtle/include/mockturtle/utils/progress_bar.hpp new file mode 100644 index 00000000000..a36840c218c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/progress_bar.hpp @@ -0,0 +1,178 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file progress_bar.hpp + \brief Progress bar + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Prints progress bars. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + { // some block + progress_bar bar( 100, "|{0}| neg. index = {1}, index squared = {2}" ); + + for ( auto i = 0; i < 100; ++i ) + { + bar( i, -i, i * i ); + } + } // progress bar is deleted at exit of this block + \endverbatim + */ +class progress_bar +{ +public: + /*! \brief Constructor. + * + * This constructor is used when a the total number of iterations is known, + * and the progress should be visually printed. When using this constructor, + * the current iteration must be provided to the `operator()` call. + * + * \param size Number of iterations (for progress bar) + * \param fmt Format strind; used with `fmt::format`, first placeholder `{0}` + * is used for progress bar, the others for the parameters passed + * to `operator()` + * \param enable If true, output is printed, otherwise not + * \param os Output stream + */ + progress_bar( uint32_t size, std::string const& fmt, bool enable = true, std::ostream& os = std::cout ) + : _show_progress( true ), + _size( size ), + _fmt( fmt ), + _enable( enable ), + _os( os ) {} + + /*! \brief Constructor. + * + * This constructor is used when a the total number of iterations is not + * known. In this case, the progress is not visually printed. When using this + * constructor, just pass the arguments for the `fmt` string. + * + * \param fmt Format strind; used with `fmt::format`, parameters are passed + * to `operator()` + * \param enable If true, output is printed, otherwise not + * \param os Output stream + */ + progress_bar( std::string const& fmt, bool enable = true, std::ostream& os = std::cout ) + : _show_progress( false ), + _fmt( fmt ), + _enable( enable ), + _os( os ) {} + + /*! \brief Deconstructor + * + * Will remove the last printed line and restore the cursor. + */ + ~progress_bar() + { + done(); + } + + /*! \brief Prints and updates the progress bar status. + * + * This updates the progress and re-prints the progress line. The previous + * print of the line is removed. If the progress bar has a progress segment + * the first argument must be an integer indicating the current progress + * with respect to the `size` parameter passed to the constructor. + * + * \param first Progress position or first argument + * \param args Vardiadic argument pack with values for format string + */ + template + void operator()( First first, Args... args ) + { + if ( _show_progress ) + { + show_with_progress( first, args... ); + } + else + { + show_without_progress( first, args... ); + } + } + + /*! \brief Removes the progress bar. + * + * This method is automatically invoked when the progress bar is deleted. In + * some cases, one may wish to invoke this method manually. + */ + void done() + { + if ( _enable ) + { + _os << "\u001B[G" << std::string( 79, ' ' ) << "\u001B[G\u001B[?25h" << std::flush; + } + } + +private: + template + void show_with_progress( uint32_t pos, Args... args ) + { + if ( !_enable ) + return; + + int spidx = static_cast( ( 6.0 * pos ) / _size ); + _os << "\u001B[G" << fmt::format( _fmt, spinner.substr( spidx * 5, 5 ), args... ) << std::flush; + } + + template + void show_without_progress( Args... args ) + { + if ( !_enable ) + return; + + _os << "\u001B[G" << fmt::format( _fmt, args... ) << std::flush; + } + +private: + bool _show_progress; + uint32_t _size; + std::string _fmt; + bool _enable; + std::ostream& _os; + + std::string spinner{ " . .. ... .... ....." }; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/recursive_cost_functions.hpp b/third-party/mockturtle/include/mockturtle/utils/recursive_cost_functions.hpp new file mode 100644 index 00000000000..1792cd1b932 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/recursive_cost_functions.hpp @@ -0,0 +1,122 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file recursive_cost_functions.hpp + \brief Various recursive cost functions for (optimization) algorithms + + \author Hanyu Wang +*/ + +#pragma once + +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief (Recursive) customizable cost function + * + * To define a new cost function, you need to first specify how each node + * contributes to the total cost via the *contribution function*. Each node + * is evaluated individually and independently. + * + * If additional (global) information is required to decide a node's contribution, + * you may specify them as *context*. The content stored in the context can be + * arbitrarily defined (`context_t`), but the derivation must be recursive. In + * other words, the context of a node is derived using *context propagation function* + * which takes only the context of fanins as input. + * + * Examples of recursive cost functions can be found at: + * `mockturtle/utils/recursive_cost_functions.hpp` + */ +template +struct recursive_cost_functions +{ + using base_type = recursive_cost_functions; + using context_t = uint32_t; + /*! \brief Context propagation function + * + * Return the context of a node given fanin contexts. + */ + virtual context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const = 0; + + /*! \brief Contribution function + * + * Update the total cost using node n and its context. + */ + virtual void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const = 0; +}; + +template +struct xag_depth_cost_function : recursive_cost_functions +{ +public: + using context_t = uint32_t; + context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const + { + uint32_t _cost = ntk.is_pi( n ) ? 0 : *std::max_element( std::begin( fanin_contexts ), std::end( fanin_contexts ) ) + 1; + return _cost; + } + void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const + { + total_cost = std::max( total_cost, context ); + } +}; + +template +struct t_xag_depth_cost_function : recursive_cost_functions +{ +public: + using context_t = uint32_t; + context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const + { + uint32_t _cost = ntk.is_pi( n ) ? 0 : *std::max_element( std::begin( fanin_contexts ), std::end( fanin_contexts ) ) + ntk.is_and( n ); + return _cost; + } + void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const + { + total_cost = std::max( total_cost, context ); + } +}; + +template +struct xag_size_cost_function : recursive_cost_functions +{ +public: + using context_t = uint32_t; + context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const + { + return 0; + } + void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const + { + total_cost += ( !ntk.is_pi( n ) && ntk.visited( n ) != ntk.trav_id() ) ? 1 : 0; + } +}; + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/utils/sop_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/sop_utils.hpp new file mode 100644 index 00000000000..4c2432c17aa --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/sop_utils.hpp @@ -0,0 +1,594 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sop_utils.hpp + \brief Utilities for sum-of-products + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +inline bool cube_has_lit( uint64_t const cube, uint64_t const lit ) +{ + return ( cube & ( static_cast( 1 ) << lit ) ) > 0; +} + +inline uint32_t cube_count_literals( uint64_t cube ) +{ + uint32_t count; + for ( count = 0; cube; ++count ) + { + cube &= cube - 1u; + }; + return count; +} + +inline int64_t sop_literals_occurrences( std::vector const& sop, uint32_t const num_lit ) +{ + /* find the first literal which occurs more than once */ + for ( uint64_t lit = 0; lit < num_lit; ++lit ) + { + unsigned occurrences = 0; + for ( auto const& cube : sop ) + { + if ( cube_has_lit( cube, lit ) ) + ++occurrences; + + if ( occurrences > 1 ) + return lit; + } + } + + /* each literal appears once */ + return -1; +} + +inline int64_t sop_least_occurrent_literal( std::vector const& sop, uint32_t const num_lit ) +{ + int64_t min_lit = -1; + uint32_t min_occurrences = UINT32_MAX; + + /* find the first literal which occurs more than once */ + for ( uint64_t lit = 0; lit < num_lit; ++lit ) + { + uint32_t occurrences = 0; + for ( auto const& cube : sop ) + { + if ( cube_has_lit( cube, lit ) ) + ++occurrences; + } + + if ( occurrences > 1 && occurrences < min_occurrences ) + { + min_lit = static_cast( lit ); + min_occurrences = occurrences; + } + } + + return min_lit; +} + +inline int64_t sop_most_occurrent_literal_masked( std::vector const& sop, uint64_t const cube, uint32_t const num_lit ) +{ + int64_t max_lit = -1; + uint32_t max_occurrences = 1; + + for ( uint64_t lit = 0; lit < num_lit; ++lit ) + { + if ( !cube_has_lit( cube, lit ) ) + continue; + + uint32_t occurrences = 0; + for ( auto const& c : sop ) + { + if ( cube_has_lit( c, lit ) ) + ++occurrences; + } + + if ( occurrences > max_occurrences ) + { + max_lit = static_cast( lit ); + max_occurrences = occurrences; + } + } + + return max_lit; +} + +inline bool sop_maximal_cube_literal( std::vector const& sop, uint64_t& cube, uint32_t const lit ) +{ + uint64_t max_cube = UINT64_MAX; + uint32_t occurrences = 0; + + for ( auto const& c : sop ) + { + if ( cube_has_lit( c, lit ) ) + { + ++occurrences; + max_cube &= c; + } + } + + cube = max_cube; + return occurrences > 1; +} + +inline void sop_best_literal( std::vector const& sop, std::vector& result, uint64_t const cube, uint32_t const num_lit ) +{ + int64_t max_lit = sop_most_occurrent_literal_masked( sop, cube, num_lit ); + assert( max_lit >= 0 ); + + result.push_back( static_cast( 1 ) << max_lit ); +} + +} // namespace detail + +inline uint32_t sop_count_literals( std::vector const& sop ) +{ + uint32_t lit_count = 0; + + for ( auto const& c : sop ) + { + lit_count += detail::cube_count_literals( c ); + } + + return lit_count; +} + +/*! \brief Makes a SOP cube free + * + * This method checks for a common cube divisor in + * the SOP. If found, the SOP is divided by that cube. + * + * \param sop + */ +inline void sop_make_cube_free( std::vector& sop ) +{ + /* find common cube */ + uint64_t mask = UINT64_MAX; + for ( auto const& c : sop ) + { + mask &= c; + } + + if ( mask == 0 ) + return; + + /* make cube free */ + for ( auto& c : sop ) + { + c &= ~mask; + } +} + +/*! \brief Checks if a SOP is cube free + * + * This method checks for a common cube divisor in + * the SOP. + * + * \param sop + */ +inline bool sop_is_cube_free( std::vector const& sop ) +{ + /* find common cube */ + uint64_t mask = UINT64_MAX; + for ( auto const& c : sop ) + { + mask &= c; + } + + return mask == 0; +} + +/*! \brief Algebraic division by literal + * + * This method divides a SOP inplace by a literal and + * stores the resulting quotient in the original SOP. + * + * \param sop + * \param lit + */ +inline void sop_divide_by_literal_inplace( std::vector& sop, uint64_t const lit ) +{ + uint32_t p = 0; + for ( auto i = 0; i < sop.size(); ++i ) + { + if ( detail::cube_has_lit( sop[i], lit ) ) + { + sop[p++] = sop[i] & ( ~( static_cast( 1 ) << lit ) ); + } + } + + sop.resize( p ); +} + +/*! \brief Algebraic division by a cube + * + * This method divides a SOP (divident) by the divisor + * and stores the resulting quotient and reminder. + * + * \param Divident + * \param Divisor + * \param Quotient + * \param Reminder + */ +inline void sop_divide_by_cube( std::vector const& divident, std::vector const& divisor, std::vector& quotient, std::vector& reminder ) +{ + assert( divisor.size() == 1 ); + + quotient.clear(); + reminder.clear(); + + uint32_t p = 0; + for ( auto const& c : divident ) + { + if ( ( c & divisor[0] ) == divisor[0] ) + { + quotient.push_back( c & ( ~divisor[0] ) ); + } + else + { + reminder.push_back( c ); + } + } +} + +/*! \brief Algebraic division by a cube + * + * This method divides a SOP (divident) by the divisor + * and stores the resulting quotient. + * + * \param Divident + * \param Divisor + * \param Quotient + */ +inline void sop_divide_by_cube_no_reminder( std::vector const& divident, uint64_t const& divisor, std::vector& quotient ) +{ + quotient.clear(); + + uint32_t p = 0; + for ( auto const& c : divident ) + { + if ( ( c & divisor ) == divisor ) + { + quotient.push_back( c & ~divisor ); + } + } +} + +/*! \brief Algebraic division + * + * This method divides a SOP (divident) by the divisor + * and stores the resulting quotient and reminder. + * + * \param Divident + * \param Divisor + * \param Quotient + * \param Reminder + */ +inline void sop_divide( std::vector& divident, std::vector const& divisor, std::vector& quotient, std::vector& reminder ) +{ + /* divisor contains a single cube */ + if ( divisor.size() == 1 ) + { + sop_divide_by_cube( divident, divisor, quotient, reminder ); + return; + } + + quotient.clear(); + reminder.clear(); + + /* perform division */ + for ( auto i = 0; i < divident.size(); ++i ) + { + auto const c = divident[i]; + + /* cube has been already covered */ + if ( detail::cube_has_lit( c, 63 ) ) + continue; + + uint32_t div_i; + for ( div_i = 0u; div_i < divisor.size(); ++div_i ) + { + if ( ( c & divisor[div_i] ) == divisor[div_i] ) + break; + } + + /* cube is not divisible -> reminder */ + if ( div_i >= divisor.size() ) + continue; + + /* extract quotient */ + uint64_t c_quotient = c & ~divisor[div_i]; + + /* find if c_quotient can be obtained for all the divisors */ + bool found = true; + for ( auto const& div : divisor ) + { + if ( div == divisor[div_i] ) + continue; + + found = false; + for ( auto const& c2 : divident ) + { + /* cube has been already covered */ + if ( detail::cube_has_lit( c2, 63 ) ) + continue; + + if ( ( ( c2 & div ) == div ) && ( c_quotient == ( c2 & ~div ) ) ) + { + found = true; + break; + } + } + + if ( !found ) + break; + } + + if ( !found ) + continue; + + /* valid divisor, select covered cubes */ + quotient.push_back( c_quotient ); + + divident[i] |= static_cast( 1 ) << 63; + for ( auto const& div : divisor ) + { + if ( div == divisor[div_i] ) + continue; + + for ( auto& c2 : divident ) + { + /* cube has been already covered */ + if ( detail::cube_has_lit( c2, 63 ) ) + continue; + + if ( ( ( c2 & div ) == div ) && ( c_quotient == ( c2 & ~div ) ) ) + { + c2 |= static_cast( 1 ) << 63; + break; + } + } + } + } + + /* add remainder */ + for ( auto& c : divident ) + { + if ( !detail::cube_has_lit( c, 63 ) ) + { + reminder.push_back( c ); + } + else + { + /* unmark */ + c &= ~( static_cast( 1 ) << 63 ); + } + } +} + +/*! \brief Extracts all the kernels + * + * This method is used to identify and collect all + * the kernels. + * + * \param sop + * \param kernels + * \param j + * \param num_lit + */ +inline void sop_kernels_rec( std::vector const& sop, std::vector>& kernels, uint32_t const j, uint32_t const num_lit ) +{ + std::vector kernel; + + for ( uint32_t i = j; i < num_lit; ++i ) + { + uint64_t c; + if ( detail::sop_maximal_cube_literal( sop, c, i ) ) + { + /* cube has been visited already */ + if ( ( c & ( ( static_cast( 1 ) << i ) - 1 ) ) > 0u ) + continue; + + sop_divide_by_cube_no_reminder( sop, c, kernel ); + sop_kernels_rec( kernel, kernels, i + 1, num_lit ); + } + } + + kernels.push_back( sop ); +} + +/*! \brief Extracts the best factorizing kernel + * + * This method is used to identify the best kernel + * according to the algebraic factorization value. + * + * \param sop + * \param kernel + * \param best_kernel + * \param j + * \param best_cost + * \param num_lit + */ +inline uint32_t sop_best_kernel_rec( std::vector& sop, std::vector& kernel, std::vector& best_kernel, uint32_t const j, uint32_t& best_cost, uint32_t const num_lit ) +{ + std::vector new_kernel; + std::vector quotient; + std::vector reminder; + + /* evaluate kernel */ + sop_divide( sop, kernel, quotient, reminder ); + uint32_t division_cost = sop_count_literals( quotient ) + sop_count_literals( reminder ); + uint32_t best_fact_cost = sop_count_literals( kernel ); + + for ( uint32_t i = j; i < num_lit; ++i ) + { + uint64_t c; + if ( detail::sop_maximal_cube_literal( kernel, c, i ) ) + { + /* cube has been already visited */ + if ( ( c & ( ( static_cast( 1 ) << i ) - 1 ) ) > 0u ) + continue; + + /* extract the new kernel */ + sop_divide_by_cube( kernel, { c }, new_kernel, reminder ); + uint32_t fact_cost_rec = detail::cube_count_literals( c ) + sop_count_literals( reminder ); + uint32_t fact_cost = sop_best_kernel_rec( sop, new_kernel, best_kernel, i + 1, best_cost, num_lit ); + + /* compute the factorization value for kernel */ + if ( ( fact_cost + fact_cost_rec ) < best_fact_cost ) + best_fact_cost = fact_cost + fact_cost_rec; + } + } + + if ( best_kernel.empty() || ( division_cost + best_fact_cost ) < best_cost ) + { + best_kernel = kernel; + best_cost = division_cost + best_fact_cost; + } + + return best_fact_cost; +} + +/*! \brief Extracts a one level-0 kernel + * + * This method is used to identify and return a one + * level-0 kernel. + * + * \param sop + * \param num_lit + */ +inline void sop_one_level_zero_kernel_rec( std::vector& sop, uint32_t const num_lit ) +{ + /* find least occurring leteral which occurs more than once. TODO: test other metrics */ + int64_t min_lit = detail::sop_least_occurrent_literal( sop, num_lit ); + + if ( min_lit == -1 ) + return; + + sop_divide_by_literal_inplace( sop, static_cast( min_lit ) ); + sop_make_cube_free( sop ); + + sop_one_level_zero_kernel_rec( sop, num_lit ); +} + +/*! \brief Finds a quick divisor for a SOP + * + * This method is used to identify and return a quick + * divisor for the SOP. + * + * \param sop + * \param num_lit + */ +inline bool sop_quick_divisor( std::vector const& sop, std::vector& res, uint32_t const num_lit ) +{ + if ( sop.size() <= 1 ) + return false; + + /* each literal appears no more than once */ + if ( detail::sop_literals_occurrences( sop, num_lit ) < 0 ) + return false; + + /* one level 0-kernel */ + res = sop; + sop_one_level_zero_kernel_rec( res, num_lit ); + + assert( res.size() ); + return true; +} + +/*! \brief Finds a good divisor for a SOP + * + * This method is used to identify and return a good + * divisor for the SOP. + * + * \param sop + * \param num_lit + */ +inline bool sop_good_divisor( std::vector& sop, std::vector& res, uint32_t const num_lit ) +{ + if ( sop.size() <= 1 ) + return false; + + /* each literal appears no more than once */ + if ( detail::sop_literals_occurrences( sop, num_lit ) < 0 ) + return false; + + std::vector kernel = sop; + + /* compute all the kernels and return the one with the best factorization value */ + uint32_t best_cost = 0; + sop_best_kernel_rec( sop, kernel, res, 0, best_cost, num_lit ); + + return true; +} + +/*! \brief Translates cubes into products + * + * This method translate SOP of kitty::cubes (bits + mask) + * into SOP of products represented by literals. + * Example for: ab'c* + * - cube: _bits = 1010; _mask = 1110 + * - product: 10011000 + * + * \param cubes Sum-of-products described using cubes + * \param num_vars Number of variables + */ +inline std::vector cubes_to_sop( std::vector const& cubes, uint32_t const num_vars ) +{ + using sop_t = std::vector; + + sop_t sop( cubes.size() ); + + /* Represent literals instead of variables as a a' b b' */ + /* Bit 63 is reserved, up to 31 varibles support */ + auto it = sop.begin(); + for ( auto const& c : cubes ) + { + uint64_t& product = *it++; + for ( auto i = 0; i < num_vars; ++i ) + { + if ( c.get_mask( i ) ) + product |= static_cast( 1 ) << ( 2 * i + static_cast( c.get_bit( i ) ) ); + } + } + + return sop; +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/standard_cell.hpp b/third-party/mockturtle/include/mockturtle/utils/standard_cell.hpp new file mode 100644 index 00000000000..917550dc0f8 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/standard_cell.hpp @@ -0,0 +1,98 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file standard_cell.hpp + \brief Defines logic cells. + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../io/genlib_reader.hpp" + +namespace mockturtle +{ + +struct standard_cell +{ + /* Unique name */ + std::string name; + + /* Unique ID */ + uint32_t id; + + /* Pointer to a gate representing each individual output */ + std::vector gates; + + /* Area */ + double area; +}; + +/*! \brief Reconstruct standard cells from GENLIB gates. + * + * This function returns a vector of standard cells given + * GENLIB gates. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( in, genlib_reader( gates ) ); + + // Extract standard cells + std::vector cells = get_standard_cells( gates ); + \endverbatim + */ +inline std::vector get_standard_cells( std::vector const& gates ) +{ + std::unordered_map name_to_index; + std::vector cells; + + for ( gate const& g : gates ) + { + if ( auto it = name_to_index.find( g.name ); it != name_to_index.end() ) + { + /* add to existing cell (multi-output) */ + cells[it->second].gates.push_back( g ); + } + else + { + name_to_index[g.name] = cells.size(); + cells.emplace_back( standard_cell{ g.name, static_cast( cells.size() ), { g }, g.area } ); + } + } + + return cells; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/stopwatch.hpp b/third-party/mockturtle/include/mockturtle/utils/stopwatch.hpp new file mode 100644 index 00000000000..5f1daacc302 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/stopwatch.hpp @@ -0,0 +1,180 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file stopwatch.hpp + \brief Stopwatch + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Stopwatch interface + * + * This class implements a stopwatch interface to track time. It starts + * tracking time at construction and stops tracking time at deletion + * automatically. A reference to a duration object is passed to the + * constructor. After stopping the time the measured time interval is added + * to the durationr reference. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + { // some block + stopwatch t( time ); + + // do some work + } // stopwatch is stopped here + + std::cout << fmt::format( "{:5.2f} seconds passed\n", to_seconds( time ) ); + \endverbatim + */ +template +class stopwatch +{ +public: + using clock = Clock; + using duration = typename Clock::duration; + using time_point = typename Clock::time_point; + + /*! \brief Default constructor. + * + * Starts tracking time. + */ + explicit stopwatch( duration& dur ) + : dur( dur ), + beg( clock::now() ) + { + } + + /*! \brief Default deconstructor. + * + * Stops tracking time and updates duration. + */ + ~stopwatch() + { + dur += ( clock::now() - beg ); + } + +private: + duration& dur; + time_point beg; +}; + +/*! \brief Calls a function and tracks time. + * + * The function that is passed as second parameter can be any callable object + * that takes no parameters. This construction can be used to avoid + * pre-declaring the result type of a computation that should be tracked. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + auto result = call_with_stopwatch( time, [&]() { return function( parameters ); } ); + \endverbatim + * + * \param dur Duration reference (time will be added to it) + * \param fn Callable object with no arguments + */ +template +std::invoke_result_t call_with_stopwatch( typename Clock::duration& dur, Fn&& fn ) +{ + stopwatch t( dur ); + return fn(); +} + +/*! \brief Constructs an object and calls time. + * + * This function can track the time for the construction of an object and + * returns the constructed object. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + // create vector with 100000 elements initialized to 42 + auto result = make_with_stopwatch>( time, 100000, 42 ); + \endverbatim + */ +template +T make_with_stopwatch( typename Clock::duration& dur, Args... args ) +{ + stopwatch t( dur ); + return T{ std::forward( args )... }; +} + +/*! \brief Utility function to convert duration into seconds. */ +template +inline double to_seconds( Duration const& dur ) +{ + return std::chrono::duration_cast>( dur ).count(); +} + +template +class print_time +{ +public: + print_time() + : _t( new stopwatch( _d ) ) + { + } + + ~print_time() + { + delete _t; + std::cout << fmt::format( "[i] run-time: {:5.2f} secs\n", to_seconds( _d ) ); + } + +private: + stopwatch* _t{ nullptr }; + typename stopwatch::duration _d{}; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/string_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/string_utils.hpp new file mode 100644 index 00000000000..af7101e054d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/string_utils.hpp @@ -0,0 +1,63 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file string_utils.hpp + \brief String utils + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +namespace mockturtle +{ + +template +std::invoke_result_t map_and_join( Iterator begin, Iterator end, MapFn&& map_fn, JoinFn&& join_fn ) +{ + if constexpr ( std::is_same_v, std::string> ) + { + return std::accumulate( begin + 1, end, map_fn( *begin ), + [&]( auto const& a, auto const& v ) { + return a + join_fn + map_fn( v ); + } ); + } + else + { + return std::accumulate( begin + 1, end, map_fn( *begin ), + [&]( auto const& a, auto const& v ) { + return join_fn( a, map_fn( v ) ); + } ); + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/struct_library.hpp b/third-party/mockturtle/include/mockturtle/utils/struct_library.hpp new file mode 100644 index 00000000000..165e9eb9cc2 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/struct_library.hpp @@ -0,0 +1,1542 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file struct_library.hpp + \brief Implements utilities for structural matching + + \author Alessandro Tempia Calvino + \author Gianluca Radi +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "../io/genlib_reader.hpp" +#include "include/supergate.hpp" + +namespace mockturtle +{ + +struct struct_library_params +{ + /*! \brief Load gates with minimum size only */ + bool load_minimum_size_only{ true }; + + /*! \brief Reports loaded gates */ + bool verbose{ false }; +}; + +/*! \brief Library of gates for structural matching + * + * This class creates a technology library from a set + * of input gates. + * + * Gates are processed to derive rules in the AIG format. + * Then, every rule and subrule gets a unique id and the AND table is built. + * Every gate gets a unique label comprehensive of its rule id and whether it is positive or negative. + * + * The template parameter `NInputs` selects the maximum number of variables + * allowed for a gate in the library. + * + * By default, `struct_library` is used in `tech_library` when NInputs is greater than 6. + * + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + // struct library + mockturtle::struct_library lib( gates ); + \endverbatim + */ +template +class struct_library +{ +public: + enum class node_type + { + none, + zero_, + pi_, + and_, + or_, + mux_, + xor_ + }; + + struct signal + { + union + { + struct + { + uint32_t inv : 1; + uint32_t index : 31; + }; + uint32_t data; + }; + + bool operator==( signal const& other ) const + { + return data == other.data; + } + }; + + /* struct for representing nodes in dsd decomposition */ + struct dsd_node + { + node_type type; + + uint32_t index; + + std::vector fanin = {}; + }; + + /* struct for labels to assign to gates */ + struct label + { + union + { + struct + { + uint32_t inv : 1; + uint32_t index : 31; + }; + uint32_t data; + }; + bool operator==( label const& other ) const + { + return data == other.data; + } + }; + + struct signal_hash + { + std::size_t operator()( signal const& s ) const noexcept + { + return std::hash{}( s.data ); + } + }; + + struct tuple_s_hash + { + std::size_t operator()( std::tuple const& t ) const noexcept + { + size_t h1 = signal_hash()( std::get<0>( t ) ); + size_t h2 = signal_hash()( std::get<1>( t ) ); + return (uint64_t)h1 ^ ( ( (uint64_t)h2 ) << 32 ); // or use boost::hash_combine + } + }; + +private: + static constexpr uint32_t invalid_index = UINT32_MAX; + using supergates_list_t = std::vector>; + using composed_list_t = std::vector>; + using lib_rule = phmap::flat_hash_map, kitty::hash>; + using rule = std::vector; + using lib_table = phmap::flat_hash_map, uint32_t, tuple_s_hash>; + using map_label_gate = std::unordered_map; + +public: + explicit struct_library( std::vector const& gates, struct_library_params const& ps = {} ) + : _gates( gates ), + _ps( ps ), + _supergates(), + _dsd_map(), + _and_table(), + _label_to_gate() + {} + +public: + /*! \brief Construct the structural library. + * + * Generates the patterns for structural matching. + * Variable `min_vars` defines the minimum number of + * gate inputs considered for the library creation. + * 0 < min_vars < UINT32_MAX + */ + void construct( uint32_t min_vars = 2u ) + { + generate_library( min_vars ); + } + + /*! \brief Construct the structural library. + * + * Generates the patterns for structural matching. + */ + const map_label_gate& get_struct_library() const + { + return _label_to_gate; + } + + /*! \brief Get the pattern ID. + * + * \param id1 first pattern id. + * \param id2 second pattern id. + * Returns a pattern ID if found, UINT32_MAX otherwise given the + * children IDs. This function works with only AND operators. + */ + const uint32_t get_pattern_id( uint32_t id1, uint32_t id2 ) const + { + signal l, r; + l.data = id1; + /* ignore input negations */ + if ( l.data == 3 ) + l.data = 2; + r.data = id2; + if ( r.data == 3 ) + r.data = 2; + std::tuple key; + if ( l.index <= r.index ) + key = std::make_tuple( l, r ); + else + key = std::make_tuple( r, l ); + auto match = _and_table.find( key ); + if ( match != _and_table.end() ) + return match->second; + return UINT32_MAX; + } + + /*! \brief Get the gates matching the pattern ID. + * + * Returns a list of gates that match the pattern ID. + */ + const supergates_list_t* get_supergates_pattern( uint32_t id, bool phase ) const + { + auto match = _label_to_gate.find( ( id << 1 ) | ( phase ? 1 : 0 ) ); + if ( match != _label_to_gate.end() ) + { + return &( match->second ); + } + return nullptr; + } + + /*! \brief Returns the number of large gates. + * + * Number of gates with more than 6 inputs. + */ + const uint32_t get_num_large_gates() const + { + return num_large_gates; + } + + /*! \brief Print and table. + * + */ + void print_and_table() + { + for ( auto elem : _and_table ) + { + auto first0 = std::get<0>( elem.first ); + auto first1 = std::get<1>( elem.first ); + std::cout << "<" << ( first0.inv ? "!" : "" ) << first0.index; + std::cout << ", " << ( first1.inv ? "!" : "" ) << first1.index << "> "; + std::cout << elem.second << "\n"; + } + } + +private: + void generate_library( uint32_t min_vars ) + { + /* select and load gates */ + _supergates.reserve( _gates.size() ); + generate_composed_gates(); + + /* mark dominate gates */ + std::vector skip_gates( _supergates.size(), false ); + filter_gates( skip_gates ); + + std::vector indexes( _supergates.size() ); + std::iota( indexes.begin(), indexes.end(), 0 ); + uint32_t max_label = 1; + uint32_t gate_pol = 0; // polarity of AND equivalent gate + uint32_t shift = 0; + + /* sort cells by increasing order of area */ + std::stable_sort( indexes.begin(), indexes.end(), + [&]( auto const& a, auto const& b ) -> bool { + return _supergates[a].area < _supergates[b].area; + } ); + + for ( uint32_t const ind : indexes ) + { + composed_gate const& gate = _supergates[ind]; + + if ( gate.num_vars < 2 || skip_gates[ind] ) + continue; + + /* DSD decomposition */ + rule rule = {}; + std::vector support = {}; + for ( uint32_t i = 0; i < gate.num_vars; i++ ) + { + rule.push_back( { node_type::pi_, i, {} } ); + support.push_back( i ); + } + auto cpy = gate.function; + gate_disjoint = false; + compute_dsd( cpy, support, rule ); + + /* ignore gates with reconvergence */ + if ( gate_disjoint ) + continue; + + if ( gate.num_vars > 6 ) + { + ++num_large_gates; + } + + _dsd_map.insert( { gate.function, rule } ); + if ( _ps.verbose ) + { + std::cout << "Dsd:\n"; + print_rule( rule, rule[rule.size() - 1] ); + } + + /* Aig conversion */ + auto aig_rule = map_to_aig( rule ); + if ( _ps.verbose ) + { + std::cout << "\nAig:\n"; + print_rule( aig_rule, aig_rule[aig_rule.size() - 1] ); + } + + /* Rules derivation */ + std::vector> der_rules = {}; + der_rules.push_back( aig_rule ); + std::vector> depths = { { get_depth( aig_rule, aig_rule[aig_rule[aig_rule.size() - 1].fanin[0].index] ), get_depth( aig_rule, aig_rule[aig_rule[aig_rule.size() - 1].fanin[1].index] ) } }; + create_rules_from_dsd( der_rules, aig_rule, aig_rule[aig_rule.size() - 1], depths, true, true ); + if ( _ps.verbose ) + { + std::cout << "\nDerived:\n"; + } + + /* Indexing of rules and subrules, and_table construction, and gates' label assignement */ + for ( auto elem : der_rules ) + { + gate_pol = 0; + shift = 0; + std::vector perm( gate.num_vars ); + auto index_rule = do_indexing_rule( elem, elem[elem.size() - 1], max_label, gate_pol, perm, shift ); + + /* skip gate creation for small gates (<`min_vars` inputs) */ + if ( gate.num_vars < min_vars ) + continue; + + supergate sg = { &gate, + static_cast( gate.area ), + gate.tdelay, + perm, + gate_pol }; + + /* permute pin-to-pin delays */ + for ( uint32_t i = 0; i < gate.num_vars; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + auto& v = _label_to_gate[index_rule.data]; + + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + v.insert( it, sg ); + + if ( _ps.verbose ) + { + print_rule( elem, elem[elem.size() - 1] ); + std::cout << "\n"; + for ( const std::pair& elem : _label_to_gate ) + { + std::cout << elem.first << "\n"; + for ( auto sg : elem.second ) + { + std::cout << ( sg.root )->root->expression << "\n"; + } + } + } + } + + if ( _ps.verbose ) + { + std::cout << "\n"; + std::cout << "And table:\n"; + print_and_table(); + std::cout << "\n"; + } + } + if ( _ps.verbose ) + std::cout << "\n"; + } + + void generate_composed_gates() + { + /* filter multi-output gates */ + std::unordered_map multioutput_map; + multioutput_map.reserve( _gates.size() ); + + for ( const auto& g : _gates ) + { + if ( multioutput_map.find( g.name ) != multioutput_map.end() ) + { + multioutput_map[g.name] += 1; + } + else + { + multioutput_map[g.name] = 1; + } + } + + /* create composed gates */ + uint32_t ignored = 0; + for ( const auto& g : _gates ) + { + std::array pin_to_pin_delays{}; + + /* filter large gates and multi-output gates */ + if ( g.function.num_vars() > NInputs || multioutput_map[g.name] > 1 ) + { + ++ignored; + continue; + } + + auto i = 0u; + for ( auto const& pin : g.pins ) + { + /* use worst pin delay */ + pin_to_pin_delays[i++] = std::max( pin.rise_block_delay, pin.fall_block_delay ); + } + + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + } + + bool compare_sizes( composed_gate const& s1, composed_gate const& s2 ) + { + if ( s1.area < s2.area ) + return true; + else if ( s1.area > s2.area ) + return false; + + /* compute average pin delay */ + float s1_delay = 0, s2_delay = 0; + assert( s1.num_vars == s2.num_vars ); + for ( uint32_t i = 0; i < s1.num_vars; ++i ) + { + s1_delay += s1.tdelay[i]; + s2_delay += s2.tdelay[i]; + } + + if ( s1_delay < s2_delay ) + return true; + else if ( s1_delay > s2_delay ) + return false; + else if ( s1.root->name < s2.root->name ) + return true; + + return false; + } + + void filter_gates( std::vector& skip_gates ) + { + for ( uint32_t i = 0; i < skip_gates.size() - 1; ++i ) + { + if ( _supergates[i].root == nullptr ) + continue; + + if ( skip_gates[i] ) + continue; + + auto const& tti = _supergates[i].function; + for ( uint32_t j = i + 1; j < skip_gates.size(); ++j ) + { + auto const& ttj = _supergates[j].function; + + /* get the same functionality */ + if ( skip_gates[j] || tti != ttj ) + continue; + + if ( _ps.load_minimum_size_only ) + { + if ( compare_sizes( _supergates[i], _supergates[j] ) ) + { + skip_gates[j] = true; + continue; + } + else + { + skip_gates[i] = true; + break; + } + } + + /* is i smaller than j */ + bool smaller = _supergates[i].area < _supergates[j].area; + + /* is i faster for every pin */ + bool faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( _supergates[i].tdelay[k] > _supergates[j].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[j] = true; + continue; + } + + /* is j faster for every pin */ + faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( _supergates[j].tdelay[k] > _supergates[i].tdelay[k] ) + faster = false; + } + + if ( !smaller && faster ) + { + skip_gates[i] = true; + break; + } + } + } + } + + uint32_t try_top_dec( kitty::dynamic_truth_table& tt, uint32_t num_vars ) + { + uint32_t i = 0; + for ( ; i < num_vars; i++ ) + { + auto res = is_top_dec( tt, i, false ); + if ( res.type != node_type::none ) + break; + } + return i; + } + + dsd_node do_top_dec( kitty::dynamic_truth_table& tt, uint32_t index, std::vector mapped_support ) + { + auto node = is_top_dec( tt, index, false, &tt ); + + node.fanin[0].index = mapped_support[index]; + return node; + } + + std::tuple try_bottom_dec( kitty::dynamic_truth_table& tt, uint32_t num_vars ) + { + uint32_t i; + uint32_t j; + dsd_node res; + for ( i = 0; i < num_vars; i++ ) + { + for ( j = i + 1; j < num_vars; j++ ) + { + res = is_bottom_dec( tt, i, j ); + if ( res.type != node_type::none ) + break; + } + if ( res.type != node_type::none ) + break; + } + std::tuple ret = { i, j }; + return ret; + } + + dsd_node do_bottom_dec( kitty::dynamic_truth_table& tt, uint32_t i, uint32_t j, uint32_t new_index, std::vector& mapped_support ) + { + auto node = is_bottom_dec( tt, i, j, &tt, new_index, false ); + + node.fanin[0].index = mapped_support[i]; + node.fanin[1].index = mapped_support[j]; + + mapped_support[i] = node.index; + return node; + } + + dsd_node do_shannon_dec( kitty::dynamic_truth_table tt, uint32_t index, kitty::dynamic_truth_table& co0, kitty::dynamic_truth_table& co1, std::vector mapped_support ) + { + auto node = shannon_dec( tt, index, &co0, &co1 ); + node.fanin[0].index = mapped_support[index]; + return node; + } + + void update_support( std::vector& v, uint32_t index ) + { + uint32_t i = 0; + for ( ; i < v.size() && i < index; i++ ) + ; + + for ( ; i < v.size() - 1; i++ ) + { + v[i] = v[i + 1]; + } + + v.pop_back(); + } + + template + void min_base_shrink( TT& tt, TT& tt_shr ) + { + kitty::min_base_inplace( tt ); + kitty::shrink_to_inplace( tt_shr, tt ); + } + + uint32_t is_PI( kitty::dynamic_truth_table const& rem, uint32_t n_vars ) + { + for ( uint32_t i = 0; i < n_vars; i++ ) + { + auto var = rem.construct(); + kitty::create_nth_var( var, i ); + if ( rem == var ) + { + return i; + } + } + return invalid_index; + } + + uint32_t is_inv_PI( kitty::dynamic_truth_table const& rem, uint32_t n_vars ) + { + for ( uint32_t i = 0; i < n_vars; i++ ) + { + auto var = rem.construct(); + kitty::create_nth_var( var, i ); + if ( rem == ~var ) + { + return i; + } + } + return invalid_index; + } + + void update_found_rule( kitty::dynamic_truth_table& tt, std::vector& mapped_support, std::vector& rule ) + { + uint32_t count_old = 0; + uint32_t count_curr = 0; + std::vector new_rule; + auto found_rule = get_rules( tt ); + std::copy_if( found_rule.begin(), found_rule.end(), std::back_inserter( new_rule ), []( dsd_node n ) { + return ( n.type != node_type::pi_ ); + } ); + for_each( found_rule.begin(), found_rule.end(), [&]( dsd_node elem ) { + if ( elem.type == node_type::pi_ ) + count_old++; + } ); + for_each( rule.begin(), rule.end(), [&]( dsd_node elem ) { + count_curr++; + } ); + /* update index of node */ + std::transform( new_rule.begin(), new_rule.end(), new_rule.begin(), [&]( dsd_node& n ) -> dsd_node { + return { n.type, n.index + count_curr - count_old, n.fanin }; + } ); + /* update index of signal of fanins of nodes */ + std::transform( new_rule.begin(), new_rule.end(), new_rule.begin(), [&]( dsd_node& n ) -> dsd_node { + transform( n.fanin.begin(), n.fanin.end(), n.fanin.begin(), [&]( signal s ) -> signal { + if ( s.index >= count_old ) + return { s.inv, s.index + count_curr - count_old }; + else + return { s.inv, mapped_support[s.index] }; + } ); + return { n.type, n.index, n.fanin }; + } ); + rule.insert( rule.end(), new_rule.begin(), new_rule.end() ); + } + + /*! \brief Compute DSD decomposition for a boolean function recursively. + * + * \param tt dynamic truth table representing the function. + * \param mapped_support vector indicating function's support at every recursive step. + * \param rule DSD decomposition of the function. + * Returns index of dsd_node to add to rule. + */ + uint32_t compute_dsd( kitty::dynamic_truth_table& tt, std::vector mapped_support, std::vector& rule ) + { + /* Function has been already found */ + if ( !get_rules( tt ).empty() ) + { + update_found_rule( tt, mapped_support, rule ); + return rule.size() - 1; + } + /* try top decomposition */ + uint32_t i = try_top_dec( tt, tt.num_vars() ); + if ( i < tt.num_vars() ) // it was top decomposable + { + auto res = do_top_dec( tt, i, mapped_support ); + + update_support( mapped_support, i ); + + kitty::dynamic_truth_table tt_shr( tt.num_vars() - 1 ); + min_base_shrink( tt, tt_shr ); + + if ( is_PI( tt_shr, tt_shr.num_vars() ) == invalid_index && is_inv_PI( tt_shr, tt_shr.num_vars() ) == invalid_index ) // check if remainder is PI + { + res.fanin.push_back( { 0, compute_dsd( tt_shr, mapped_support, rule ) } ); + } + else + { + if ( is_PI( tt_shr, tt_shr.num_vars() ) != invalid_index ) + { + res.fanin.push_back( { 0, mapped_support[is_PI( tt_shr, tt_shr.num_vars() )] } ); + } + else + { + res.fanin.push_back( { 1, mapped_support[is_inv_PI( tt_shr, tt_shr.num_vars() )] } ); + } + } + res.index = rule.size(); + rule.push_back( res ); + + return res.index; + } + else /* try bottom decomposition */ + { + auto couple = try_bottom_dec( tt, tt.num_vars() ); + i = std::get<0>( couple ); + uint32_t j = std::get<1>( couple ); + + if ( i < tt.num_vars() ) // it was bottom decomposable + { + auto res = do_bottom_dec( tt, i, j, rule.size(), mapped_support ); + rule.push_back( res ); + + update_support( mapped_support, j ); + + kitty::dynamic_truth_table tt_shr( tt.num_vars() - 1 ); + min_base_shrink( tt, tt_shr ); + + return compute_dsd( tt_shr, mapped_support, rule ); + } + else /* do shannon decomposition */ + { + kitty::dynamic_truth_table co0( tt.num_vars() ); + kitty::dynamic_truth_table co1( tt.num_vars() ); + kitty::dynamic_truth_table co0_shr( tt.num_vars() - 1 ); + kitty::dynamic_truth_table co1_shr( tt.num_vars() - 1 ); + + uint32_t index = find_unate_var( tt ); + + auto res = do_shannon_dec( tt, index, co0, co1, mapped_support ); + + /* check for reconvergence */ + gate_disjoint = true; + + uint32_t inv_var_co1 = is_inv_PI( co1, co1.num_vars() ); + uint32_t var_co1 = is_PI( co1, co1.num_vars() ); + uint32_t inv_var_co0 = is_inv_PI( co0, co0.num_vars() ); + uint32_t var_co0 = is_PI( co0, co0.num_vars() ); + + update_support( mapped_support, index ); + + if ( inv_var_co1 == invalid_index && var_co1 == invalid_index ) // check if co1 is PI + { + min_base_shrink( co1, co1_shr ); + res.fanin.insert( res.fanin.begin(), { 0, compute_dsd( co1_shr, mapped_support, rule ) } ); + } + else + { + if ( inv_var_co1 != invalid_index ) + { + uint32_t map_inv_var_co1 = mapped_support[inv_var_co1]; + res.fanin.insert( res.fanin.begin(), { 1, map_inv_var_co1 } ); + } + else + { + uint32_t map_var_co1 = mapped_support[var_co1]; + res.fanin.insert( res.fanin.begin(), { 0, map_var_co1 } ); + } + } + + if ( inv_var_co0 == invalid_index && var_co0 == invalid_index ) // check if co0 is PI + { + min_base_shrink( co0, co0_shr ); + res.fanin.insert( res.fanin.begin(), { 0, compute_dsd( co0_shr, mapped_support, rule ) } ); + } + else + { + if ( inv_var_co0 != invalid_index ) + { + uint32_t map_inv_var_co0 = mapped_support[inv_var_co0]; + res.fanin.insert( res.fanin.begin(), { 1, map_inv_var_co0 } ); + } + else + { + uint32_t map_var_co0 = mapped_support[var_co0]; + res.fanin.insert( res.fanin.begin(), { 0, map_var_co0 } ); + } + } + + res.index = rule.size(); + rule.push_back( res ); + + return res.index; + } + } + } + + rule get_rules( kitty::dynamic_truth_table const& tt ) + { + auto match = _dsd_map.find( tt ); + if ( match != _dsd_map.end() ) + return match->second; + return {}; + } + + dsd_node* get_father( rule& rule, dsd_node& node ) + { + for ( uint32_t i = 0; i < rule.size(); i++ ) + { + if ( rule[i].type != node_type::pi_ && rule[i].type != node_type::zero_ && ( rule[i].fanin[0].index == node.index || rule[i].fanin[1].index == node.index ) ) + return &rule[i]; + } + return nullptr; + } + + dsd_node* find_node( rule& r, uint32_t i ) + { + for ( uint32_t j = 0; j < r.size(); j++ ) + { + if ( r[j].index == i ) + return &r[j]; + } + return nullptr; + } + + /*! \brief Convert rule derived from DSD decomposition into aig format. + * + * \param r rule to convert. + * Returns rule converted into aig format. + */ + rule map_to_aig( rule& r ) + { + std::vector rule( r ); + std::vector aig_rule; + + std::transform( rule.begin(), rule.end(), rule.begin(), []( dsd_node n ) -> dsd_node { + for ( auto& s : n.fanin ) + { + s.index += 1; + } + return { n.type, n.index + 1, n.fanin }; + } ); + + rule.insert( rule.begin(), { node_type::zero_, 0, {} } ); + + for ( typename std::vector::reverse_iterator i = rule.rbegin(); i != rule.rend(); ++i ) + { + dsd_node n = *i; + dsd_node new_node; + + if ( n.type == node_type::and_ || n.type == node_type::pi_ || n.type == node_type::zero_ ) + { + new_node = n; + } + else if ( n.type == node_type::or_ ) + { + new_node = { node_type::and_, n.index, { { ~n.fanin[0].inv, n.fanin[0].index }, { ~n.fanin[1].inv, n.fanin[1].index } } }; + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() ), { { 1, 0 }, { 1, n.index } } }; + aig_rule.insert( aig_rule.begin(), new_root ); + } + } + else if ( n.type == node_type::mux_ ) + { + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + dsd_node node_or = { node_type::and_, n.index + 2, { { 1, n.index }, { 1, n.index + 1 } } }; + dsd_node node_and1 = { node_type::and_, n.index + 1, { { 0, n.fanin[2].index }, { n.fanin[1].inv, n.fanin[1].index } } }; + new_node = { node_type::and_, n.index, { { 1, n.fanin[2].index }, { n.fanin[0].inv, n.fanin[0].index } } }; // and0_node + + // node already in aig_rule must have index and fanin index update (index += 2, fanin_index -> (>= n.index -> +2; nothing)) + for ( auto& elem : aig_rule ) + { + elem.index += 2; + for ( auto& s : elem.fanin ) + { + if ( s.index >= n.index ) + s.index += 2; + } + } + + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() + 2 ), { { 1, 0 }, { 1, static_cast( rule.size() ) + 1 } } }; + dsd_node node_or = { node_type::and_, static_cast( rule.size() + 1 ), { { 1, static_cast( rule.size() - 1 ) }, { 1, static_cast( rule.size() ) } } }; + dsd_node node_and1 = { node_type::and_, static_cast( rule.size() ), { { 0, n.fanin[2].index }, { n.fanin[1].inv, n.fanin[1].index } } }; + new_node = { node_type::and_, static_cast( rule.size() - 1 ), { { 1, n.fanin[2].index }, { n.fanin[0].inv, n.fanin[0].index } } }; // and0_node + + aig_rule.insert( aig_rule.begin(), new_root ); + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + } + else if ( n.type == node_type::xor_ ) + { + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + dsd_node node_or = { node_type::and_, n.index + 2, { { 1, n.index }, { 1, n.index + 1 } } }; + dsd_node node_and1 = { node_type::and_, n.index + 1, { { 0, n.fanin[0].index }, { 1, n.fanin[1].index } } }; + new_node = { node_type::and_, n.index, { { 1, n.fanin[0].index }, { 0, n.fanin[1].index } } }; // and0_node + + // node already in aig_rule must have index and fanin index update (index += 2, fanin_index -> (>= n.index -> +2; nothing)) + for ( auto& elem : aig_rule ) + { + elem.index += 2; + for ( auto& s : elem.fanin ) + { + if ( s.index >= n.index ) + s.index += 2; + } + } + + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() + 2 ), { { 1, 0 }, { 1, static_cast( rule.size() + 1 ) } } }; + dsd_node node_or = { node_type::and_, static_cast( rule.size() + 1 ), { { 1, static_cast( rule.size() - 1 ) }, { 1, static_cast( rule.size() ) } } }; + dsd_node node_and1 = { node_type::and_, static_cast( rule.size() ), { { 0, n.fanin[0].index }, { 1, n.fanin[1].index } } }; + new_node = { node_type::and_, static_cast( rule.size() - 1 ), { { 1, n.fanin[0].index }, { 0, n.fanin[1].index } } }; // and0_node + + aig_rule.insert( aig_rule.begin(), new_root ); + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + } + aig_rule.insert( aig_rule.begin(), new_node ); + } + return aig_rule; + } + + void swap( rule& rule, dsd_node* node_i, dsd_node* node_j ) + { + auto i = node_i->index; + auto j = node_j->index; + node_i->index = j; + node_j->index = i; + std::swap( rule[i], rule[j] ); + } + + /* makes left or right move to derive a new rule */ + void make_move( rule& rule, dsd_node* target, dsd_node* r, uint8_t left ) + { + auto targ_index = 0 + left; + auto r_index = 1 - targ_index; + auto temp_index = target->fanin[targ_index].index; + auto temp_inv = target->fanin[targ_index].inv; + // swap position internal to rule of the two elements + swap( rule, target, r ); + auto temp = target; + target = r; + r = temp; + + // adjust children + target->fanin[targ_index].index = r->index; + target->fanin[targ_index].inv = 0; + r->fanin[r_index].index = temp_index; + r->fanin[r_index].inv = temp_inv; + } + + /* checks whether a rule with the same left depth or right depth has already been encountered */ + bool check_depths( rule rule, dsd_node root, std::vector> depth_branches, uint32_t left ) // for left = 1 check if left move is possible + { + auto left_node = rule[root.fanin[0].index]; + auto right_node = rule[root.fanin[1].index]; + auto left_depth = get_depth( rule, left_node ); + auto right_depth = get_depth( rule, right_node ); + auto left_it1 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ left_depth - 1, right_depth + 1 } ); + auto left_it2 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ right_depth + 1, left_depth - 1 } ); + auto right_it1 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ left_depth + 1, right_depth - 1 } ); + auto right_it2 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ right_depth - 1, left_depth + 1 } ); + if ( left ) + return left_it1 == depth_branches.end() && left_it2 == depth_branches.end(); + else + return right_it1 == depth_branches.end() && right_it2 == depth_branches.end(); + } + + /*! \brief Recursively create new rules from the given one. + * For every dsd node of the original rule, left and right moves are tried. + * Then, the same algorithm is applied to all derived rules. + * The algorithm stops if no new acceptable rules can be found. + * A new rule is acceptable if no other rule with the same left and right depths has been found. + * \param new_rules vector of derived rules. + * \param rule original rule. + * \param start_node node of rule on which we try the right and left moves. + * \param depth_branches vector of encountered left and right depths. + * \param can_left specifies if we can perform a left move. + * \param can_right specifies if we can perform a right move. + */ + void create_rules_from_dsd( std::vector& new_rules, rule rule, dsd_node start_node, std::vector>& depth_branches, bool can_left, bool can_right ) + { + if ( start_node.type == node_type::pi_ || start_node.type == node_type::zero_ ) // if you cannot produce new rules or you are a PI return + return; + + std::vector left_rule( rule ); + std::vector right_rule( rule ); + std::vector> next_depths = {}; + auto left_node = &left_rule[start_node.fanin[0].index]; + auto right_node = &right_rule[start_node.fanin[1].index]; + bool new_left = false; + bool new_right = false; + + std::tuple depths = { get_depth( rule, *left_node ), get_depth( rule, *right_node ) }; + depth_branches.push_back( depths ); + + /* left move */ + if ( can_left && left_node->type == start_node.type && start_node.fanin[0].inv == 0 && check_depths( rule, start_node, depth_branches, 1 ) ) + { + auto r = &left_rule[start_node.index]; + + make_move( left_rule, left_node, r, 1 ); + + new_rules.push_back( left_rule ); + new_left = true; + depth_branches.push_back( { get_depth( left_rule, left_rule[left_rule[left_node->index].fanin[0].index] ), get_depth( left_rule, left_rule[left_rule[left_node->index].fanin[1].index] ) } ); + } + /* right move */ + if ( can_right && right_node->type == start_node.type && start_node.fanin[1].inv == 0 && check_depths( rule, start_node, depth_branches, 0 ) ) + { + auto r = &right_rule[start_node.index]; + + make_move( right_rule, right_node, r, 0 ); + + new_rules.push_back( right_rule ); + new_right = true; + depth_branches.push_back( { get_depth( right_rule, right_rule[right_rule[right_node->index].fanin[0].index] ), get_depth( right_rule, right_rule[right_rule[right_node->index].fanin[1].index] ) } ); + } + + /* initial rule, start_node left children */ + create_rules_from_dsd( new_rules, rule, rule[start_node.fanin[0].index], next_depths, true, true ); + /* initial rule, start_node right children */ + create_rules_from_dsd( new_rules, rule, rule[start_node.fanin[1].index], next_depths, true, true ); + /* left rule, start_node new root */ + if ( new_left ) + { + create_rules_from_dsd( new_rules, left_rule, left_rule[start_node.index], depth_branches, true, false ); + } + /* right rule, start_node new root */ + if ( new_right ) + { + create_rules_from_dsd( new_rules, right_rule, right_rule[start_node.index], depth_branches, false, true ); + } + } + + uint32_t compute_canonized_polarity( uint32_t polarity, uint32_t left_pi, uint32_t right_pi, uint32_t obs_pi ) + { + uint32_t mask_l = 0; + uint32_t mask_r = 0; + uint32_t mask_obs = 0; + for ( uint32_t i = 0; i < obs_pi; i++ ) + { + mask_obs |= ( 1 << i ); + } + for ( uint32_t i = obs_pi; i < obs_pi + left_pi; i++ ) + { + mask_l |= ( 1 << i ); + } + for ( uint32_t i = obs_pi + left_pi; i < obs_pi + left_pi + right_pi; i++ ) + { + mask_r |= ( 1 << i ); + } + return ( ( polarity & mask_l ) << right_pi ) | ( ( polarity & mask_r ) >> left_pi ) | ( polarity & mask_obs ); + } + + void compute_canonized_permutation( std::vector& perm, uint32_t left_pi, uint32_t right_pi, uint32_t obs_pi ) + { + std::vector copy( perm ); + for ( uint32_t i = obs_pi; i < obs_pi + right_pi; i++ ) + { + perm[i] = copy[( i + left_pi )]; + } + for ( uint32_t i = right_pi + obs_pi; i < perm.size(); i++ ) + { + perm[i] = copy[( i - right_pi )]; + } + } + + /*! \brief Recursively assigns indexes to a rule and its subrules and builds and_table. + * It also computes negations and permutations for the gate whose rule is being passed as parameter. + * + * \param r rule to index. + * \param n dsd node to start from. + * \param max max index assigned. + * \param polarity polarity of gate. + * \param perm permutation of gate. + * \param shift specifies the number of PIs encountered. + * Returns label to be assigned to gate whose rule is r. + */ + label do_indexing_rule( rule r, dsd_node n, uint32_t& max, uint32_t& polarity, std::vector& perm, uint32_t& shift ) + { + if ( n.type == node_type::pi_ ) + { + perm[shift] = n.index - 1; + return { 0, 1 }; + } + if ( n.type == node_type::zero_ ) + return { 0, 0 }; + + uint32_t obs_pi = shift; + + /* do indexing on the left */ + uint32_t left_index = do_indexing_rule( r, r[n.fanin[0].index], max, polarity, perm, shift ).index; + if ( r[n.fanin[0].index].type == node_type::pi_ ) + { + polarity |= ( n.fanin[0].inv << shift ); + shift++; + } + /* encountered PIs on the left */ + uint32_t left_pi = shift - obs_pi; + + /* do indexing on the right */ + uint32_t right_index = do_indexing_rule( r, r[n.fanin[1].index], max, polarity, perm, shift ).index; + if ( r[n.fanin[1].index].type == node_type::pi_ ) + { + polarity |= ( n.fanin[1].inv << shift ); + shift++; + } + /* encountered PIs on the right */ + uint32_t right_pi = shift - obs_pi - left_pi; + + /* check if it is inverted gate */ + if ( n.fanin[0].index == 0 && n.fanin[1].inv && n.index == r.size() - 1 ) + { + return { 1, right_index }; + } + signal left, right; + + /* ignore invertion of PIs */ + if ( r[n.fanin[0].index].type == node_type::pi_ ) + left.inv = 0; + else + left.inv = (uint64_t)n.fanin[0].inv; + left.index = left_index; + if ( r[n.fanin[1].index].type == node_type::pi_ ) + right.inv = 0; + else + right.inv = (uint64_t)n.fanin[1].inv; + right.index = right_index; + + std::tuple t; + + /* canonize and_table on left index being smaller than right one */ + if ( left.index <= right.index ) + t = std::make_tuple( left, right ); + else + { + /* new polarity */ + polarity = compute_canonized_polarity( polarity, left_pi, right_pi, obs_pi ); + + /* new permutation */ + compute_canonized_permutation( perm, left_pi, right_pi, obs_pi ); + + t = std::make_tuple( right, left ); + } + auto match = _and_table.find( t ); + if ( match != _and_table.end() ) + return { 0, match->second }; + max++; + /* insert new value in and_table */ + _and_table.insert( { t, max } ); + return { 0, max }; + } + + template + dsd_node is_top_dec( const TT& tt, uint32_t var_index, bool allow_xor = false, TT* func = nullptr ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + auto var = tt.construct(); + kitty::create_nth_var( var, var_index ); + + if ( kitty::implies( tt, var ) ) + { + if ( func ) + { + *func = kitty::cofactor1( tt, var_index ); + } + dsd_node res = { node_type::and_, var_index, {} }; + res.fanin.push_back( { 0, UINT32_MAX } ); + return res; + } + else if ( kitty::implies( var, tt ) ) + { + if ( func ) + { + *func = kitty::cofactor0( tt, var_index ); + } + dsd_node res = { node_type::or_, var_index, {} }; + res.fanin.push_back( { 0, UINT32_MAX } ); + return res; + } + else if ( kitty::implies( tt, ~var ) ) + { + if ( func ) + { + *func = kitty::cofactor0( tt, var_index ); + } + dsd_node res = { node_type::and_, var_index, {} }; + res.fanin.push_back( { 1, UINT32_MAX } ); + return res; + } + else if ( kitty::implies( ~var, tt ) ) + { + if ( func ) + { + *func = kitty::cofactor1( tt, var_index ); + } + dsd_node res = { node_type::or_, var_index, {} }; + res.fanin.push_back( { 1, UINT32_MAX } ); + return res; + } + + if ( allow_xor ) + { + /* try XOR */ + const auto co0 = kitty::cofactor0( tt, var_index ); + const auto co1 = kitty::cofactor1( tt, var_index ); + + if ( kitty::equal( co0, ~co1 ) ) + { + if ( func ) + { + *func = co0; + } + dsd_node res = { node_type::xor_, var_index, {} }; + res.fanin.push_back( { 0, UINT32_MAX } ); + return res; + } + } + + return { node_type::none, var_index, {} }; + } + + template + dsd_node is_bottom_dec( const TT& tt, uint32_t var_index1, uint32_t var_index2, TT* func = nullptr, uint32_t new_index = invalid_index, bool allow_xor = false ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = kitty::cofactor0( tt, var_index1 ); + const auto tt1 = kitty::cofactor1( tt, var_index1 ); + + const auto tt00 = kitty::cofactor0( tt0, var_index2 ); + const auto tt01 = kitty::cofactor1( tt0, var_index2 ); + const auto tt10 = kitty::cofactor0( tt1, var_index2 ); + const auto tt11 = kitty::cofactor1( tt1, var_index2 ); + + const auto eq01 = kitty::equal( tt00, tt01 ); + const auto eq02 = kitty::equal( tt00, tt10 ); + const auto eq03 = kitty::equal( tt00, tt11 ); + const auto eq12 = kitty::equal( tt01, tt10 ); + const auto eq13 = kitty::equal( tt01, tt11 ); + const auto eq23 = kitty::equal( tt10, tt11 ); + + const auto num_pairs = + static_cast( eq01 ) + + static_cast( eq02 ) + + static_cast( eq03 ) + + static_cast( eq12 ) + + static_cast( eq13 ) + + static_cast( eq23 ); + + if ( num_pairs != 2u && num_pairs != 3 ) + { + return { node_type::none, invalid_index, {} }; + } + + if ( !eq01 && !eq02 && !eq03 ) // 00 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt11, tt00 ); + } + dsd_node res = { node_type::or_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq01 && !eq12 && !eq13 ) // 01 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt10 ); + } + dsd_node res = { node_type::and_, new_index, {} }; + res.fanin.push_back( { 1, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq02 && !eq12 && !eq23 ) // 10 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt10 ); + } + dsd_node res = { node_type::or_, new_index, {} }; + res.fanin.push_back( { 1, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq03 && !eq13 && !eq23 ) // 11 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt11, tt00 ); + } + dsd_node res = { node_type::and_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( allow_xor ) // XOR + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt00 ); + } + dsd_node res = { node_type::xor_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + + return { node_type::none, invalid_index, {} }; + } + + template + uint32_t find_unate_var( const TT tt ) + { + for ( uint32_t index = 0; index < tt.num_vars() - 2; ++index ) + { + const auto tt0 = kitty::cofactor0( tt, index ); + const auto tt1 = kitty::cofactor1( tt, index ); + if ( ( ( tt0 & tt1 ) == tt0 ) && ( ( tt0 & tt1 ) == tt1 ) ) + return index; + } + + return tt.num_vars() - 1; + } + + template + dsd_node shannon_dec( const TT& tt, uint32_t index, TT* func0 = nullptr, TT* func1 = nullptr ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = kitty::cofactor0( tt, index ); + const auto tt1 = kitty::cofactor1( tt, index ); + + dsd_node res = { node_type::mux_, index, {} }; + res.fanin.push_back( { 0, index } ); + + if ( func0 && func1 ) + { + *func0 = tt0; + *func1 = tt1; + } + + return res; + } + + /*! \brief Get depth of rule starting from a specific dsd_node. + * + * \param rule rule + * \param n dsd_node to start from + * Returns depth of rule starting from n. + */ + uint32_t get_depth( rule rule, dsd_node n ) + { + if ( n.type == node_type::pi_ || n.type == node_type::zero_ ) + { + return 0; + } + uint32_t max_depth; + uint32_t left_depth = get_depth( rule, rule[n.fanin[0].index] ); + uint32_t right_depth = get_depth( rule, rule[n.fanin[1].index] ); + max_depth = ( left_depth > right_depth ) ? left_depth : right_depth; + return max_depth + 1; + } + +#pragma region Report + std::string to_string( node_type t ) + { + if ( t == node_type::and_ ) + return "*"; + if ( t == node_type::or_ ) + return "+"; + if ( t == node_type::mux_ ) + return "+"; + if ( t == node_type::xor_ ) + return "xor"; + if ( t == node_type::pi_ ) + return "pi"; + if ( t == node_type::none ) + return "none"; + if ( t == node_type::zero_ ) + return "zero"; + } + + void print_dsd_node( dsd_node& n ) + { + std::cout << n.index << " " << to_string( n.type ) << " "; + for ( auto elem : n.fanin ) + std::cout << "{" << elem.index << ", " << elem.inv << "}"; + std::cout << "\n"; + } + + void print_rule( rule& r ) + { + for ( auto elem : r ) + print_dsd_node( elem ); + } + + /*! \brief Print expression of a rule. + * + * \param rule rule. + * \param n dsd_node to start from. + */ + void print_rule( rule rule, dsd_node n ) + { + if ( n.type == node_type::pi_ ) + { + std::cout << char( 'a' + n.index ); + return; + } + if ( n.type == node_type::zero_ ) + { + std::cout << "0"; + return; + } + else + { + std::cout << "("; + if ( n.fanin[0].inv ) + { + std::cout << "!"; + } + if ( n.type == node_type::mux_ ) + { + std::cout << "!" << char( 'a' + n.fanin[2].index ) << " * "; + } + print_rule( rule, rule[n.fanin[0].index] ); + std::cout << " " << to_string( n.type ) << " "; + if ( n.type == node_type::mux_ ) + { + std::cout << char( 'a' + n.fanin[2].index ) << " * "; + } + if ( n.fanin[1].inv ) + { + std::cout << "!"; + } + print_rule( rule, rule[n.fanin[1].index] ); + std::cout << ")"; + } + } +#pragma endregion + +private: + bool gate_disjoint{ false }; /* flag for gate support*/ + uint32_t num_large_gates{ 0 }; + + std::vector const& _gates; /* collection of gates */ + struct_library_params const _ps; + + composed_list_t _supergates; /* list of composed_gates */ + lib_rule _dsd_map; /* hash map for DSD decomposition of gates */ + lib_table _and_table; /* AND table */ + map_label_gate _label_to_gate; /* map label to gate */ +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/super_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/super_utils.hpp new file mode 100644 index 00000000000..c7a74b1c144 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/super_utils.hpp @@ -0,0 +1,466 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file super_utils.hpp + \brief Implements utilities to create supergates for technology mapping + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../io/genlib_reader.hpp" +#include "../io/super_reader.hpp" +#include "include/supergate.hpp" + +namespace mockturtle +{ + +struct super_utils_params +{ + /*! \brief load multi-output gates in simple supergates */ + bool load_multioutput_in_single{ false }; + + /*! \brief reports loaded supergates */ + bool verbose{ false }; +}; + +/*! \brief Utilities to generate supergates + * + * This class creates supergates starting from supergates + * specifications contained in `supergates_spec` extracted + * from a SUPER file. + * + * Multi-output gates are also extracted from the list of + * GENLIB gates. However multi-output gates are currently not + * supported as supergates members. + * + * This utility is called by `tech_library` to construct + * the library for technology mapping. + */ +template +class super_utils +{ +private: + static constexpr uint32_t truth_table_size = 6; + +public: + explicit super_utils( std::vector const& gates, super_lib const& supergates_spec = {}, super_utils_params const ps = {} ) + : _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _supergates(), + _multioutput_gates() + { + if ( _supergates_spec.supergates.size() == 0 ) + { + generate_library_with_genlib(); + } + else + { + generate_library_with_super(); + } + } + + /*! \brief Get the all the supergates. + * + * Returns a list of supergates created accordingly to + * the standard library and the supergates specifications. + */ + const std::deque>& get_super_library() const + { + return _supergates; + } + + /*! \brief Get the number of standard gates. + * + * Returns the number of standard gates contained in the + * supergate library. + */ + const uint32_t get_standard_library_size() const + { + return simple_gates_size; + } + + /*! \brief Get multi-output gates. + * + * Returns a list of multioutput gates. + */ + const std::vector>>& get_multioutput_library() const + { + return _multioutput_gates; + } + +public: + void generate_library_with_genlib() + { + uint32_t initial_size = _supergates.size(); + + std::unordered_map multioutput_map; + std::unordered_map multioutput_idx; + multioutput_map.reserve( _gates.size() ); + + /* look for multi-output gates (gates with the same name) */ + uint32_t multioutput_i = 0; + for ( const auto& g : _gates ) + { + if ( multioutput_map.find( g.name ) != multioutput_map.end() ) + { + /* assign an index */ + if ( multioutput_map[g.name] == 1 ) + multioutput_idx[g.name] = multioutput_i++; + + multioutput_map[g.name] += 1; + } + else + { + multioutput_map[g.name] = 1; + multioutput_idx[g.name] = UINT32_MAX; + } + } + + /* create composed gates */ + uint32_t ignored = 0; + uint32_t ignored_id = 0; + uint32_t large_gates = 0; + for ( const auto& g : _gates ) + { + std::array pin_to_pin_delays{}; + + if ( g.function.num_vars() > NInputs ) + { + ++ignored; + ignored_id = g.id; + continue; + } + if ( g.function.num_vars() > truth_table_size ) + { + ++large_gates; + continue; + } + + auto i = 0u; + for ( auto const& pin : g.pins ) + { + /* use worst pin delay */ + pin_to_pin_delays[i++] = std::max( pin.rise_block_delay, pin.fall_block_delay ); + } + + if ( multioutput_map[g.name] == 1 || _ps.load_multioutput_in_single ) + { + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + + if ( multioutput_map[g.name] > 1 ) + { + uint32_t idx = multioutput_idx[g.name]; + if ( _multioutput_gates.size() <= idx ) + _multioutput_gates.emplace_back( std::vector>() ); + + _multioutput_gates[multioutput_idx[g.name]].emplace_back( + composed_gate{ static_cast( idx ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + } + + simple_gates_size = _supergates.size() - initial_size; + + if ( _ps.verbose ) + { + std::cout << fmt::format( "[i] Loading {} simple library cells\n", simple_gates_size + large_gates ); + std::cout << fmt::format( "[i] Loading {} multi-output library cells\n", _multioutput_gates.size() ); + } + + if ( ignored > 0 ) + { + std::cerr << fmt::format( "[i] WARNING: {} gates IGNORED (e.g., {}), too many inputs for the library settings\n", ignored, _gates[ignored_id].name ); + } + } + + void generate_library_with_super() + { + if ( _supergates_spec.max_num_vars > NInputs || _supergates_spec.max_num_vars > truth_table_size ) + { + std::cerr << fmt::format( + "[e] ERROR: NInputs ({}) should be greater or equal than the max number of variables ({}) in the super file.\n", NInputs, _supergates_spec.max_num_vars ); + std::cerr << "[i] WARNING: ignoring supergates, proceeding with standard library." << std::endl; + generate_library_with_genlib(); + return; + } + + /* create a map for the gates IDs */ + std::unordered_map gates_map; + + for ( auto const& g : _gates ) + { + if ( gates_map.find( g.name ) != gates_map.end() ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring genlib gate {}, duplicated name entry in supergates.", g.name ) << std::endl; + } + else + { + gates_map[g.name] = g.id; + } + } + + /* creating input variables */ + for ( uint8_t i = 0; i < _supergates_spec.max_num_vars; ++i ) + { + kitty::dynamic_truth_table tt{ NInputs }; + kitty::create_nth_var( tt, i ); + + _supergates.emplace_back( composed_gate{ static_cast( i ), + false, + nullptr, + 0, + tt, + 0.0, + {}, + {} } ); + } + + generate_library_with_genlib(); + + uint32_t super_count = 0; + + /* add supergates */ + for ( auto const& g : _supergates_spec.supergates ) + { + uint32_t root_match_id; + if ( auto it = gates_map.find( g.name ); it != gates_map.end() ) + { + root_match_id = it->second; + } + else + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, no reference in genlib.", g.id ) << std::endl; + continue; + } + + uint32_t num_vars = _gates[root_match_id].num_vars; + + if ( num_vars != g.fanin_id.size() ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, wrong number of fanins.", g.id ) << std::endl; + continue; + } + if ( num_vars > _supergates_spec.max_num_vars ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, too many variables for the library settings.", g.id ) << std::endl; + continue; + } + + std::vector*> sub_gates; + + bool error = false; + bool simple_gate = true; + for ( uint32_t f : g.fanin_id ) + { + if ( f >= g.id + _supergates_spec.max_num_vars ) + { + error = true; + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, wrong fanins.", g.id ) << std::endl; + } + if ( f < _supergates_spec.max_num_vars ) + { + sub_gates.emplace_back( &_supergates[f] ); + } + else + { + sub_gates.emplace_back( &_supergates[f + simple_gates_size] ); + simple_gate = false; + } + } + + if ( error ) + { + continue; + } + + /* force at `is_super = false` simple gates considered as supergates. + * This is necessary to not have duplicates since tech_library + * computes indipendently the permutations for simple gates. + * Moreover simple gates permutations could be incomplete in SUPER + * libraries which are constrained by the number of gates. */ + bool is_super_verified = g.is_super; + if ( simple_gate ) + { + is_super_verified = false; + } + + float area = compute_area( root_match_id, sub_gates ); + const kitty::dynamic_truth_table tt = compute_truth_table( root_match_id, sub_gates ); + + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + is_super_verified, + &_gates[root_match_id], + 0, + tt, + area, + {}, + sub_gates } ); + + if ( g.is_super ) + { + ++super_count; + } + + auto& s = _supergates[_supergates.size() - 1]; + s.num_vars = compute_support( s ); + compute_delay_parameters( s ); + } + + /* minimize supergates */ + for ( auto& g : _supergates ) + { + if ( g.is_super ) + { + g.function = shrink_to( g.function, static_cast( g.num_vars ) ); + } + } + + if ( _ps.verbose ) + { + std::cout << fmt::format( "[i] Loaded {} supergates in the library\n", super_count ); + } + } + +private: + inline float compute_area( uint32_t root_id, std::vector*> const& sub_gates ) + { + float area = _gates[root_id].area; + for ( auto const f : sub_gates ) + { + area += f->area; + } + + return area; + } + + inline uint32_t compute_support( composed_gate& s ) + { + std::array used_pins{}; + uint32_t support = 0; + + return compute_support_rec( s, used_pins ); + } + + uint32_t compute_support_rec( composed_gate& s, std::array& used_pins ) + { + /* termination: input variable */ + if ( s.root == nullptr ) + { + if ( used_pins[s.id]++ == 0u ) + { + return 1; + } + return 0; + } + + uint32_t support = 0; + for ( auto const pin : s.fanin ) + { + support += compute_support_rec( *pin, used_pins ); + } + return support; + } + + inline kitty::dynamic_truth_table compute_truth_table( uint32_t root_id, std::vector*> const& sub_gates ) + { + std::vector ttv; + + for ( auto const f : sub_gates ) + { + ttv.emplace_back( f->function ); + } + + return kitty::compose_truth_table( _gates[root_id].function, ttv ); + } + + inline void compute_delay_parameters( composed_gate& s ) + { + const auto& root = *( s.root ); + + auto i = 0u; + for ( auto const& pin : root.pins ) + { + float worst_delay = std::max( pin.rise_block_delay, pin.fall_block_delay ); + + compute_delay_pin_rec( s, *( s.fanin[i++] ), worst_delay ); + } + } + + void compute_delay_pin_rec( composed_gate& root, composed_gate& s, float delay ) + { + /* termination: input variable */ + if ( s.root == nullptr ) + { + root.tdelay[s.id] = std::max( root.tdelay[s.id], delay ); + return; + } + + auto i = 0u; + for ( auto const& pin : s.root->pins ) + { + float worst_delay = delay + std::max( pin.rise_block_delay, pin.fall_block_delay ); + + compute_delay_pin_rec( root, *( s.fanin[i++] ), worst_delay ); + } + } + +protected: + uint32_t simple_gates_size{ 0 }; + + std::vector const& _gates; + super_lib const& _supergates_spec; + super_utils_params const _ps; + std::deque> _supergates; + std::vector>> _multioutput_gates; +}; /* class super_utils */ + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/utils/tech_library.hpp b/third-party/mockturtle/include/mockturtle/utils/tech_library.hpp new file mode 100644 index 00000000000..16695554ff6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/tech_library.hpp @@ -0,0 +1,1652 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file tech_library.hpp + \brief Implements utilities to enumerates gates for technology mapping + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../io/genlib_reader.hpp" +#include "../io/super_reader.hpp" +#include "include/supergate.hpp" +#include "standard_cell.hpp" +#include "struct_library.hpp" +#include "super_utils.hpp" + +namespace mockturtle +{ + +/* +std::string const mcnc_library = "GATE inv1 1 O=!a; PIN * INV 1 999 0.9 0.3 0.9 0.3\n" + "GATE inv2 2 O=!a; PIN * INV 2 999 1.0 0.1 1.0 0.1\n" + "GATE inv3 3 O=!a; PIN * INV 3 999 1.1 0.09 1.1 0.09\n" + "GATE inv4 4 O=!a; PIN * INV 4 999 1.2 0.07 1.2 0.07\n" + "GATE nand2 2 O=!(a*b); PIN * INV 1 999 1.0 0.2 1.0 0.2\n" + "GATE nand3 3 O=!(a*b*c); PIN * INV 1 999 1.1 0.3 1.1 0.3\n" + "GATE nand4 4 O=!(a*b*c*d); PIN * INV 1 999 1.4 0.4 1.4 0.4\n" + "GATE nor2 2 O=!(a+b); PIN * INV 1 999 1.4 0.5 1.4 0.5\n" + "GATE nor3 3 O=!(a+b+c); PIN * INV 1 999 2.4 0.7 2.4 0.7\n" + "GATE nor4 4 O=!(a+b+c+d); PIN * INV 1 999 3.8 1.0 3.8 1.0\n" + "GATE and2 3 O=a*b; PIN * NONINV 1 999 1.9 0.3 1.9 0.3\n" + "GATE or2 3 O=a+b; PIN * NONINV 1 999 2.4 0.3 2.4 0.3\n" + "GATE xor2a 5 O=a*!b+!a*b; PIN * UNKNOWN 2 999 1.9 0.5 1.9 0.5\n" + "#GATE xor2b 5 O=!(a*b+!a*!b); PIN * UNKNOWN 2 999 1.9 0.5 1.9 0.5\n" + "GATE xnor2a 5 O=a*b+!a*!b; PIN * UNKNOWN 2 999 2.1 0.5 2.1 0.5\n" + "#GATE xnor2b 5 O=!(a*!b+!a*b); PIN * UNKNOWN 2 999 2.1 0.5 2.1 0.5\n" + "GATE aoi21 3 O=!(a*b+c); PIN * INV 1 999 1.6 0.4 1.6 0.4\n" + "GATE aoi22 4 O=!(a*b+c*d); PIN * INV 1 999 2.0 0.4 2.0 0.4\n" + "GATE oai21 3 O=!((a+b)*c); PIN * INV 1 999 1.6 0.4 1.6 0.4\n" + "GATE oai22 4 O=!((a+b)*(c+d)); PIN * INV 1 999 2.0 0.4 2.0 0.4\n" + "GATE buf 2 O=a; PIN * NONINV 1 999 1.0 0.0 1.0 0.0\n" + "GATE zero 0 O=CONST0;\n" + "GATE one 0 O=CONST1;"; +*/ + +enum class classification_type : uint32_t +{ + /*! \brief generate the NP configurations (n! * 2^n) + * Direct matching: best up to ~200 library gates */ + np_configurations = 0, + + /*! \brief generate the P configurations (n!) + * Matching by N-canonization: best for more + * than ~200 library gates */ + p_configurations = 1, + + /*! \brief generate the n configurations (2^n) + * Direct fast matching, less quality */ + n_configurations = 2, +}; + +struct tech_library_params +{ + /*! \brief Load large gates with more than 6 inputs */ + bool load_large_gates{ true }; + + /*! \brief Loads multioutput gates in the library */ + bool load_multioutput_gates{ true }; + + /*! \brief Don't load symmetrical permutations of gate pins (drastically speeds-up mapping) */ + bool ignore_symmetries{ false }; + + /*! \brief Load gates with minimum size only */ + bool load_minimum_size_only{ true }; + + /*! \brief Remove dominated gates (larger sizes) */ + bool remove_dominated_gates{ true }; + + /*! \brief Loads multioutput gates in single-output library */ + bool load_multioutput_gates_single{ false }; + + /*! \brief reports np enumerations */ + bool verbose{ false }; + + /*! \brief reports all the entries in the library */ + bool very_verbose{ false }; +}; + +namespace detail +{ + +template +struct tuple_tt_hash +{ + inline std::size_t operator()( std::array, NumOutputs> const& tts ) const + { + std::size_t seed = kitty::hash_block( tts[0]._bits ); + + for ( auto i = 1; i < NumOutputs; ++i ) + kitty::hash_combine( seed, kitty::hash_block( tts[i]._bits ) ); + + return seed; + } +}; + +} // namespace detail + +/*! \brief Library of gates for Boolean matching + * + * This class creates a technology library from a set + * of input gates. Each NP- or P-configuration of the gates + * are enumerated and inserted in the library. + * + * The configuration is selected using the template + * parameter `Configuration`. P-configuration is suggested + * for big libraries with few symmetric gates. The template + * parameter `NInputs` selects the maximum number of variables + * allowed for a gate in the library. + * + * The library can be generated also using supergates definitions. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + // standard library + mockturtle::tech_library lib( gates ); + + super_lib supergates_spec; + lorina::read_super( "file.super", super_reader( supergates_spec ) ); + // library with supergates + mockturtle::tech_library lib_super( gates, supergates_spec ); + \endverbatim + */ +template +class tech_library +{ +private: + static constexpr float epsilon = 0.0005; + static constexpr uint32_t max_multi_outputs = 2; + static constexpr uint32_t truth_table_size = 6; + using supergates_list_t = std::vector>; + using TT = kitty::static_truth_table; + using tt_hash = kitty::hash; + using multi_tt_hash = detail::tuple_tt_hash; + using index_t = phmap::flat_hash_map; + using lib_t = phmap::flat_hash_map; + using multi_relation_t = std::array; + using multi_supergates_list_t = std::array>, max_multi_outputs>; + using multi_lib_t = phmap::flat_hash_map; + using multi_func_t = phmap::flat_hash_map; + using struct_lib_t = phmap::flat_hash_map; + +public: + explicit tech_library( std::vector const& gates, tech_library_params const ps = {}, super_lib const& supergates_spec = {} ) + : _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _cells( get_standard_cells( _gates ) ), + _super( _gates, _supergates_spec, super_utils_params{ ps.load_multioutput_gates_single, ps.verbose } ), + _use_supergates( false ), + _struct( _gates, struct_library_params{ ps.load_minimum_size_only, ps.very_verbose } ), + _super_lib(), + _multi_lib(), + _struct_lib() + { + static_assert( NInputs < 16, "The technology library database supports NInputs up to 15\n" ); + + generate_library(); + + if ( ps.load_multioutput_gates ) + generate_multioutput_library(); + + if ( ps.load_large_gates ) + { + _struct.construct( 2 ); + } + } + + explicit tech_library( std::vector const& gates, super_lib const& supergates_spec, tech_library_params const ps = {} ) + : _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _cells( get_standard_cells( _gates ) ), + _super( _gates, _supergates_spec, super_utils_params{ ps.load_multioutput_gates_single, ps.verbose } ), + _use_supergates( true ), + _struct( _gates, struct_library_params{ ps.load_minimum_size_only, ps.very_verbose } ), + _super_lib(), + _multi_lib(), + _struct_lib() + { + static_assert( NInputs < 16, "The technology library database supports NInputs up to 15\n" ); + + generate_library(); + + if ( ps.load_multioutput_gates ) + generate_multioutput_library(); + + if ( ps.load_large_gates ) + { + _struct.construct( 2 ); + } + } + + tech_library ( const tech_library& ) = delete; + tech_library& operator=( const tech_library& ) = delete; + + /*! \brief Get the gates matching the function. + * + * Returns a list of gates that match the function represented + * by the truth table. + */ + const supergates_list_t* get_supergates( TT const& tt ) const + { + auto match = _super_lib.find( tt ); + if ( match != _super_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the multi-output gates matching the function. + * + * Returns a list of multi-output gates that match the function + * represented by the truth table. + */ + const multi_supergates_list_t* get_multi_supergates( std::array const& tts ) const + { + auto match = _multi_lib.find( tts ); + if ( match != _multi_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the multi-output gate function ID for a single output. + * + * Returns the function ID of a multi-output gate output if matched. This function + * supports up to 6 inputs. Returns zero in case of no match. + */ + uint64_t get_multi_function_id( uint64_t const& tt ) const + { + auto match = _multi_funcs.find( tt ); + if ( match != _multi_funcs.end() ) + return match->second; + return 0; + } + + /*! \brief Get the pattern ID for structural matching. + * + * Returns a pattern ID if found, UINT32_MAX otherwise given the + * children IDs. This function works with only AND operators. + */ + uint32_t get_pattern_id( uint32_t id1, uint32_t id2 ) const + { + return _struct.get_pattern_id( id1, id2 ); + } + + /*! \brief Get the gates matching the pattern ID and phase. + * + * Returns a list of gates that match the pattern ID and the given polarity. + */ + const supergates_list_t* get_supergates_pattern( uint32_t id, bool phase ) const + { + return _struct.get_supergates_pattern( id, phase ); + } + + /*! \brief Get inverter information. + * + * Returns area, delay, and ID of the smallest inverter. + */ + const std::tuple get_inverter_info() const + { + return std::make_tuple( _inv_area, _inv_delay, _inv_id ); + } + + /*! \brief Get buffer information. + * + * Returns area, delay, and ID of the smallest buffer. + */ + const std::tuple get_buffer_info() const + { + return std::make_tuple( _buf_area, _buf_delay, _buf_id ); + } + + /*! \brief Returns the maximum number of variables of the gates. */ + unsigned max_gate_size() + { + return _max_size; + } + + /*! \brief Returns the original gates. */ + const std::vector get_gates() const + { + return _gates; + } + + /*! \brief Returns the standard cells. */ + const std::vector& get_cells() const + { + return _cells; + } + + /*! \brief Returns multioutput gates. */ + const std::vector>>& get_multioutput_gates() const + { + return _super.get_multioutput_library(); + } + + /*! \brief Returns the number of multi-output gates loaded in the library. */ + const uint32_t num_multioutput_gates() const + { + if ( !_ps.load_multioutput_gates ) + return 0; + return _multi_lib.size(); + } + + /*! \brief Returns the number of gates for structural matching. */ + const uint32_t num_structural_gates() const + { + return _struct.get_struct_library().size(); + } + + /*! \brief Returns the number of gates for structural matching with more than 6 inputs. */ + const uint32_t num_structural_large_gates() const + { + return _struct.get_struct_library().size(); + } + +private: + void generate_library() + { + bool inv = false; + bool buf = false; + + /* extract the smallest inverter and buffer info */ + for ( auto& gate : _gates ) + { + if ( gate.function.num_vars() == 1 ) + { + /* extract inverter delay and area */ + if ( kitty::is_const0( kitty::cofactor1( gate.function, 0 ) ) ) + { + /* get the smallest area inverter */ + if ( !inv || gate.area < _inv_area - epsilon ) + { + _inv_area = gate.area; + _inv_delay = compute_worst_delay( gate ); + _inv_id = gate.id; + inv = true; + } + } + else + { + /* get the smallest area buffer */ + if ( !buf || gate.area < _buf_area - epsilon ) + { + _buf_area = gate.area; + _buf_delay = compute_worst_delay( gate ); + _buf_id = gate.id; + buf = true; + } + } + } + } + + auto const& supergates = _super.get_super_library(); + uint32_t const standard_gate_size = _super.get_standard_library_size(); + + std::vector skip_gates( supergates.size(), false ); + + if ( _ps.load_minimum_size_only || _ps.remove_dominated_gates ) + { + filter_gates( supergates, skip_gates ); + } + + /* generate the configurations for the standard gates */ + uint32_t i = 0u; + uint32_t skip_count = 0; + for ( auto const& gate : supergates ) + { + uint32_t np_count = 0; + + if ( skip_gates[skip_count++] ) + { + /* exclude gate */ + ++i; + continue; + } + + if ( gate.root == nullptr ) + { + /* exclude PIs */ + continue; + } + + _max_size = std::max( _max_size, gate.num_vars ); + + if ( i++ < standard_gate_size ) + { + const auto on_np = [&]( auto const& tt, auto neg, auto const& perm ) { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + 0 }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + sg.polarity |= ( ( neg >> perm[i] ) & 1 ) << i; /* permutate input negation to match the right pin */ + } + + const auto static_tt = kitty::extend_to( tt ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && ( _ps.ignore_symmetries || sg.tdelay == it->tdelay ) ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + }; + + const auto on_p = [&]( auto const& tt, auto const& perm ) { + /* get all the configurations that lead to the N-class representative */ + auto [tt_canon, phases] = kitty::exact_n_canonization_complete( tt ); + + for ( auto phase : phases ) + { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( phase ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt_canon ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && ( _ps.ignore_symmetries || sg.tdelay == it->tdelay ) ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + } + }; + + if constexpr ( Configuration == classification_type::np_configurations ) + { + /* NP enumeration of the function */ + const auto tt = gate.function; + kitty::exact_np_enumeration( tt, on_np ); + } + else if ( Configuration == classification_type::n_configurations ) + { + /* N enumeration of the function */ + const auto tt = gate.function; + std::vector pin_order( tt.num_vars() ); + std::iota( pin_order.begin(), pin_order.end(), 0 ); + kitty::exact_n_enumeration( tt, [&]( auto const& tt, auto neg ) { on_np( tt, neg, pin_order ); } ); + } + else + { + /* P enumeration followed by N canonization of the function */ + const auto tt = gate.function; + kitty::exact_p_enumeration( tt, on_p ); + } + } + else + { + /* process the supergates */ + if ( !gate.is_super ) + { + /* ignore simple gates */ + continue; + } + + const auto on_np = [&]( auto const& tt, auto neg ) { + std::vector perm( gate.num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( neg ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && sg.tdelay == it->tdelay ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + }; + + const auto on_p = [&]() { + auto [tt_canon, phases] = kitty::exact_n_canonization_complete( gate.function ); + std::vector perm( gate.num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto phase : phases ) + { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( phase ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt_canon ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && sg.tdelay == it->tdelay ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + } + }; + + if constexpr ( Configuration == classification_type::np_configurations ) + { + /* N enumeration of the function */ + const auto tt = gate.function; + kitty::exact_n_enumeration( tt, on_np ); + } + else + { + /* N canonization of the function */ + const auto tt = gate.function; + on_p(); + } + } + + if ( _ps.very_verbose ) + { + std::cout << "Gate " << gate.root->name << ", num_vars = " << gate.num_vars << ", np entries = " << np_count << std::endl; + } + } + + if ( !inv ) + { + std::cerr << "[i] WARNING: inverter gate has not been detected in the library" << std::endl; + } + + if ( !buf ) + { + std::cerr << "[i] WARNING: buffer gate has not been detected in the library" << std::endl; + } + + if ( _ps.very_verbose ) + { + for ( auto const& entry : _super_lib ) + { + kitty::print_hex( entry.first ); + std::cout << ": "; + for ( auto const& gate : entry.second ) + { + printf( "%d(a:%.2f, p:%d) ", gate.root->id, gate.area, gate.polarity ); + } + std::cout << std::endl; + } + } + } + + /* Supports only NP configurations */ + void generate_multioutput_library() + { + uint32_t np_count = 0; + std::string ignored_name; + bool consistency_check = true; + + /* load multi-output gates */ + auto const& multioutput_gates = _super.get_multioutput_library(); + + uint32_t ignored_gates = 0; + for ( auto const& multi_gate : multioutput_gates ) + { + /* select the on up to max_multi_outputs outputs */ + if ( multi_gate.size() > max_multi_outputs ) + { + ignored_name = multi_gate[0].root->name; + ++ignored_gates; + continue; + } + + std::array order = { 0 }; + + const auto on_np = [&]( auto const& tts, auto neg, auto const& perm ) { + std::vector> multi_sg; + + for ( auto const& gate : multi_gate ) + { + multi_sg.emplace_back( supergate{ &gate, + static_cast( gate.area ), + {}, + perm, + 0 } ); + } + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + uint32_t j = 0; + for ( auto& sg : multi_sg ) + { + sg.tdelay[i] = multi_gate[j++].tdelay[perm[i]]; + sg.polarity |= ( ( neg >> perm[i] ) & 1 ) << i; /* permutate input negation to match the right pin */ + } + } + + std::array static_tts = {}; + std::array sorted_tts = {}; + + /* canonize output */ + for ( auto i = 0; i < tts.size(); ++i ) + { + static_tts[i] = kitty::extend_to( tts[i] ); + if ( ( static_tts[i]._bits & 1 ) == 1 ) + { + static_tts[i] = ~static_tts[i]; + multi_sg[i].polarity |= 1 << NInputs; /* set flipped output polarity*/ + } + } + + std::iota( order.begin(), order.end(), 0 ); + + std::stable_sort( order.begin(), order.end(), [&]( size_t a, size_t b ) { + return static_tts[a] < static_tts[b]; + } ); + + std::transform( order.begin(), order.end(), sorted_tts.begin(), [&]( size_t a ) { + return static_tts[a]; + } ); + + // std::stable_sort( static_tts.begin(), static_tts.end() ); + + auto& v = _multi_lib[sorted_tts]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v[0].begin(), v[0].end(), multi_sg[0], [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated elements due to symmetries */ + while ( it != v[0].end() ) + { + /* if different gate, exit */ + if ( multi_sg[0].root->id != it->root->id ) + break; + + /* if already in the library, exit */ + if ( multi_sg[order[0]].polarity != it->polarity ) + { + ++it; + continue; + } + + bool same_delay = true; + size_t d = std::distance( v[0].begin(), it ); + for ( auto i = 0; i < multi_sg.size(); ++i ) + { + if ( multi_sg[order[i]].tdelay != v[i][d].tdelay ) + { + same_delay = false; + break; + } + } + + /* do not add if equivalent to another in the library */ + if ( same_delay ) + { + to_add = false; + break; + } + + ++it; + } + + if ( to_add ) + { + size_t d = std::distance( v[0].begin(), it ); + for ( auto i = 0; i < multi_sg.size(); ++i ) + { + v[i].insert( v[i].begin() + d, multi_sg[order[i]] ); + } + ++np_count; + } + }; + + /* NP enumeration of the function */ + std::vector tts; + for ( auto gate : multi_gate ) + tts.push_back( gate.function ); + kitty::exact_multi_np_enumeration( tts, on_np ); + + /* NPN enumeration of the single outputs */ + uint32_t pin = 0; + for ( auto const& gate : multi_gate ) + { + consistency_check &= check_delay_consistency( gate, pin++ ); + exact_npn_enumeration( gate.function, [&]( auto const& tt, auto neg, auto const& perm ) { + (void)neg; + (void)perm; + _multi_funcs[tt._bits[0]] = gate.function._bits[0]; + } ); + } + } + + /* update area based on the single output contribution */ + multi_update_area(); + + if ( _ps.verbose && ignored_gates > 0 ) + { + std::cerr << fmt::format( "[i] WARNING: {} multi-output gates IGNORED (e.g., {}), too many outputs for the library settings\n", ignored_gates, ignored_name ); + } + + if ( !consistency_check ) + { + std::cerr << "[i] WARNING: technology mapping using multi-output cells with warnings might generate required time violations or circuits with dangling pins\n"; + } + + // std::cout << _multi_lib.size() << "\n"; + } + + void multi_update_area() + { + /* update area for each sub-function in a multi-output gate with their contribution */ + for ( auto& pair : _multi_lib ) + { + auto& multi_gates = pair.second; + for ( auto i = 0; i < multi_gates[0].size(); ++i ) + { + /* get sum of area and area count */ + double area = 0; + uint32_t contribution_count = 0; + std::array area_contribution = { 0 }; + for ( auto j = 0; j < max_multi_outputs; ++j ) + { + auto& gate = multi_gates[j][i]; + const TT tt = kitty::extend_to( gate.root->function ); + + /* get the area of the smallest match with a simple gate */ + const auto match = get_supergates( tt ); + if ( match == nullptr ) + continue; + + area_contribution[j] = ( *match )[0].area; + area += area_contribution[j]; + ++contribution_count; + + // std::cout << fmt::format( "Contribution {}\t = {}\n", ( *match )[0].root->root->name, area_contribution[j] ); + } + + /* compute scaling factor and remaining area for non-matched gates */ + double scaling_factor = 1.0; + double remaining_area = 0; + + if ( contribution_count != max_multi_outputs ) + { + scaling_factor = 0.9; + + if ( area > multi_gates[0][i].area ) + scaling_factor -= ( area - multi_gates[0][i].area ) / area; + + remaining_area = ( multi_gates[0][i].area - area * scaling_factor ); + area = area * scaling_factor + remaining_area; + remaining_area /= ( max_multi_outputs - contribution_count ); + } + + /* assign weighted contribution */ + // double area_old = multi_gates[0][i].area; + // double area_check = 0; + for ( auto j = 0; j < max_multi_outputs; ++j ) + { + auto& gate = multi_gates[j][i]; + + if ( area_contribution[j] > 0 ) + gate.area = scaling_factor * area_contribution[j] * gate.area / area; + else + gate.area = remaining_area; + + // area_check += gate.area; + } + + // std::cout << fmt::format( "Area before: {}\t Area after {}\n", area_old, area_check ); + } + } + } + + bool check_delay_consistency( composed_gate const& g, uint32_t pin ) + { + TT tt = kitty::extend_to( g.function ); + uint16_t polarity = 0; + + /* canonicalize in case of P-configurations */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( tt, g.num_vars ); + tt = std::get<0>( canon ); + polarity = static_cast( std::get<1>( canon ) ); + } + + auto entry = _super_lib.find( tt ); + if ( entry == _super_lib.end() ) + { + std::cerr << fmt::format( "[i] WARNING: library does not contain cells that can implement output pin {} of the multi-output cell {}\n", pin, g.root->name ); + return false; + } + + /* check delay (at least one entry must have better or equal delay) */ + for ( auto const& sg : entry->second ) + { + bool valid = true; + for ( uint32_t i = 0; i < g.num_vars; ++i ) + { + float pin_delay = sg.tdelay[i]; + if ( ( sg.polarity >> i ) & 1 ) + pin_delay += _inv_delay; + + float mo_pin_delay = g.tdelay[i]; + if ( ( polarity >> i ) & 1 ) + mo_pin_delay += _inv_delay; + + if ( pin_delay > mo_pin_delay ) + { + valid = false; + break; + } + } + + if ( valid ) + { + return true; + } + } + + std::cerr << fmt::format( "[i] WARNING: library does not contain cells that could match the delay of output pin {} of multi-output cell {}\n", pin + 1, g.root->name ); + return false; + } + + float compute_worst_delay( gate const& g ) + { + float worst_delay = 0.0f; + + /* consider only block_delay */ + for ( auto const& pin : g.pins ) + { + float worst_pin_delay = static_cast( std::max( pin.rise_block_delay, pin.fall_block_delay ) ); + worst_delay = std::max( worst_delay, worst_pin_delay ); + } + return worst_delay; + } + + bool compare_sizes( composed_gate const& s1, composed_gate const& s2 ) + { + if ( s1.area < s2.area ) + return true; + else if ( s1.area > s2.area ) + return false; + + /* compute average pin delay */ + float s1_delay = 0, s2_delay = 0; + assert( s1.num_vars == s2.num_vars ); + for ( uint32_t i = 0; i < s1.num_vars; ++i ) + { + s1_delay += s1.tdelay[i]; + s2_delay += s2.tdelay[i]; + } + + if ( s1_delay < s2_delay ) + return true; + else if ( s1_delay > s2_delay ) + return false; + else if ( s1.root->name < s2.root->name ) + return true; + + return false; + } + + void filter_gates( std::deque> const& supergates, std::vector& skip_gates ) + { + assert( supergates.size() >= skip_gates.size() ); + for ( uint32_t i = 0; i < skip_gates.size() - 1; ++i ) + { + if ( supergates[i].root == nullptr ) + continue; + + if ( skip_gates[i] ) + continue; + + auto const& tti = supergates[i].function; + for ( uint32_t j = i + 1; j < skip_gates.size(); ++j ) + { + auto const& ttj = supergates[j].function; + + /* get the same functionality */ + if ( skip_gates[j] || tti != ttj ) + continue; + + if ( _ps.load_minimum_size_only ) + { + if ( compare_sizes( supergates[i], supergates[j] ) ) + { + skip_gates[j] = true; + continue; + } + else + { + skip_gates[i] = true; + break; + } + } + + /* is i smaller than j */ + bool smaller = supergates[i].area <= supergates[j].area; + + /* is i faster for every pin */ + bool faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( supergates[i].tdelay[k] > supergates[j].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[j] = true; + continue; + } + + /* is j faster for every pin */ + smaller = supergates[i].area >= supergates[j].area; + faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( supergates[j].tdelay[k] > supergates[i].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[i] = true; + break; + } + } + } + } + +private: + /* inverter info */ + float _inv_area{ 0.0 }; + float _inv_delay{ 0.0 }; + uint32_t _inv_id{ UINT32_MAX }; + + /* buffer info */ + float _buf_area{ 0.0 }; + float _buf_delay{ 0.0 }; + uint32_t _buf_id{ UINT32_MAX }; + + unsigned _max_size{ 0 }; /* max #fanins of the gates in the library */ + + bool _use_supergates; + + std::vector const _gates; /* collection of gates */ + super_lib const _supergates_spec; /* collection of supergates declarations */ + tech_library_params const _ps; + + std::vector const _cells; /* collection of standard cells */ + + super_utils _super; /* supergates generation */ + struct_library _struct; /* library for structural matching */ + lib_t _super_lib; /* library of enumerated gates */ + multi_lib_t _multi_lib; /* library of enumerated multioutput gates */ + multi_func_t _multi_funcs; /* enumerated functions for multioutput gates */ + struct_lib_t _struct_lib; /* library of gates for patterns IDs */ +}; /* class tech_library */ + +template +struct exact_supergate +{ + signal root; + + /* number of inputs of the supergate */ + uint8_t n_inputs{ 0 }; + /* saved polarities for inputs and/or outputs */ + uint8_t polarity{ 0 }; + + /* area */ + float area{ 0 }; + /* worst delay */ + float worstDelay{ 0 }; + /* pin-to-pin delay */ + std::array tdelay{ 0 }; + + exact_supergate( signal const root ) + : root( root ) {} +}; + +struct exact_library_params +{ + /* area of a gate */ + float area_gate{ 1.0f }; + /* area of an inverter */ + float area_inverter{ 0.0f }; + /* delay of a gate */ + float delay_gate{ 1.0f }; + /* delay of an inverter */ + float delay_inverter{ 0.0f }; + + /* classify in NP instead of NPN */ + bool np_classification{ false }; + /* Compute DC classes for matching with don't cares */ + bool compute_dc_classes{ false }; + /* verbose */ + bool verbose{ false }; +}; + +/*! \brief Library of graph structures for Boolean matching + * + * This class creates a technology library from a database + * of structures classified in NPN classes. Each NPN-entry in + * the database is stored in its NP class by removing the output + * inverter if present. The class creates supergates from the + * database computing area and delay information. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + mockturtle::mig_npn_resynthesis mig_resyn{ true }; + mockturtle::exact_library lib( mig_resyn ); + \endverbatim + */ +template +class exact_library +{ + using supergates_list_t = std::vector>; + using TT = kitty::static_truth_table; + using tt_hash = kitty::hash; + using lib_t = std::unordered_map; + using dc_transformation_t = std::tuple>; + using dc_t = std::pair; + using dc_lib_t = std::unordered_map, tt_hash>; + +public: + explicit exact_library( exact_library_params const& ps = {} ) + : _database(), + _ps( ps ), + _super_lib(), + _dc_lib() + { + _super_lib.reserve( 222 ); + } + + template + explicit exact_library( RewritingFn const& rewriting_fn, exact_library_params const& ps = {} ) + : _database(), + _ps( ps ), + _super_lib(), + _dc_lib() + { + _super_lib.reserve( 222 ); + generate_library( rewriting_fn ); + } + + template + void add_library( RewritingFn const& rewriting_fn ) + { + generate_library( rewriting_fn ); + } + + /*! \brief Get the structures matching the function. + * + * Returns a list of graph structures that match the function + * represented by the truth table. + */ + const supergates_list_t* get_supergates( TT const& tt ) const + { + auto match = _super_lib.find( tt ); + if ( match != _super_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the structures matching the function with DC. + * + * Returns a list of graph structures that match the function + * represented by the truth table and its dont care set. + * This functions also updates the phase and permutation vector + * of the original NPN class to the new one obtained using + * don't cares. + */ + const supergates_list_t* get_supergates( TT const& tt, TT const& dc, uint32_t& phase, std::vector& perm ) const + { + auto match = _super_lib.find( tt ); + if ( match == _super_lib.end() ) + return nullptr; + + /* lookup for don't care optimization */ + auto match_dc = _dc_lib.find( tt ); + if ( dc._bits == 0 || match_dc == _dc_lib.end() ) + return &match->second; + + for ( auto const& entry : match_dc->second ) + { + auto const& dc_entry_tt = std::get<0>( entry ); + + /* check for containment */ + if ( ( dc & dc_entry_tt ) == dc_entry_tt ) + { + auto const& dc_entry = std::get<1>( entry ); + + /* update phase and perm */ + uint32_t dc_entry_phase = std::get<1>( dc_entry ); + auto const& dc_entry_perm = std::get<2>( dc_entry ); + std::vector temp_perm( perm.size() ); + uint32_t temp_phase = dc_entry_phase & ( 1 << NInputs ); + for ( auto i = 0u; i < NInputs; ++i ) + { + temp_perm[dc_entry_perm[i]] = perm[i]; + temp_phase |= ( ( dc_entry_phase >> i ) & 1 ) << perm[i]; + } + phase ^= temp_phase; + std::copy( temp_perm.begin(), temp_perm.end(), perm.begin() ); + return std::get<0>( dc_entry ); + } + } + + /* no dont care optimization found */ + return &match->second; + } + + /*! \brief Returns the NPN database of structures. */ + Ntk& get_database() + { + return _database; + } + + /*! \brief Returns the NPN database of structures. */ + const Ntk& get_database() const + { + return _database; + } + + /*! \brief Get inverter information. + * + * Returns area, and delay cost of the inverter. + */ + const std::tuple get_inverter_info() const + { + return std::make_pair( _ps.area_inverter, _ps.delay_inverter ); + } + +private: + template + void generate_library( RewritingFn const& rewriting_fn ) + { + std::vector> pis; + for ( auto i = 0u; i < NInputs; ++i ) + { + pis.push_back( _database.create_pi() ); + } + + /* Compute NPN classes */ + std::unordered_set classes; + TT tt; + do + { + const auto res = kitty::exact_npn_canonization( tt ); + classes.insert( std::get<0>( res ) ); + kitty::next_inplace( tt ); + } while ( !kitty::is_const0( tt ) ); + + /* Constuct supergates */ + for ( auto const& entry : classes ) + { + supergates_list_t supergates_pos; + supergates_list_t supergates_neg; + auto const not_entry = ~entry; + + const auto add_supergate = [&]( auto const& f_new ) { + bool complemented = _database.is_complemented( f_new ); + auto f = f_new; + if ( _ps.np_classification && complemented ) + { + f = !f; + } + exact_supergate sg( f ); + compute_info( sg ); + if ( _ps.np_classification && complemented ) + { + supergates_neg.push_back( sg ); + } + else + { + supergates_pos.push_back( sg ); + } + _database.create_po( f ); + return true; + }; + + kitty::dynamic_truth_table function = kitty::extend_to( entry, NInputs ); + rewriting_fn( _database, function, pis.begin(), pis.end(), add_supergate ); + if ( supergates_pos.size() > 0 ) + { + std::stable_sort( supergates_pos.begin(), supergates_pos.end(), [&]( auto const& a, auto const& b ) { + return a.area < b.area; + } ); + _super_lib.insert( { entry, supergates_pos } ); + } + if ( _ps.np_classification && supergates_neg.size() > 0 ) + { + std::stable_sort( supergates_neg.begin(), supergates_neg.end(), [&]( auto const& a, auto const& b ) { + return a.area < b.area; + } ); + _super_lib.insert( { not_entry, supergates_neg } ); + } + } + + if ( _ps.compute_dc_classes ) + compute_dont_cares_classes(); + + if ( _ps.verbose ) + { + std::cout << "Classified in " << _super_lib.size() << " entries" << std::endl; + for ( auto const& pair : _super_lib ) + { + kitty::print_hex( pair.first ); + std::cout << ": "; + + for ( auto const& gate : pair.second ) + { + printf( "%.2f,%.2f,%x,%d,:", gate.worstDelay, gate.area, gate.polarity, gate.n_inputs ); + for ( auto j = 0u; j < NInputs; ++j ) + printf( "%.2f/", gate.tdelay[j] ); + std::cout << " "; + } + std::cout << std::endl; + } + } + } + + /* Computes delay and area info */ + void compute_info( exact_supergate& sg ) + { + _database.incr_trav_id(); + /* info does not consider input and output inverters */ + bool compl_root = _database.is_complemented( sg.root ); + auto const root = compl_root ? !sg.root : sg.root; + sg.area = compute_info_rec( sg, root, 0.0f ); + + /* output polarity */ + sg.polarity |= ( unsigned( compl_root ) ) << NInputs; + /* number of inputs */ + for ( auto i = 0u; i < NInputs; ++i ) + { + sg.tdelay[i] *= -1; /* invert to positive value */ + if ( sg.tdelay[i] != 0.0f ) + sg.n_inputs++; + } + sg.worstDelay *= -1; + } + + float compute_info_rec( exact_supergate& sg, signal const& root, float delay ) + { + auto n = _database.get_node( root ); + + if ( _database.is_constant( n ) ) + return 0.0f; + + float area = 0.0f; + float tdelay = delay; + + if ( _database.is_pi( n ) ) + { + sg.tdelay[_database.index_to_node( n ) - 1u] = std::min( sg.tdelay[_database.index_to_node( n ) - 1u], tdelay ); + sg.worstDelay = std::min( sg.worstDelay, tdelay ); + sg.polarity |= ( unsigned( _database.is_complemented( root ) ) ) << ( _database.index_to_node( n ) - 1u ); + return area; + } + + tdelay -= _ps.delay_gate; + + /* add gate area once */ + if ( _database.visited( n ) != _database.trav_id() ) + { + area += _ps.area_gate; + _database.set_value( n, 0u ); + _database.set_visited( n, _database.trav_id() ); + } + + if ( _database.is_complemented( root ) ) + { + tdelay -= _ps.delay_inverter; + /* add inverter area only once (shared by fanout) */ + if ( _database.value( n ) == 0u ) + { + area += _ps.area_inverter; + _database.set_value( n, 1u ); + } + } + + _database.foreach_fanin( n, [&]( auto const& child ) { + area += compute_info_rec( sg, child, tdelay ); + } ); + + return area; + } + + void compute_dont_cares_classes() + { + _dc_lib.clear(); + + /* save the size for each NPN class */ + std::unordered_map class_sizes; + for ( auto const& entry : _super_lib ) + { + const unsigned numgates = static_cast( std::get<1>( entry ).front().area ); + class_sizes.insert( { std::get<0>( entry ), numgates } ); + } + + uint32_t conflict_found = 0; + uint32_t total_exploration = 0; + + /* find don't care links */ + for ( auto entry_i = class_sizes.begin(); entry_i != class_sizes.end(); ++entry_i ) + { + auto const& tt_i = std::get<0>( *entry_i ); + auto const current_size = std::get<1>( *entry_i ); + + /* use a map to link the dont cares to the new size, NPN class, negations, and permutation vector */ + using dc_transf_t = std::tuple>; + std::unordered_map dc_sets; + + for ( auto entry_j = class_sizes.begin(); entry_j != class_sizes.end(); ++entry_j ) + { + auto const& tt_j = std::get<0>( *entry_j ); + uint32_t size = std::get<1>( *entry_j ); + + /* evaluate DC only for size improvement */ + if ( size >= current_size ) + continue; + + /* skip the same NPN class if gates are constructed in NP classes */ + if ( _ps.np_classification && tt_i == ~tt_j ) + continue; + + exact_npn_enumeration( tt_j, [&]( auto const& tt, uint32_t phase, std::vector const& perm ) { + /* extract the DC set */ + const auto dc = tt_i ^ tt; + + /* limit the explosion of DC combinations to evaluate */ + // if ( kitty::count_ones( dc ) > 3 ) + // return; + + ++total_exploration; + + /* check existance: filters ~12% of conflicts */ + if ( auto const& p = dc_sets.find( dc ); p != dc_sets.end() ) + { + if ( size < std::get<0>( std::get<1>( *p ) ) ) + dc_sets[dc] = std::make_tuple( size, tt_j, phase, perm ); + + ++conflict_found; + return; + } + + /* check dominance */ + auto it = dc_sets.begin(); + while ( it != dc_sets.end() ) + { + auto const& dc_set_tt = std::get<0>( *it ); + auto const& and_tt = dc_set_tt & dc; + + if ( dc_set_tt == and_tt && std::get<0>( std::get<1>( *it ) ) <= size ) + { + return; + } + else if ( dc == and_tt && size <= std::get<0>( std::get<1>( *it ) ) ) + { + it = dc_sets.erase( it ); + } + else + { + ++it; + } + } + + /* permute phase */ + uint32_t phase_perm = phase & ( 1 << NInputs ); + for ( auto i = 0u; i < NInputs; ++i ) + { + phase_perm |= ( ( phase >> perm[i] ) & 1 ) << i; + } + + /* insert in the dc_sets */ + dc_sets[dc] = std::make_tuple( size, tt_j, phase_perm, perm ); + } ); + } + + /* add entries to the main data structure */ + std::vector dc_transformations; + dc_transformations.reserve( dc_sets.size() ); + + std::array permutation; + + /* insert in a sorted way based on gain */ + /* TODO: optimize to reduce the number of cycles */ + for ( auto i = 0u; i < std::get<1>( *entry_i ); ++i ) + { + for ( auto const& dc : dc_sets ) + { + auto const& transf = std::get<1>( dc ); + + if ( std::get<0>( transf ) != i ) + { + continue; + } + + supergates_list_t const* sg = &_super_lib[std::get<1>( transf )]; + auto const& perm = std::get<3>( transf ); + + assert( perm.size() == NInputs ); + + for ( auto j = 0u; j < NInputs; ++j ) + { + permutation[j] = perm[j]; + } + + dc_transformations.emplace_back( std::make_pair( std::get<0>( dc ), std::make_tuple( sg, std::get<2>( transf ), permutation ) ) ); + } + } + + if ( !dc_transformations.empty() ) + _dc_lib.insert( { tt_i, dc_transformations } ); + } + } + +private: + Ntk _database; + exact_library_params const _ps; + lib_t _super_lib; + dc_lib_t _dc_lib; +}; /* class exact_library */ + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/truth_table_cache.hpp b/third-party/mockturtle/include/mockturtle/utils/truth_table_cache.hpp new file mode 100644 index 00000000000..2224e1864bb --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/truth_table_cache.hpp @@ -0,0 +1,172 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file truth_table_cache.hpp + \brief Truth table cache + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Truth table cache. + * + * A truth table cache is used to store truth tables. Many applications + * require to assign truth tables to nodes in a network. But since the truth + * tables often repeat, it is more convenient to store an index to a cache + * that stores the truth table. In order to reduce space, only one entry for + * a truth table and its complement are stored in the cache. To distinguish + * between both versions, the index to an entry in the truth table cache is + * represented as literal. The truth table whose function maps the input + * assignment \f$0, \dots, 0\f$ to \f$0\f$ is considered the *normal* truth + * table, while its complement is considered the complemented version. Only + * the normal truth table is stored at some index \f$i\f$. A positive literal + * \f$2i\f$ points to the normal truth table at index \f$i\f$. A negative + * literal \f$2i + 1\f$ points to the same truth table but returns its + * complement. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + truth_table_cache cache; + + kitty::dynamic_truth_table maj( 3 ); + kitty::create_majority( maj ); + auto l1 = cache.insert( maj ); // index is 0 + + auto tt = cache[l1 ^ 1]; // tt is ~maj + auto l2 = cache.insert( tt ); // index is 1 + + auto s = cache.size(); // size is 1 + \endverbatim + */ +template +class truth_table_cache +{ +public: + /*! \brief Creates a truth table cache and reserves memory. */ + truth_table_cache( uint32_t capacity = 1000u ); + + /*! \brief Inserts a truth table and returns a literal. + * + * To save space, only normal functions are stored in the truth table cache. + * A function is normal, if the input pattern \f$0, \dots, 0\f$ maps to + * \f$0\f$. If a function is not normal, its complement is inserted into the + * cache and a negative literal is returned. + * + * The default convention for literals is assumed. That is an index \f$i\f$ + * (starting) from \f$0\f$ has positive literal \f$2i\f$ and negative literal + * \f$2i + 1\f$. + * + * \param tt Truth table to insert + * \return Literal of position in cache + */ + uint32_t insert( TT tt ); + + /*! \brief Returns truth table for a given literal. + * + * The function requires that `lit` is smaller than `size()`. + */ + TT operator[]( uint32_t lit ) const; + + /*! \brief Returns number of normalized truth tables in the cache. */ + auto size() const { return _data.size(); } + + /*! \brief Resizes the cache. + * + * Reserve additional space for cache and data. + */ + void resize( uint32_t capacity ); + +private: + phmap::flat_hash_map> _indexes; + std::vector _data; +}; + +template +truth_table_cache::truth_table_cache( uint32_t capacity ) +{ + _indexes.reserve( capacity ); + _data.reserve( capacity ); +} + +template +uint32_t truth_table_cache::insert( TT tt ) +{ + uint32_t is_compl{ 0 }; + + if ( kitty::get_bit( tt, 0 ) ) + { + is_compl = 1; + tt = ~tt; + } + + /* is truth table already in cache? */ + const auto it = _indexes.find( tt ); + if ( it != _indexes.end() ) + { + return static_cast( 2 * it->second + is_compl ); + } + + /* add truth table to end of cache */ + const auto size = _data.size(); + const auto index = static_cast( 2 * size + is_compl ); + _data.push_back( tt ); + _indexes[tt] = static_cast( size ); + return index; +} + +template +TT truth_table_cache::operator[]( uint32_t index ) const +{ + auto& entry = _data[index >> 1]; + return ( index & 1 ) ? ~entry : entry; +} + +template +void truth_table_cache::resize( uint32_t capacity ) +{ + _indexes.reserve( capacity ); + _data.reserve( capacity ); +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/utils/truth_table_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/truth_table_utils.hpp new file mode 100644 index 00000000000..2a0d4fb4360 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/truth_table_utils.hpp @@ -0,0 +1,63 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file truth_table_utils.hpp + \brief Truth table manipulation utils + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include + +namespace mockturtle +{ + +/*! \brief Replacement rule of MAJ3 + * + * Given a majority gate with fanin functions `fanin0`, `fanin1` and `fanin2`, + * check if the first fanin `fanin0` can be replaced by `replacement` + * without changing the output function of the majority gate. + * + * By the replacement rule, ` = ` if and only if + * `(x ^ w)(y ^ z) = 0`, i.e., `y != z` implies `w = x`. + * + * This check is used in relevance optimization in MIG resubstitution. + * + * \param fanin0 Truth table of the first fanin function; also the fanin to be replaced. + * \param fanin1 Truth table of the second fanin function. + * \param fanin2 Truth table of the third fanin function. + * \param replacement Truth table of the candidate to replace `fanin0`. + * \return ` = ` + */ +template::value>> +bool can_replace_majority_fanin( TT const& fanin0, TT const& fanin1, TT const& fanin2, TT const& replacement ) +{ + return kitty::is_const0( ( ( fanin0 ^ replacement ) & ( fanin1 ^ fanin2 ) ) ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/window_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/window_utils.hpp new file mode 100644 index 00000000000..6fce12d579d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/window_utils.hpp @@ -0,0 +1,1046 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file window_utils.hpp + \brief Utilities to collect small-scale sets of nodes + + \author Heinz Riener +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +inline void collect_nodes_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using signal = typename Ntk::signal; + + if ( ntk.eval_color( n, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + return; + } + ntk.paint( n ); + + ntk.foreach_fanin( n, [&]( signal const& fi ) { + if ( ntk.is_constant( ntk.get_node( fi ) ) ) + return; + collect_nodes_recur( ntk, ntk.get_node( fi ), nodes ); + } ); + nodes.push_back( n ); +} + +} /* namespace detail */ + +/*! \brief Collect nodes in between of two node sets + * + * \param ntk A network + * \param inputs A node set + * \param outputs A signal set + * \return Nodes enclosed by inputs and outputs + * + * The output set has to be chosen in a way such that every path from + * PIs to outputs passes through at least one input. + * + * Uses a new color. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `new_color` + * - `paint` + */ +template>> +inline std::vector collect_nodes( Ntk const& ntk, + std::vector const& inputs, + std::vector const& outputs ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /* convert output signals to nodes */ + std::vector _outputs; + std::transform( std::begin( outputs ), std::end( outputs ), std::back_inserter( _outputs ), + [&ntk]( signal const& s ) { + return ntk.get_node( s ); + } ); + return collect_nodes( ntk, inputs, _outputs ); +} + +/*! \brief Collect nodes in between of two node sets + * + * \param ntk A network + * \param inputs A node set + * \param outputs A node set + * \return Nodes enclosed by inputs and outputs + * + * The output set has to be chosen in a way such that every path from + * PIs to outputs passes through at least one input. + * + * Uses a new color. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `new_color` + * - `paint` + */ +template +inline std::vector collect_nodes( Ntk const& ntk, + std::vector const& inputs, + std::vector const& outputs ) +{ + using node = typename Ntk::node; + + ntk.new_color(); + + /* mark inputs visited */ + for ( auto const& i : inputs ) + { + if ( ntk.eval_color( i, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + ntk.paint( i ); + } + + /* recursively collect all nodes in between inputs and outputs */ + std::vector nodes; + for ( auto const& o : outputs ) + { + detail::collect_nodes_recur( ntk, o, nodes ); + } + return nodes; +} + +/*! \brief Identify inputs using reference counting + * + * Uses a new_color and marks all nodes and inputs. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `new_color` + * - `paint` + */ +template +std::vector collect_inputs( Ntk const& ntk, std::vector const& nodes ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /* mark all nodes with a new color */ + ntk.new_color(); + for ( const auto& n : nodes ) + { + ntk.paint( n ); + } + + /* if a fanin is not colored, then it's an input */ + std::vector inputs; + for ( const auto& n : nodes ) + { + ntk.foreach_fanin( n, [&]( signal const& fi ) { + node const i = ntk.get_node( fi ); + if ( ntk.eval_color( i, [&ntk]( auto c ) { return c != ntk.current_color(); } ) ) + { + if ( std::find( std::begin( inputs ), std::end( inputs ), i ) == std::end( inputs ) ) + { + inputs.push_back( i ); + } + } + return true; + } ); + } + + /* mark all inputs */ + for ( const auto& n : inputs ) + { + ntk.paint( n ); + } + + return inputs; +} + +/*! \brief Identify outputs using reference counting + * + * Identify outputs using a reference counting approach. The + * algorithm counts the references of the fanins of all nodes and + * compares them with the fanout_sizes of the respective nodes. If + * reference count and fanout_size do not match, then the node is + * references outside of the node set and the respective is identified + * as an output. + * + * \param ntk A network + * \param inputs Inputs of a window + * \param nodes Inner nodes of a window (i.e., the intersection of + * inputs and nodes is assumed to be empty) + * \param refs Reference counters (in the size of the network and + * initialized to 0) + * \return Output signals of the window + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `fanout_size` + * - `foreach_fanin` + * - `get_node` + * - `is_ci` + * - `is_constant` + * - `make_signal` + */ +template +inline std::vector collect_outputs( Ntk const& ntk, + std::vector const& inputs, + std::vector const& nodes, + std::vector& refs ) +{ + using signal = typename Ntk::signal; + + std::vector outputs; + + /* mark the inputs visited */ + ntk.new_color(); + for ( auto const& i : inputs ) + { + ntk.paint( i ); + } + + /* reference fanins of nodes */ + for ( auto const& n : nodes ) + { + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + + assert( !ntk.is_constant( n ) && !ntk.is_ci( n ) ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + refs[ntk.get_node( fi )] += 1; + } ); + } + + /* if the fanout_size of a node does not match the reference count, + the node has fanouts outside of the window is an output */ + for ( const auto& n : nodes ) + { + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + + if ( ntk.fanout_size( n ) != refs[n] ) + { + outputs.emplace_back( ntk.make_signal( n ) ); + } + } + + /* dereference fanins of nodes */ + for ( auto const& n : nodes ) + { + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + + assert( !ntk.is_constant( n ) && !ntk.is_ci( n ) ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + refs[ntk.get_node( fi )] -= 1; + } ); + } + + return outputs; +} + +namespace detail +{ + +template +inline bool cut_is_trivial( Ntk const& ntk, std::vector const& inputs ) +{ + for ( const auto& n : inputs ) + { + if ( !ntk.is_constant( n ) && !ntk.is_ci( n ) ) + { + return false; + } + } + return true; +} + +} // namespace detail + +/*! \brief Performs in-place zero-cost expansion of a set of nodes towards TFI + * + * The algorithm attempts to derive a different cut of the same size + * that is closer to the network's PIs. This expansion towards TFI is + * called zero-cost because it merges nodes only if the number of + * inputs does not increase. + * + * Precondition: This procedure presumes that nodes and inputs are + * painted in the current color. + * + * Uses the current color to mark nodes. Only nodes not painted with + * the current color are considered for expanding the cut. Nodes + * marked are considered already in the cut. + * + * \param ntk A network + * \param inputs Input nodes + * \return True if and only if the inputs form a trivial cut that + * cannot be further extended, e.g., when the cut only + * consists of PIs. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `paint` + * - `size` + */ +template +bool expand0_towards_tfi( Ntk const& ntk, std::vector& inputs ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /* we call a set of inputs (= a cut) trivial if all nodes are either + constants or CIs, such that they cannot be further expanded towards + the TFI */ + bool trivial_cut{ true }; + + /* repeat expansion towards TFI until a fix-point is reached */ + bool changed{ true }; + std::vector new_inputs; + while ( changed ) + { + trivial_cut = true; + changed = false; + + for ( auto it = std::begin( inputs ); it != std::end( inputs ); ) + { + assert( ntk.color( *it ) == ntk.current_color() ); + if ( ntk.is_constant( *it ) || ntk.is_ci( *it ) ) + { + ++it; + continue; + } + trivial_cut = false; + + /* count how many fanins are already in the cut */ + uint32_t count_fanin_outside{ 0 }; + std::optional ep; + ntk.foreach_fanin( *it, [&]( signal const& fi ) { + node const n = ntk.get_node( fi ); + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + ++count_fanin_outside; + } + else + { + ep = n; + } + } ); + + /* if the expansion is not cost-free, then proceeded with the next leaf */ + if ( count_fanin_outside + 1 < ntk.fanin_size( *it ) ) + { + ++it; + continue; + } + + if ( ep ) + { + if ( ntk.eval_color( *ep, [&ntk]( auto c ) { return c != ntk.current_color(); } ) ) + { + new_inputs.push_back( *ep ); + ntk.paint( *ep ); + } + } + it = inputs.erase( it ); + changed = true; + } + + std::copy( std::begin( new_inputs ), std::end( new_inputs ), + std::back_inserter( inputs ) ); + new_inputs.clear(); + } + + assert( trivial_cut == detail::cut_is_trivial( ntk, inputs ) ); + return trivial_cut; +} + +namespace detail +{ + +template +inline void evaluate_fanin( typename Ntk::node const& n, std::vector>& candidates ) +{ + auto it = std::find_if( std::begin( candidates ), std::end( candidates ), + [&n]( auto const& p ) { + return p.first == n; + } ); + if ( it == std::end( candidates ) ) + { + /* new fanin: referenced for the 1st time */ + candidates.push_back( std::make_pair( n, 1u ) ); + } + else + { + /* otherwise, if not new, then just increase the reference counter */ + ++it->second; + } +} + +template +inline typename Ntk::node select_next_fanin_to_expand_tfi( Ntk const& ntk, std::vector const& inputs ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + assert( inputs.size() > 0u && "inputs must not be empty" ); + assert( !cut_is_trivial( ntk, inputs ) ); + + /* evaluate the fanins with respect to their costs (how often are they referenced?) */ + std::vector> candidates; + for ( auto const& i : inputs ) + { + if ( ntk.is_constant( i ) || ntk.is_ci( i ) ) + { + continue; + } + + ntk.foreach_fanin( i, [&]( signal const& fi ) { + if ( ntk.is_constant( ntk.get_node( fi ) ) ) + { + return true; + } + detail::evaluate_fanin( ntk.get_node( fi ), candidates ); + return true; + } ); + } + + assert( candidates.size() > 0u ); + + /* select the fanin with maximum reference count; if two fanins have equal reference count, select the one with more fanouts */ + std::pair best_fanin{ candidates[0] }; + for ( auto const& candidate : candidates ) + { + if ( candidate.second > best_fanin.second || + ( candidate.second == best_fanin.second && ntk.fanout_size( candidate.first ) > ntk.fanout_size( best_fanin.first ) ) ) + { + best_fanin = candidate; + } + } + + /* as long as the inputs do not form a trivial cut, this procedure will always find a fanin to expand */ + assert( best_fanin.first != 0 ); + + return best_fanin.first; +} + +} /* namespace detail */ + +/*! \brief Performs in-place expansion of a set of nodes towards TFI + * + * Expand the inputs towards TFI by iteratively selecting the fanins + * with the highest reference count within the cut and highest number + * of fanouts. Expansion continues until either `inputs` forms a + * trivial cut or the `inputs`'s size reaches `input_limit`. The + * procedure allows a temporary increase of `inputs` beyond the + * `input_limit` for at most `MAX_ITERATIONS`. + * + * Precondition: This procedure presumes that nodes and inputs are + * painted in the current color. + * + * Uses a new color. + * + * \param ntk A network + * \param inputs Input nodes + * \param input_limit Size limit for the maximum number of input nodes + */ +template +void expand_towards_tfi( Ntk const& ntk, std::vector& inputs, uint32_t input_limit ) +{ + using node = typename Ntk::node; + + static constexpr uint32_t const MAX_ITERATIONS{ 5u }; + + if ( expand0_towards_tfi( ntk, inputs ) ) + { + return; + } + + std::optional> best_cut; + if ( inputs.size() <= input_limit ) + { + best_cut = inputs; + } + + bool trivial_cut = false; + uint32_t iterations{ 0 }; + while ( !trivial_cut && ( inputs.size() <= input_limit || iterations < MAX_ITERATIONS ) ) + { + node const n = detail::select_next_fanin_to_expand_tfi( ntk, inputs ); + inputs.push_back( n ); + ntk.paint( n ); + + trivial_cut = expand0_towards_tfi( ntk, inputs ); + assert( trivial_cut == detail::cut_is_trivial( ntk, inputs ) ); + + iterations = inputs.size() > input_limit ? iterations + 1 : 0; + if ( inputs.size() <= input_limit && + ( !best_cut || best_cut->size() <= inputs.size() ) ) + { + best_cut = inputs; + } + } + + if ( best_cut ) + { + inputs = *best_cut; + } + else + { + assert( inputs.size() > input_limit ); + } +} + +/*! \brief Performs in-place expansion of a set of nodes towards TFO + * + * Iteratively expands the inner nodes of the window with those + * fanouts that are supported by the window until a fixed-point is + * reached. + * + * Uses a new color. + * + * \param ntk A network + * \param inputs Input nodes of a window + * \param nodes Inner nodes of a window + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `foreach_fanout` + * - `get_node` + * - `is_ci` + * - `new_color` + */ +template +void expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + auto explore_fanouts = [&]( Ntk const& ntk, node const& n, std::set& result ) { + ntk.foreach_fanout( n, [&]( node const& fo, uint64_t index ) { + /* only look at the first few fanouts */ + if ( index > 5 ) + { + return false; + } + /* skip all nodes that are already in nodes */ + if ( ntk.eval_color( fo, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + return true; + } + result.insert( fo ); + return true; + } ); + }; + + /* create a new traversal ID */ + ntk.new_color(); + + /* mark the inputs visited */ + std::for_each( std::begin( inputs ), std::end( inputs ), + [&ntk]( node const& n ) { ntk.paint( n ); } ); + + /* mark the nodes visited */ + std::for_each( std::begin( nodes ), std::end( nodes ), + [&ntk]( node const& n ) { ntk.paint( n ); } ); + + /* collect all nodes that have fanouts not yet contained in nodes */ + std::set eps; + for ( const auto& i : inputs ) + { + explore_fanouts( ntk, i, eps ); + } + for ( const auto& n : nodes ) + { + explore_fanouts( ntk, n, eps ); + } + + bool changed = true; + std::set new_eps; + while ( changed ) + { + new_eps.clear(); + changed = false; + + auto it = std::begin( eps ); + while ( it != std::end( eps ) ) + { + node const ep = *it; + if ( ntk.eval_color( ep, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + it = eps.erase( it ); + continue; + } + + bool all_children_belong_to_window = true; + ntk.foreach_fanin( ep, [&]( signal const& fi ) { + node const child = ntk.get_node( fi ); + if ( ntk.eval_color( child, [&]( auto c ) { return c != ntk.current_color(); } ) ) + { + all_children_belong_to_window = false; + return false; + } + return true; + } ); + + if ( all_children_belong_to_window ) + { + assert( ep != 0 ); + assert( !ntk.is_ci( ep ) ); + nodes.emplace_back( ep ); + ntk.paint( ep ); + it = eps.erase( it ); + + explore_fanouts( ntk, ep, new_eps ); + } + + if ( it != std::end( eps ) ) + { + ++it; + } + } + + if ( !new_eps.empty() ) + { + eps.insert( std::begin( new_eps ), std::end( new_eps ) ); + changed = true; + } + } +} + +namespace detail +{ + +template +void levelized_expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes, + std::vector>& levels ) +{ + using node = typename Ntk::node; + + static constexpr uint32_t const MAX_FANOUTS{ 5u }; + + ntk.new_color(); + + /* mapping from level to nodes (which nodes are on a certain level?) */ + levels.resize( ntk.depth() + 1 ); + + /* list of indices of used levels (avoid iterating over all levels) */ + std::vector used; + + /* mark all inputs and fill their level information into `levels` and `used` */ + for ( const auto& i : inputs ) + { + uint32_t const node_level = ntk.level( i ); + ntk.paint( i ); + if constexpr ( auto_resize ) + { + if ( levels.size() <= node_level ) + { + levels.resize( std::max( uint32_t( 2 * levels.size() ), node_level ) ); + } + } + levels.at( node_level ).push_back( i ); + if ( std::find( std::begin( used ), std::end( used ), node_level ) == std::end( used ) ) + { + used.push_back( node_level ); + } + } + + /* mark all nodes and fill their level information into `levels` and `used` */ + for ( const auto& n : nodes ) + { + uint32_t const node_level = ntk.level( n ); + ntk.paint( n ); + if constexpr ( auto_resize ) + { + if ( levels.size() <= node_level ) + { + levels.resize( std::max( uint32_t( 2 * levels.size() ), node_level ) ); + } + } + levels.at( node_level ).push_back( n ); + if ( std::find( std::begin( used ), std::end( used ), node_level ) == std::end( used ) ) + { + used.push_back( node_level ); + } + } + + std::stable_sort( std::begin( used ), std::end( used ) ); + + for ( uint32_t index = 0u; index < used.size(); ++index ) + { + std::vector& level = levels.at( used[index] ); + for ( auto j = 0u; j < level.size(); ++j ) + { + ntk.foreach_fanout( level[j], [&]( node const& fo, uint64_t index ) { + /* avoid getting stuck on nodes with many fanouts */ + if ( index == MAX_FANOUTS ) + { + return false; + } + + /* ignore nodes without fanins */ + if ( ntk.is_constant( fo ) || ntk.is_ci( fo ) ) + { + return true; + } + + if ( ntk.eval_color( fo, [&ntk]( auto c ) { return c != ntk.current_color(); } ) && + ntk.eval_fanins_color( fo, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + /* add fanout to nodes */ + nodes.push_back( fo ); + + /* update data structured */ + uint32_t const node_level = ntk.level( fo ); + ntk.paint( fo ); + if constexpr ( auto_resize ) + { + if ( levels.size() <= node_level ) + { + levels.resize( std::max( uint32_t( 2 * levels.size() ), node_level ) ); + } + } + levels.at( node_level ).push_back( fo ); + if ( std::find( std::begin( used ), std::end( used ), node_level ) == std::end( used ) ) + { + used.push_back( node_level ); + std::stable_sort( std::begin( used ), std::end( used ) ); + } + } + + return true; + } ); + } + level.clear(); + } +} + +} // namespace detail + +/*! \brief Performs in-place expansion of a set of nodes towards TFO + * + * Iteratively expands the inner nodes of the window with those + * fanouts that are supported by the window. Explores the fanouts + * level by level. Starting with those that are closest to the + * inputs. + * + * Uses a new color. + * + * \param ntk A network + * \param inputs Input nodes of a window + * \param nodes Inner nodes of a window + * + * **Required network functions:** + * - `current_color` + * - `depth` + * - `eval_color` + * - `eval_fanins_color` + * - `foreach_fanin` + * - `foreach_fanout` + * - `get_node` + * - `is_ci` + * - `is_constant` + * - `level` + * - `new_color` + * - `paint` + */ +template +void levelized_expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes ) +{ + std::vector> levels; + detail::levelized_expand_towards_tfo( ntk, inputs, nodes, levels ); +} + +namespace detail +{ + +template +void cover_recursive( Ntk const& ntk, typename Ntk::node const& root, std::vector& nodes ) +{ + if ( ntk.color( root ) == ntk.current_color() ) + { + return; + } + + ntk.foreach_fanin( root, [&]( auto const& fi ) { + cover_recursive( ntk, ntk.get_node( fi ), nodes ); + } ); + + nodes.push_back( root ); +} + +} // namespace detail + +template +std::vector cover( Ntk const& ntk, typename Ntk::node const& root, std::vector const& leaves ) +{ + ntk.new_color(); + for ( auto const& l : leaves ) + { + ntk.paint( l ); + } + + std::vector nodes; + detail::cover_recursive( ntk, root, nodes ); + + /* remove duplicates */ + std::stable_sort( std::begin( nodes ), std::end( nodes ) ); + auto last = std::unique( std::begin( nodes ), std::end( nodes ) ); + nodes.erase( last, std::end( nodes ) ); + + return nodes; +} + +/*! \brief Create a (l,k)-window around a pivot. + * + * Expands a reconvergency rooted in a given pivot node `p` into a + * window with l inputs and k outputs. + * + * Uses a new color. + * + * **Required network functions:** + * - `current_color` + * - `depth` + * - `eval_color` + * - `eval_fanins_color` + * - `foreach_fanin` + * - `foreach_fanout` + * - `get_node` + * - `is_ci` + * - `is_constant` + * - `level` + * - `new_color` + * - `paint` + */ +template +class create_window_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + struct window + { + std::vector inputs; + std::vector nodes; + std::vector outputs; + }; + +protected: + /* constant node used to denotes invalid window element */ + static constexpr node INVALID_NODE{ 0 }; + + /* number of iterations */ + static constexpr uint32_t NUM_ITERATIONS{ 5 }; + +public: + create_window_impl( Ntk const& ntk ) + : ntk( ntk ), path( ntk.size() ), refs( ntk.size() ) + { + } + + void resize( uint32_t size ) + { + path.resize( size ); + refs.resize( size ); + } + + std::optional run( node const& pivot, uint32_t cut_size, uint32_t num_levels ) + { + /* find a reconvergence from the pivot and collect the nodes */ + std::optional> nodes; + if ( !( nodes = identify_reconvergence( pivot, num_levels ) ) ) + { + /* if there is no reconvergence, then optimization is not possible */ + return std::nullopt; + } + + /* collect the fanins for these nodes */ + std::vector inputs = collect_inputs( ntk, *nodes ); + if ( inputs.size() <= cut_size + 3 ) + { + /* expand the nodes towards the TFI */ + expand_towards_tfi( ntk, inputs, cut_size ); + + /* compute the cover of the (pivot, inputs)-cut */ + *nodes = cover( ntk, pivot, inputs ); + + /* expand the nodes towards the TFO */ + std::stable_sort( std::begin( inputs ), std::end( inputs ) ); + detail::levelized_expand_towards_tfo( ntk, inputs, *nodes, levels ); + } + + if ( inputs.size() > cut_size || nodes->empty() ) + { + return std::nullopt; + } + + /* top. sort nodes */ + std::stable_sort( std::begin( inputs ), std::end( inputs ) ); + std::stable_sort( std::begin( *nodes ), std::end( *nodes ) ); + + /* collect the nodes with fanout outside of nodes */ + std::vector outputs = collect_outputs( ntk, inputs, *nodes, refs ); + assert( outputs.size() > 0u ); + + return window{ inputs, *nodes, outputs }; + } + +protected: + std::optional> identify_reconvergence( node const& pivot, uint64_t num_iterations ) + { + assert( !ntk.is_ci( pivot ) && !ntk.is_constant( pivot ) ); + + ntk.new_color(); + visited.clear(); + ntk.foreach_fanin( pivot, [&]( signal const& fi ) { + uint32_t const color = ntk.new_color(); + node const& n = ntk.get_node( fi ); + path[n] = INVALID_NODE; + visited.push_back( n ); + ntk.paint( n, color ); + } ); + + uint64_t start{ 0 }; + uint64_t stop; + for ( uint32_t iteration = 0u; iteration < num_iterations; ++iteration ) + { + stop = visited.size(); + for ( uint32_t i = start; i < stop; ++i ) + { + node const n = visited.at( i ); + std::optional meet = explore_frontier_of_node( n ); + if ( meet ) + { + visited.clear(); + gather_nodes_recursively( path[*meet] ); + gather_nodes_recursively( n ); + visited.push_back( pivot ); + return visited; + } + } + start = stop; + } + + return std::nullopt; + } + + std::optional explore_frontier_of_node( node const& n ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + return std::nullopt; + } + + std::optional meet; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + node const& fi_node = ntk.get_node( fi ); + if ( ntk.eval_color( n, [this]( auto c ) { return c > ntk.current_color() - ntk.max_fanin_size; } ) && + ntk.eval_color( fi_node, [this]( auto c ) { return c > ntk.current_color() - ntk.max_fanin_size; } ) && + ntk.eval_color( n, fi_node, []( auto c0, auto c1 ) { return c0 != c1; } ) ) + { + meet = fi_node; + return false; + } + + if ( ntk.eval_color( fi_node, [this]( auto c ) { return c > ntk.current_color() - ntk.max_fanin_size; } ) ) + { + return true; /* next */ + } + + ntk.paint( fi_node, n ); + path[fi_node] = n; + visited.push_back( fi_node ); + + return true; /* next */ + } ); + + return meet; + } + + /* collect nodes recursively following along the `path` until INVALID_NODE is reached */ + void gather_nodes_recursively( node const& n ) + { + if ( n == INVALID_NODE ) + { + return; + } + + visited.push_back( n ); + + node const pred = path[n]; + if ( pred == INVALID_NODE ) + { + return; + } + + assert( ntk.eval_color( n, pred, []( auto c0, auto c1 ) { return c0 == c1; } ) ); + gather_nodes_recursively( pred ); + } + +protected: + Ntk const& ntk; + std::vector visited; + std::vector path; + std::vector refs; + std::vector> levels; +}; /* create_window_impl */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/binding_view.hpp b/third-party/mockturtle/include/mockturtle/views/binding_view.hpp new file mode 100644 index 00000000000..1eb529a0ee2 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/binding_view.hpp @@ -0,0 +1,261 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file binding_view.hpp + \brief Implements methods to bind the network to a standard cell library + + \author Alessandro Tempia Calvino + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../io/genlib_reader.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Adds bindings to a technology library and mapping API methods. + * + * This view adds methods to create and manage a mapped network that + * implements gates contained in a technology library. This view + * is returned by the technology mapping command `map`. It can be used + * to report statistics about the network and write the network into + * a verilog file. It always adds the functions `has_binding`, + * `remove_binding`, `add_binding`, `add_binding_with_check`, `get_binding`, + * `get_binding_index`, `get_library`, `compute_area`, `compute_worst_delay`, + * `report_stats`, and `report_gates_usage`. + * + * **Required network functions:** + * - `size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // read cell library in genlib format + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ) + tech_library tech_lib( gates ); + + // call technology mapping to obtain the view + binding_view res = map( aig, tech_lib ); + + // prints stats and gates usage + res.report_stats(); + res.report_gates_usage(); + + // write the mapped network in verilog + write_verilog_with_binding( res, "file.v" ); + \endverbatim + */ +template +class binding_view : public Ntk +{ +public: + using node = typename Ntk::node; + +public: + explicit binding_view( std::vector const& library ) + : Ntk(), _library{ library }, _bindings( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + explicit binding_view( Ntk const& ntk, std::vector const& library ) + : Ntk( ntk ), _library{ library }, _bindings( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + binding_view& operator=( binding_view const& binding_ntk ) + { + Ntk::operator=( binding_ntk ); + _library = binding_ntk._library; + _bindings = binding_ntk._bindings; + return *this; + } + + void add_binding( node const& n, uint32_t gate_id ) + { + assert( gate_id < _library.size() ); + _bindings[n] = gate_id; + } + + bool add_binding_with_check( node const& n, uint32_t gate_id ) + { + assert( gate_id < _library.size() ); + + auto const& cell = _library[gate_id]; + + if ( Ntk::node_function( n ) == cell.function ) + { + _bindings[n] = gate_id; + return true; + } + return false; + } + + void remove_binding( node const& n ) const + { + _bindings.erase( n ); + } + + const gate& get_binding( node const& n ) const + { + return _library[_bindings[n]]; + } + + bool has_binding( node const& n ) const + { + return _bindings.has( n ); + } + + unsigned int get_binding_index( node const& n ) const + { + return _bindings[n]; + } + + const std::vector& get_library() const + { + return _library; + } + + double compute_area() const + { + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_binding( n ) ) + { + area += get_binding( n ).area; + } + } ); + + return area; + } + + double compute_worst_delay() const + { + topo_view ntk_topo{ *this }; + node_map delays( *this ); + double worst_delay = 0; + + ntk_topo.foreach_node( [&]( auto const& n, auto ) { + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + { + delays[n] = 0; + return true; + } + + if ( has_binding( n ) ) + { + auto const& g = get_binding( n ); + double gate_delay = 0; + Ntk::foreach_fanin( n, [&]( auto const& f, auto i ) { + gate_delay = std::max( gate_delay, (double)( delays[f] + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ) ); + } ); + delays[n] = gate_delay; + worst_delay = std::max( worst_delay, gate_delay ); + } + return true; + } ); + + return worst_delay; + } + + void report_stats( std::ostream& os = std::cout ) const + { + os << fmt::format( "[i] Report stats: area = {:>5.2f}; delay = {:>5.2f};\n", compute_area(), compute_worst_delay() ); + } + + void report_gates_usage( std::ostream& os = std::cout ) const + { + std::vector gates_profile( _library.size(), 0u ); + + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_binding( n ) ) + { + auto const& g = get_binding( n ); + ++gates_profile[g.id]; + area += g.area; + } + } ); + + os << "[i] Report gates usage:\n"; + + uint32_t tot_instances = 0u; + for ( auto i = 0u; i < gates_profile.size(); ++i ) + { + if ( gates_profile[i] > 0u ) + { + float tot_gate_area = gates_profile[i] * _library[i].area; + + os << fmt::format( "[i] {:<25}", _library[i].name ) + << fmt::format( "\t Instance = {:>10d}", gates_profile[i] ) + << fmt::format( "\t Area = {:>12.2f}", tot_gate_area ) + << fmt::format( " {:>8.2f} %\n", tot_gate_area / area * 100 ); + + tot_instances += gates_profile[i]; + } + } + + os << fmt::format( "[i] {:<25}", "TOTAL" ) + << fmt::format( "\t Instance = {:>10d}", tot_instances ) + << fmt::format( "\t Area = {:>12.2f} 100.00 %\n", area ); + } + +private: + std::vector _library; + node_map> _bindings; +}; /* binding_view */ + +template +binding_view( T const& ) -> binding_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/cell_view.hpp b/third-party/mockturtle/include/mockturtle/views/cell_view.hpp new file mode 100644 index 00000000000..505cea82255 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/cell_view.hpp @@ -0,0 +1,297 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cell_view.hpp + \brief Implements methods to bind the network to a standard cell library + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../utils/node_map.hpp" +#include "../utils/standard_cell.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Adds cells to a technology library and mapping API methods. + * + * This view adds methods to create and manage a mapped network that + * implements cells contained in a technology library. This view + * is returned by the technology mapping command `emap`. It can be used + * to report statistics about the network and write the network into + * a verilog file. It always adds the functions `has_cell`, + * `remove_cell`, `add_cell`, `add_cell_with_check`, `get_cell`, + * `get_cell_index`, `get_library`, `compute_area`, `compute_worst_delay`, + * `report_stats`, and `report_cells_usage`. + * + * **Required network functions:** + * - `size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // read cell library in genlib format + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ) + tech_library tech_lib( gates ); + + // call technology mapping to obtain the view + cell_view res = emap_block( aig, tech_lib ); + + // prints stats and cells usage + res.report_stats(); + res.report_cells_usage(); + \endverbatim + */ +template +class cell_view : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit cell_view( std::vector const& library ) + : Ntk(), _library{ library }, _cells( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + explicit cell_view( Ntk const& ntk, std::vector const& library ) + : Ntk( ntk ), _library{ library }, _cells( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + cell_view& operator=( cell_view const& cell_ntk ) + { + Ntk::operator=( cell_ntk ); + _library = cell_ntk._library; + _cells = cell_ntk._cells; + return *this; + } + + void add_cell( node const& n, uint32_t cell_id ) + { + assert( cell_id < _library.size() ); + _cells[n] = cell_id; + } + + bool add_cell_with_check( node const& n, uint32_t cell_id ) + { + assert( cell_id < _library.size() ); + + auto const& cell = _library[cell_id]; + + if constexpr ( has_num_outputs_v && has_node_function_v ) + { + if ( Ntk::num_outputs( n ) != cell.gates.size() ) + return false; + + for ( uint32_t i = 0; i < Ntk::num_outputs( n ); ++i ) + { + if ( Ntk::node_function_pin( n, i ) != cell.gates[i].function ) + { + return false; + } + } + + _cells[n] = cell_id; + return true; + } + + if ( cell.gates.size() > 1 ) + return false; + + if ( Ntk::node_function( n ) == cell.gates[0].function ) + { + _cells[n] = cell_id; + return true; + } + + return false; + } + + void remove_cell( node const& n ) const + { + _cells.erase( n ); + } + + const standard_cell& get_cell( node const& n ) const + { + return _library[_cells[n]]; + } + + bool has_cell( node const& n ) const + { + return _cells.has( n ); + } + + unsigned int get_cell_index( node const& n ) const + { + return _cells[n]; + } + + const std::vector& get_library() const + { + return _library; + } + + double compute_area() const + { + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_cell( n ) ) + { + area += get_cell( n ).area; + } + } ); + + return area; + } + + double compute_worst_delay() const + { + topo_view ntk_topo{ *this }; + std::vector> delays( Ntk::size() ); + double worst_delay = 0; + + ntk_topo.foreach_node( [&]( auto const& n, auto ) { + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + { + delays[n].push_back( 0 ); + return true; + } + + if ( has_cell( n ) ) + { + auto const& cell = get_cell( n ); + + for ( gate const& g : cell.gates ) + { + double cell_delay = 0; + if constexpr ( has_get_output_pin_v ) + { + Ntk::foreach_fanin( n, [&]( signal const& f, auto i ) { + cell_delay = std::max( cell_delay, delays[Ntk::get_node( f )][Ntk::get_output_pin( f )] + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ); + } ); + } + else + { + Ntk::foreach_fanin( n, [&]( signal const& f, auto i ) { + cell_delay = std::max( cell_delay, delays[Ntk::get_node( f )].front() + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ); + } ); + } + delays[n].push_back( cell_delay ); + worst_delay = std::max( worst_delay, cell_delay ); + } + } + else + { + worst_delay = -1; + return false; + } + return true; + } ); + + return worst_delay; + } + + void report_stats( std::ostream& os = std::cout ) const + { + os << fmt::format( "[i] Report stats: area = {:>5.2f}; delay = {:>5.2f};\n", compute_area(), compute_worst_delay() ); + } + + void report_cells_usage( std::ostream& os = std::cout ) const + { + std::vector cells_profile( _library.size(), 0u ); + + double area = 0; + Ntk::foreach_node( [&]( node const& n, auto ) { + if ( has_cell( n ) ) + { + auto const& g = get_cell( n ); + ++cells_profile[g.id]; + area += g.area; + } + } ); + + os << "[i] Report cells usage:\n"; + + uint32_t tot_instances = 0u; + for ( auto i = 0u; i < cells_profile.size(); ++i ) + { + if ( cells_profile[i] > 0u ) + { + float tot_cell_area = cells_profile[i] * _library[i].area; + + os << fmt::format( "[i] {:<25}", _library[i].name ) + << fmt::format( "\t Instance = {:>10d}", cells_profile[i] ) + << fmt::format( "\t Area = {:>12.2f}", tot_cell_area ) + << fmt::format( " {:>8.2f} %\n", tot_cell_area / area * 100 ); + + tot_instances += cells_profile[i]; + } + } + + os << fmt::format( "[i] {:<25}", "TOTAL" ) + << fmt::format( "\t Instance = {:>10d}", tot_instances ) + << fmt::format( "\t Area = {:>12.2f} 100.00 %\n", area ); + } + +private: + std::vector _library; + node_map> _cells; +}; /* cell_view */ + +template +cell_view( T const& ) -> cell_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/choice_view.hpp b/third-party/mockturtle/include/mockturtle/views/choice_view.hpp new file mode 100644 index 00000000000..a65eb3c682b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/choice_view.hpp @@ -0,0 +1,592 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file choice_view.hpp + \brief Implement choices in network + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../networks/events.hpp" +#include "../traits.hpp" + +namespace mockturtle +{ + +struct choice_view_params +{ + bool add_choices_on_substitute{ true }; + bool update_on_add{ true }; +}; + +/*! \brief Implements choices in network + * + * Overrides the interface methods `substitute_node`, `incr_fanout_size`, + * `decr_fanout_size`, `fanout_size`. + * + * This class manages equivalent nodes keeping them saved as alternatives in + * the network. Each node belongs to an equivalence class in which a node + * is the class representative, by default the one with the lowest index. + * Equivalence classes are saved as linked lists. The `_choice_repr` vector + * associates each node to the next one in the linked list (the one closer + * to the representative). The representative is the tail and "points" at itself. + * The `_choice_phase` vector is used to save the polarity of each node in the + * class with respect to the representative. The representative uses its field + * to point to the head of the list. + * + * This view is not compatible with `fanout_view`. + * + * **Required network functions:** + * - `get_node` + * - `size` + * - `node_to_index` + * - `index_to_node` + * - `is_complemented` + * - `make_signal` + */ +template> +class choice_view +{ +}; + +template +class choice_view : public Ntk +{ +public: + choice_view( Ntk const& ntk, choice_view_params const& ps = {} ) : Ntk( ntk ) + { + (void)ps; + } +}; + +template +class choice_view : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + choice_view( choice_view_params const& ps = {} ) + : Ntk(), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + init_choice_classes(); + + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + } + + choice_view( Ntk const& ntk, choice_view_params const& ps = {} ) + : Ntk( ntk ), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + init_choice_classes(); + + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + } + + choice_view& operator=( choice_view const& choice_ntk ) + { + Ntk::operator=( choice_ntk ); + if ( this != &choice_ntk ) + { + this->_choice_repr = choice_ntk._choice_repr; + this->_choice_phase = choice_ntk._choice_phase; + this->_ps = choice_ntk._ps; + } + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + return *this; + } + + ~choice_view() + { + if ( _ps.update_on_add ) + { + Ntk::events().release_add_event( _add_event ); + } + } + + template + std::enable_if_t, void> add_choice( node const& n1, node const& n2 ) + { + add_choice( n1, Ntk::make_signal( n2 ) ); + } + + void add_choice( node const& n1, signal const& s2 ) + { + auto const n2 = Ntk::get_node( s2 ); + auto const id1 = Ntk::node_to_index( n1 ); + auto const id2 = Ntk::node_to_index( n2 ); + + if ( id1 == id2 ) + { + /* same node */ + return; + } + + auto rep1 = get_choice_representative( n1 ); + auto rep2 = get_choice_representative( n2 ); + + auto idrep1 = Ntk::node_to_index( rep1 ); + auto idrep2 = Ntk::node_to_index( rep2 ); + + if ( idrep1 == idrep2 ) + { + /* already in the same equivalence class */ + return; + } + + /* set the representative as the node with lowest index */ + if ( idrep1 > idrep2 ) + { + std::swap( rep1, rep2 ); + std::swap( idrep1, idrep2 ); + } + + /* merge the eq lists */ + bool inv = false; + if ( ( ( _choice_repr->at( id1 ) != n1 && Ntk::is_complemented( _choice_phase->at( id1 ) ) ) != Ntk::is_complemented( s2 ) ) != + ( _choice_repr->at( id2 ) != n2 && Ntk::is_complemented( _choice_phase->at( id2 ) ) ) ) + { + /* before merging, complement nodes accordingly to the new representative phase, if needed */ + invert_phases_in_class( rep2 ); + inv = true; + } + _choice_repr->at( idrep2 ) = Ntk::get_node( _choice_phase->at( idrep1 ) ); + _choice_phase->at( idrep1 ) = _choice_phase->at( idrep2 ); + /* store the right phase */ + _choice_phase->at( idrep2 ) = Ntk::make_signal( rep2 ) ^ inv; + } + + /* Set sig as an equivalence representative of n without including n in the choice list */ + void set_representative( node const& n, signal const& sig ) + { + /* TODO: TEST */ + auto nsig = Ntk::get_node( sig ); + auto repr = get_choice_representative( nsig ); + bool c = false; + + if ( repr != nsig ) + { + c = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( nsig ) ) ); + } + _choice_repr->at( Ntk::node_to_index( n ) ) = repr; + _choice_phase->at( Ntk::node_to_index( n ) ) = Ntk::make_signal( n ) ^ ( Ntk::is_complemented( sig ) ^ c ); + } + + void update_choice_representative( node const& n ) + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + if ( is_choice_representative( n ) ) + return; + + bool inv = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ); + auto repr = get_choice_representative( n ); + _choice_phase->at( Ntk::node_to_index( n ) ) = Ntk::make_signal( _choice_repr->at( Ntk::node_to_index( n ) ) ); + _choice_repr->at( Ntk::node_to_index( n ) ) = n; + _choice_repr->at( Ntk::node_to_index( repr ) ) = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( repr ) ) ); + _choice_phase->at( Ntk::node_to_index( repr ) ) = Ntk::make_signal( repr ); + if ( inv ) + { + invert_phases_in_class( n ); + } + } + + /* returns the new class representative */ + std::optional remove_choice( node const& n ) + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + auto next = Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( n ) ) ); + auto repr = Ntk::node_to_index( get_choice_representative( next ) ); + auto tail = Ntk::node_to_index( Ntk::get_node( _choice_phase->at( Ntk::node_to_index( repr ) ) ) ); + + /* if n is a representative, recompute the representative of the new class */ + if ( repr == Ntk::node_to_index( n ) && tail != Ntk::node_to_index( n ) ) + { + auto new_repr = tail; + node pred = tail; + foreach_choice( tail, [&]( auto const& g ) { + if ( Ntk::node_to_index( g ) != repr && Ntk::node_to_index( g ) < new_repr ) + { + new_repr = Ntk::node_to_index( g ); + } + if ( Ntk::node_to_index( g ) != repr && Ntk::node_to_index( _choice_repr->at( g ) ) == repr ) + { + pred = Ntk::node_to_index( g ); + } + return true; + } ); + auto const new_repr_signal = _choice_phase->at( new_repr ); + bool polarity = Ntk::is_complemented( new_repr_signal ); + if ( new_repr == pred ) + { + _choice_phase->at( new_repr ) = _choice_phase->at( repr ); + } + else + { + _choice_phase->at( new_repr ) = Ntk::make_signal( _choice_repr->at( new_repr ) ); + _choice_repr->at( pred ) = Ntk::index_to_node( tail ); + } + _choice_repr->at( new_repr ) = Ntk::index_to_node( new_repr ); + if ( polarity ) + { + invert_phases_in_class( new_repr ); + } + return new_repr_signal; + } + else if ( tail == n ) + { + _choice_phase->at( Ntk::node_to_index( repr ) ) = Ntk::make_signal( next ); + } + else + { + while ( Ntk::node_to_index( _choice_repr->at( tail ) ) != Ntk::node_to_index( n ) ) + { + tail = Ntk::node_to_index( _choice_repr->at( tail ) ); + } + _choice_repr->at( tail ) = Ntk::index_to_node( next ); + } + + _choice_repr->at( Ntk::node_to_index( n ) ) = n; + return std::nullopt; + } + + void clear_choices() + { + for ( auto i = 0u; i < Ntk::size(); i++ ) + { + _choice_repr->at( i ) = Ntk::index_to_node( i ); + _choice_phase->at( i ) = Ntk::make_signal( Ntk::index_to_node( i ) ); + } + } + + bool delete_choice_from_network( node const& n ) + { + if ( Ntk::is_dead( n ) || !is_choice( n ) ) + { + /* node is already dead or not dangling */ + return false; + } + + Ntk::take_out_node( n ); + remove_choice( n ); + return true; + } + + node get_choice_representative( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + auto rep = _choice_repr->at( Ntk::node_to_index( n ) ); + while ( Ntk::node_to_index( rep ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( rep ) ) ) ) + { + rep = _choice_repr->at( Ntk::node_to_index( rep ) ); + } + return rep; + } + + bool is_choice_representative( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + return _choice_repr->at( Ntk::node_to_index( n ) ) == n; + } + + signal get_choice_representative_signal( node const& n ) const + { + auto repr = Ntk::make_signal( get_choice_representative( n ) ); + + if ( Ntk::get_node( repr ) == n ) + { + return repr; + } + + return repr ^ Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ); + } + + template + std::enable_if_t, typename T::signal> get_choice_representative_signal( signal const& sig ) const + { + auto n = Ntk::get_node( sig ); + auto repr = get_choice_representative( n ); + + if ( repr == n ) + { + return sig; + } + + bool c = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ) != Ntk::is_complemented( sig ); + return Ntk::make_signal( repr ) ^ c; + } + + uint32_t count_choices( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + uint32_t size = 1u; + auto p = n; + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + p = _choice_repr->at( Ntk::node_to_index( p ) ); + size++; + } + p = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( p ) ) ); + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( n ) ) + { + size++; + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + return size; + } + + template + void foreach_choice( node const& n, Fn&& fn ) const + { + auto p = n; + if ( !fn( p ) ) + { + return; + } + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + p = _choice_repr->at( Ntk::node_to_index( p ) ); + if ( !fn( p ) ) + { + return; + } + } + p = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( p ) ) ); + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( n ) ) + { + if ( !fn( p ) ) + { + return; + } + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + } + + /* redefine node substitution */ + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _new] = to_substitute.top(); + to_substitute.pop(); + + if ( _ps.add_choices_on_substitute ) + { + add_choice( _old, _new ); + } + // TODO: add replace choice mode + + for ( auto idx = 1u; idx < Ntk::_storage->nodes.size(); ++idx ) + { + if ( Ntk::is_ci( idx ) || Ntk::is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = Ntk::replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + Ntk::replace_in_outputs( _old, _new ); + + // set old node as choice, reset fanout + Ntk::_storage->nodes[_old].data[0].h1 &= UINT32_C( 0xC0000000 ); + take_out_choice( _old ); + + if ( is_choice( Ntk::get_node( _new ) ) && fanout_size( Ntk::get_node( _new ) ) > 0u ) + { + take_in_choice( Ntk::get_node( _new ) ); + } + } + } + + void take_out_choice( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || Ntk::is_ci( n ) ) + return; + + auto& nobj = Ntk::_storage->nodes[n]; + set_choice_flag( n ); + Ntk::_storage->hash.erase( nobj ); + + for ( auto i = 0u; i < Ntk::fanin_size( n ); ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + /* set childrens in MFFC as choice, decrement the fanout count */ + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_choice( nobj.children[i].index ); + } + } + } + + void take_in_choice( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || Ntk::is_ci( n ) ) + return; + + auto& nobj = Ntk::_storage->nodes[n]; + reset_choice_flag( n ); + Ntk::_storage->hash[nobj] = n; + + for ( auto i = 0u; i < Ntk::fanin_size( n ); ++i ) + { + /* restore choice childrens in MFFC, increment the fanout count */ + if ( incr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_in_choice( nobj.children[i].index ); + } + } + } + + /* redefine methods for choice flag: storage h1 = dead(31), choice(30), fanout_size(29 to 0) */ + uint32_t fanout_size( node const& n ) const + { + return Ntk::_storage->nodes[n].data[0].h1 & UINT32_C( 0x3FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return Ntk::_storage->nodes[n].data[0].h1++ & UINT32_C( 0x3FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --Ntk::_storage->nodes[n].data[0].h1 & UINT32_C( 0x3FFFFFFF ); + } + + inline bool is_choice( node const& n ) const + { + return ( Ntk::_storage->nodes[n].data[0].h1 >> 30 ) & 1; + } + +private: + inline void set_choice_flag( node const& n ) const + { + Ntk::_storage->nodes[n].data[0].h1 |= UINT32_C( 0x40000000 ); + } + + inline void reset_choice_flag( node const& n ) const + { + Ntk::_storage->nodes[n].data[0].h1 &= UINT32_C( 0xBFFFFFFF ); + } + + void init_choice_classes() + { + _choice_repr = std::make_shared>( Ntk::size() ); + _choice_phase = std::make_shared>( Ntk::size() ); + // Ntk::foreach_node( [&]( auto n ) { + for ( auto i = 0u; i < Ntk::size(); i++ ) + { + _choice_repr->at( i ) = Ntk::index_to_node( i ); + _choice_phase->at( i ) = Ntk::make_signal( Ntk::index_to_node( i ) ); + } + } + + void invert_phases_in_class( node const& rep ) + { + assert( Ntk::node_to_index( rep ) < Ntk::size() ); + assert( is_choice_representative( rep ) ); + + auto p = Ntk::get_node( _choice_phase->at( rep ) ); + + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + _choice_phase->at( p ) = !_choice_phase->at( p ); + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + } + + void on_add( node const& n ) + { + if ( Ntk::size() > _choice_repr->size() ) + { + _choice_repr->push_back( n ); + _choice_phase->push_back( Ntk::make_signal( n ) ); + } + } + +private: + std::shared_ptr> _choice_repr; + std::shared_ptr> _choice_phase; + choice_view_params _ps; + std::shared_ptr::add_event_type> _add_event; +}; + +template +choice_view( T const&, choice_view_params const& ps = {} ) -> choice_view; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/views/cnf_view.hpp b/third-party/mockturtle/include/mockturtle/views/cnf_view.hpp new file mode 100644 index 00000000000..240153df8f7 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/cnf_view.hpp @@ -0,0 +1,691 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cnf_view.hpp + \brief Creates a CNF while creating a network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../algorithms/cnf.hpp" +#include "../traits.hpp" +#include "../utils/include/percy.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +struct cnf_view_params +{ + /*! \brief Write DIMACS file, whenever solve is called. */ + std::optional write_dimacs{}; + + /*! \brief Automatically update clauses when network is modified. + Only meaningful when AllowModify = true. */ + bool auto_update{ true }; +}; + +/* forward declaration */ +template +class cnf_view; + +namespace detail +{ + +template +class cnf_view_impl : public Ntk +{ +public: + cnf_view_impl( CnfView& cnf_view ) + : Ntk() + { + (void)cnf_view; + } +}; + +template +class cnf_view_impl : public Ntk +{ + friend class cnf_view; + using node = typename Ntk::node; + +public: + cnf_view_impl( CnfView& cnf_view ) + : Ntk(), cnf_view_( cnf_view ), literals_( *this ) + { + } + + cnf_view_impl( CnfView& cnf_view, Ntk& ntk ) + : Ntk( ntk ), cnf_view_( cnf_view ), literals_( *this ) + { + } + + ~cnf_view_impl() + { + } + + void init() + { + cnf_view_.solver_.add_variables( Ntk::size() ); + + /* unit clause for constants */ + bill::lit_type lit_const( 0, bill::lit_type::polarities::negative ); + cnf_view_.add_clause( lit_const ); + literals_[Ntk::get_constant( false )] = lit_const; + if ( Ntk::get_node( Ntk::get_constant( false ) ) != Ntk::get_node( Ntk::get_constant( true ) ) ) + { + literals_[Ntk::get_constant( true )] = ~lit_const; + } + + uint32_t v = 0; + Ntk::foreach_pi( [&]( auto const& n ) { + literals_[n] = bill::lit_type( ++v, bill::lit_type::polarities::positive ); + } ); + + Ntk::foreach_gate( [&]( auto const& n ) { + literals_[n] = bill::lit_type( ++v, bill::lit_type::polarities::positive ); + cnf_view_.on_add( n, false ); + } ); + } + + inline bill::var_type add_var() + { + return cnf_view_.solver_.add_variable(); + } + + /*! \brief Returns the switching literal associated to a node. */ + inline bill::lit_type switch_lit( node const& n ) const + { + assert( !Ntk::is_pi( n ) && !Ntk::is_constant( n ) && "PI and constant node are not switch-able" ); + return switches_[Ntk::node_to_index( n )]; + } + + /*! \brief Whether a node is currently activated (included in CNF). */ + inline bool is_activated( node const& n ) const + { + return switch_lit( n ).is_complemented(); + /* clauses are activated if switch literal is complemented */ + } + + /*! \brief Deactivates the clauses for a node. */ + void deactivate( node const& n ) + { + if ( is_activated( n ) ) + { + switches_[Ntk::node_to_index( n )].complement(); + } + } + + /*! \brief (Re-)activates the clauses for a node. */ + void activate( node const& n ) + { + if ( !is_activated( n ) ) + { + switches_[Ntk::node_to_index( n )].complement(); + } + } + + void on_modified( node const& n ) + { + deactivate( n ); + cnf_view_.add_clause( switch_lit( n ) ); + cnf_view_.on_add( n, false ); + /* reuse literals_[n] (so that the fanout clauses are still valid), + but create a new switches_[n] to control a new set of gate clauses */ + } + + void on_delete( node const& n ) + { + deactivate( n ); + } + +private: + CnfView& cnf_view_; + + node_map literals_; + std::vector switches_; +}; + +} /* namespace detail */ + +/*! \brief A view to connect logic network creation to SAT solving. + * + * When using this view to create a new network, it creates a CNF internally + * while nodes are added to the network. It also contains a SAT solver. The + * network can be solved by calling the `solve` method, which by default assumes + * that each output should compute `true` (an overload of the `solve` method can + * override this default behaviour and apply custom assumptions). Further, the + * methods `model_value` and `pi_vmodel_alues` can be used to access model + * values in case solving was satisfiable. Finally, methods `var` and `lit` can + * be used to access variable and literal information for nodes and signals, + * respectively, in order to add custom clauses with the `add_clause` methods. + * + * The `cnf_view` can also be wrapped around an existing network by setting the + * `AllowModify` template parameter to true. Then it also updates the CNF when + * nodes are deleted or modified. This comes with an addition cost in variable + * and clause size. + */ +template +class cnf_view : public detail::cnf_view_impl, Ntk, AllowModify, Solver> +{ + friend class detail::cnf_view_impl, Ntk, AllowModify, Solver>; + +public: + using cnf_view_impl_t = detail::cnf_view_impl, Ntk, AllowModify, Solver>; + + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + // can only be constructed as empty network + explicit cnf_view( cnf_view_params const& ps = {} ) + : cnf_view_impl_t( *this ), + ps_( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + if constexpr ( AllowModify ) + { + cnf_view_impl_t::init(); + } + else + { + const auto v = solver_.add_variable(); /* for the constant input */ + assert( v == var( Ntk::get_node( Ntk::get_constant( false ) ) ) ); + add_clause( bill::lit_type( v, bill::lit_type::polarities::negative ) ); + + if ( Ntk::get_node( Ntk::get_constant( true ) ) != Ntk::get_node( Ntk::get_constant( false ) ) ) + { + const auto v = solver_.add_variable(); /* for the constant input */ + assert( v == var( Ntk::get_node( Ntk::get_constant( true ) ) ) ); + add_clause( bill::lit_type( v, bill::lit_type::polarities::positive ) ); + } + } + + register_events(); + } + + template> + explicit cnf_view( Ntk& ntk, cnf_view_params const& ps = {} ) + : cnf_view_impl_t( *this, ntk ), + ps_( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + cnf_view_impl_t::init(); + + register_events(); + } + + ~cnf_view() + { + if ( add_event ) + { + Ntk::events().release_add_event( add_event ); + } + if ( modified_event ) + { + Ntk::events().release_modified_event( modified_event ); + } + if ( delete_event ) + { + Ntk::events().release_delete_event( delete_event ); + } + } + + signal create_pi() + { + const auto f = Ntk::create_pi(); + + const auto v = solver_.add_variable(); + + if constexpr ( AllowModify ) + { + cnf_view_impl_t::literals_.resize( bill::lit_type( 0, bill::lit_type::polarities::positive ) ); + cnf_view_impl_t::literals_[f] = bill::lit_type( v, bill::lit_type::polarities::positive ); + return f; + } + + assert( v == var( Ntk::get_node( f ) ) ); + (void)v; + + return f; + } + + /* \brief Returns the variable associated to a node. */ + inline bill::var_type var( node const& n ) const + { + if constexpr ( AllowModify ) + { + return cnf_view_impl_t::literals_[n].variable(); + } + return Ntk::node_to_index( n ); + } + + /*! \brief Returns the literal associated to a node. */ + inline bill::lit_type lit( node const& n ) const + { + return bill::lit_type( var( n ), bill::lit_type::polarities::positive ); + } + + /*! \brief Returns the literal associated to a signal. */ + template>> + inline bill::lit_type lit( signal const& f ) const + { + return bill::lit_type( var( Ntk::get_node( f ) ), Ntk::is_complemented( f ) ? bill::lit_type::polarities::negative : bill::lit_type::polarities::positive ); + } + + /*! \brief Solves the network with a set of custom assumptions. + * + * This function does not assert any primary output, unless specified + * explicitly through the assumptions. + * + * The function returns `nullopt`, if no solution can be found (due to a + * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT. + * + * \param assumptions Vector of literals to be assumped when solving + * \param limit Conflict limit (unlimited if 0) + */ + inline std::optional solve( bill::result::clause_type const& assumptions, uint32_t limit = 0 ) + { + const auto _write_dimacs = [&]( bill::result::clause_type const& assumps ) { + if ( ps_.write_dimacs ) + { + for ( const auto& a : assumps ) + { + auto l = pabc::Abc_Var2Lit( a.variable(), a.is_complemented() ); + dimacs_.add_clause( &l, &l + 1 ); + } + dimacs_.set_nr_vars( solver_.num_variables() ); +#ifdef _MSC_VER + FILE* fd = nullptr; + fopen_s( &fd, ps_.write_dimacs->c_str(), "w" ); +#else + FILE* fd = fopen( ps_.write_dimacs->c_str(), "w" ); +#endif + dimacs_.to_dimacs( fd ); + fclose( fd ); + } + }; + + const auto _solve = [&]( bill::result::clause_type const& assumps ) -> std::optional { + const auto res = solver_.solve( assumps, limit ); + + switch ( res ) + { + case bill::result::states::satisfiable: + model_ = solver_.get_model().model(); + return true; + case bill::result::states::unsatisfiable: + return false; + default: + return std::nullopt; + } + + return std::nullopt; + }; + + if constexpr ( AllowModify ) + { + bill::result::clause_type assumptions_copy = assumptions; + for ( auto i = 1u; i < cnf_view_impl_t::switches_.size(); ++i ) + { + if ( !Ntk::is_pi( Ntk::index_to_node( i ) ) ) + { + assumptions_copy.push_back( cnf_view_impl_t::switches_[i] ); + } + } + + _write_dimacs( assumptions_copy ); + return _solve( assumptions_copy ); + } + + _write_dimacs( assumptions ); + return _solve( assumptions ); + } + + /*! \brief Solves the network by asserting all primary outputs to be true + * + * The function returns `nullopt`, if no solution can be found (due to a + * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT. + * + * \param limit Conflict limit (unlimited if 0) + */ + inline std::optional solve( int limit = 0 ) + { + bill::result::clause_type assumptions; + Ntk::foreach_po( [&]( auto const& f ) { + assumptions.push_back( lit( f ) ); + } ); + return solve( assumptions, limit ); + } + + /*! \brief Return model value for a node. */ + inline bool model_value( node const& n ) const + { + return model_.at( var( n ) ) == bill::lbool_type::true_; + } + + /*! \brief Return model value for a node (takes complementation into account). */ + template>> + inline bool model_value( signal const& f ) const + { + return model_value( Ntk::get_node( f ) ) != Ntk::is_complemented( f ); + } + + /* \brief Returns all model values for all primary inputs. */ + std::vector pi_model_values() + { + std::vector values( Ntk::num_pis() ); + Ntk::foreach_pi( [&]( auto const& n, auto i ) { + values[i] = model_value( n ); + } ); + return values; + } + + /*! \brief Blocks last model for primary input values. */ + void block() + { + bill::result::clause_type blocking_clause; + Ntk::foreach_pi( [&]( auto const& n ) { + blocking_clause.push_back( bill::lit_type( var( n ), model_value( n ) ? bill::lit_type::polarities::negative : bill::lit_type::polarities::positive ) ); + } ); + add_clause( blocking_clause ); + } + + /*! \brief Number of variables. */ + inline uint32_t num_vars() const + { + return solver_.num_variables(); + } + + /*! \brief Number of clauses. */ + inline uint32_t num_clauses() const + { + return solver_.num_clauses(); + } + + /*! \brief Adds a clause to the solver. */ + void add_clause( bill::result::clause_type const& clause ) + { + if ( ps_.write_dimacs ) + { + std::vector lits; + for ( auto c : clause ) + { + lits.push_back( pabc::Abc_Var2Lit( c.variable(), c.is_complemented() ) ); + } + dimacs_.add_clause( &lits[0], &lits[0] + lits.size() ); + } + solver_.add_clause( clause ); + } + + /*! \brief Adds a clause from signals to the solver. */ + void add_clause( std::vector const& clause ) + { + bill::result::clause_type lits; + std::transform( clause.begin(), clause.end(), std::back_inserter( lits ), [&]( auto const& s ) { return lit( s ); } ); + add_clause( lits ); + } + + /*! \brief Adds a clause to the solver. + * + * Entries are either all literals or network signals. + */ + template...>, + std::conjunction...>>>> + void add_clause( Lit... lits ) + { + if constexpr ( std::conjunction_v...> ) + { + add_clause( bill::result::clause_type{ { lits... } } ); + } + else + { + add_clause( bill::result::clause_type{ { lit( lits )... } } ); + } + } + +private: + void register_events() + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + modified_event = Ntk::events().register_modified_event( [this]( auto const& n, auto const& previous ) { + (void)previous; + if constexpr ( AllowModify ) + { + if ( ps_.auto_update ) + { + cnf_view_impl_t::on_modified( n ); + } + return; + } + + (void)n; + (void)this; + assert( false && "nodes should not be modified in cnf_view" ); + std::abort(); + } ); + delete_event = Ntk::events().register_delete_event( [this]( auto const& n ) { + if constexpr ( AllowModify ) + { + if ( ps_.auto_update ) + { + cnf_view_impl_t::on_delete( n ); + } + return; + } + + (void)n; + (void)this; + assert( false && "nodes should not be deleted in cnf_view" ); + std::abort(); + } ); + } + + void on_add( node const& n, bool add_var = true ) /* add_var is only used when AllowModify = true */ + { + bill::lit_type node_lit; + bill::lit_type switch_lit; + + if constexpr ( AllowModify ) + { + if ( add_var ) + { + node_lit = bill::lit_type( solver_.add_variable(), bill::lit_type::polarities::positive ); + cnf_view_impl_t::literals_.resize(); + cnf_view_impl_t::literals_[n] = node_lit; + } + else + { + node_lit = cnf_view_impl_t::literals_[n]; + } + + switch_lit = bill::lit_type( solver_.add_variable(), bill::lit_type::polarities::positive ); + cnf_view_impl_t::switches_.resize( Ntk::size() ); + cnf_view_impl_t::switches_[Ntk::node_to_index( n )] = ~switch_lit; + } + else + { + (void)add_var; + const auto v = solver_.add_variable(); + assert( v == var( n ) ); + (void)v; + + node_lit = lit( Ntk::make_signal( n ) ); + } + + const auto _add_clause = [&]( bill::result::clause_type const& clause ) { + if constexpr ( AllowModify ) + { + bill::result::clause_type clause_ = clause; + clause_.push_back( switch_lit ); + add_clause( clause_ ); + } + else + { + add_clause( clause ); + } + }; + + bill::result::clause_type child_lits; + Ntk::foreach_fanin( n, [&]( auto const& f ) { + child_lits.push_back( lit( f ) ); + } ); + + if constexpr ( has_is_and_v ) + { + if ( Ntk::is_and( n ) ) + { + detail::on_and( node_lit, child_lits[0], child_lits[1], _add_clause ); + return; + } + } + + if constexpr ( has_is_or_v ) + { + if ( Ntk::is_or( n ) ) + { + detail::on_or( node_lit, child_lits[0], child_lits[1], _add_clause ); + return; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( Ntk::is_xor( n ) ) + { + detail::on_xor( node_lit, child_lits[0], child_lits[1], _add_clause ); + return; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( Ntk::is_maj( n ) ) + { + detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause ); + return; + } + } + + if constexpr ( has_is_ite_v ) + { + if ( Ntk::is_ite( n ) ) + { + detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause ); + return; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( Ntk::is_xor3( n ) ) + { + detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause ); + return; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( Ntk::is_nary_and( n ) ) + { + fmt::print( stderr, "[e] nary-AND not yet supported in generate_cnf" ); + std::abort(); + return; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( Ntk::is_nary_or( n ) ) + { + fmt::print( stderr, "[e] nary-OR not yet supported in generate_cnf" ); + std::abort(); + return; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( Ntk::is_nary_xor( n ) ) + { + fmt::print( stderr, "[e] nary-XOR not yet supported in generate_cnf" ); + std::abort(); + return; + } + } + + detail::on_function( node_lit, child_lits, Ntk::node_function( n ), _add_clause ); + } + +private: + bill::solver solver_; + bill::result::model_type model_; + percy::cnf_formula dimacs_; + + cnf_view_params ps_; + + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +template +cnf_view( T const& ) -> cnf_view; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/color_view.hpp b/third-party/mockturtle/include/mockturtle/views/color_view.hpp new file mode 100644 index 00000000000..a9c7cc0156f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/color_view.hpp @@ -0,0 +1,244 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file color_view.hpp + \brief Manager view for traversal IDs, called colors + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +namespace mockturtle +{ + +/*!\brief Manager view for traversal IDs (in-place storage). + * + * Traversal IDs, called colors, are unsigned integers that can be + * assigned to nodes. The corresponding values are stored in-place in + * the flags of the underlying of the network. + */ +template +class color_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit color_view( Ntk const& ntk ) + : Ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + } + + /*! \brief Returns a new color and increases the current color */ + uint32_t new_color() const + { + return ++this->_storage->trav_id; + // return ++value; + } + + /*! \brief Returns the current color */ + uint32_t current_color() const + { + return this->_storage->trav_id; + // return value; + } + + /*! \brief Assigns all nodes to `color` */ + void clear_colors( uint32_t color = 0 ) const + { + std::for_each( this->_storage->nodes.begin(), this->_storage->nodes.end(), + [color]( auto& n ) { n.data[1].h1 = color; } ); + } + + /*! \brief Returns the color of a node */ + auto color( node const& n ) const + { + return this->_storage->nodes[n].data[1].h1; + } + + /*! \brief Returns the color of a node */ + template>> + auto color( signal const& n ) const + { + return this->_storage->nodes[this->get_node( n )].data[1].h1; + } + + /*! \brief Assigns the current color to a node */ + void paint( node const& n ) const + { + this->_storage->nodes[n].data[1].h1 = current_color(); + } + + /*! \brief Assigns `color` to a node */ + void paint( node const& n, uint32_t color ) const + { + this->_storage->nodes[n].data[1].h1 = color; + } + + /*! \brief Copies the color from `other` to `n` */ + void paint( node const& n, node const& other ) const + { + this->_storage->nodes[n].data[1].h1 = color( other ); + } + + /*! \brief Evaluates a predicate on the color of a node */ + template + bool eval_color( node const& n, Pred&& pred ) const + { + return pred( color( n ) ); + } + + /*! \brief Evaluates a predicate on the colors of two nodes */ + template + bool eval_color( node const& a, node const& b, Pred&& pred ) const + { + return pred( color( a ), color( b ) ); + } + + /*! \brief Evaluates a predicate on the colors of the fanins of a node */ + template + bool eval_fanins_color( node const& n, Pred&& pred ) const + { + bool result = true; + this->foreach_fanin( n, [&]( signal const& fi ) { + if ( !pred( color( this->get_node( fi ) ) ) ) + { + result = false; + return false; + } + return true; + } ); + return result; + } + +protected: + // mutable uint32_t value{0}; +}; /* color_view */ + +/*!\brief Manager view for traversal IDs (out-of-place storage). + * + * Traversal IDs, called colors, are unsigned integers that can be + * assigned to nodes. The corresponding values are stored + * out-of-place in this view. + */ +template +class out_of_place_color_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit out_of_place_color_view( Ntk const& ntk ) + : Ntk( ntk ), values( ntk.size() ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + } + + uint32_t new_color() const + { + return ++value; + } + + uint32_t current_color() const + { + return value; + } + + void clear_colors( uint32_t color = 0 ) const + { + std::for_each( std::begin( values ), std::end( values ), + [color]( auto& v ) { v = color; } ); + } + + auto color( node const& n ) const + { + return values[n]; + } + + template>> + auto color( signal const& n ) const + { + return values[this->get_node( n )]; + } + + void paint( node const& n ) const + { + values[n] = value; + } + + void paint( node const& n, uint32_t color ) const + { + values[n] = color; + } + + void paint( node const& n, node const& other ) const + { + values[n] = values[other]; + } + + /*! \brief Evaluates a predicate on the color of a node */ + template + bool eval_color( node const& n, Pred&& pred ) const + { + return pred( color( n ) ); + } + + /*! \brief Evaluates a predicate on the colors of two nodes */ + template + bool eval_color( node const& a, node const& b, Pred&& pred ) const + { + return pred( color( a ), color( b ) ); + } + + /*! \brief Evaluates a predicate on the colors of the fanins of a node */ + template + bool eval_fanins_color( node const& n, Pred&& pred ) const + { + bool result = true; + this->foreach_fanin( n, [&]( signal const& fi ) { + if ( !pred( color( this->get_node( fi ) ) ) ) + { + result = false; + return false; + } + return true; + } ); + return result; + } + +protected: + mutable std::vector values; + mutable uint32_t value{ 0 }; +}; /* out_of_place_color_view */ + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/cost_view.hpp b/third-party/mockturtle/include/mockturtle/views/cost_view.hpp new file mode 100644 index 00000000000..eb6be69b705 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/cost_view.hpp @@ -0,0 +1,281 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cost_view.hpp + \brief Implements various cost estimation methods for a network + + \author Hanyu Wang +*/ + +#pragma once + +#include "../networks/events.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/recursive_cost_functions.hpp" +#include "immutable_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Implements `get_cost` methods for networks. + * + * This view computes the cost of the entire network, a subnetwork, and + * also fanin cone of a single node. It maintains the context of each + * node, which is the aggregated variables that affect the cost a node. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `visited` + * - `set_visited` + * - `foreach_fanin` + * - `foreach_po` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + xag_network xag = ...; + + // create a cost view on the network, for example size cost + auto viewed = cost_view( xag, xag_size_cost_function() ); + + // print size + std::cout << "size: " << viewed.get_cost() << "\n"; + \endverbatim + */ +template +class cost_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using context_t = typename RecCostFn::context_t; + + explicit cost_view( RecCostFn const& cost_fn = {} ) + : Ntk(), + _cost_fn( cost_fn ), + context( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + explicit cost_view( Ntk const& ntk, RecCostFn const& cost_fn = {} ) + : Ntk( ntk ), + _cost_fn( cost_fn ), + context( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + + update_cost(); + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + explicit cost_view( cost_view const& other ) + : Ntk( other ), + _cost_fn( other._cost_fn ), + context( other.context ) + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + cost_view& operator=( cost_view const& other ) + { + /* delete the event of this network */ + Ntk::events().release_add_event( add_event ); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + context = other.context; + _cost_fn = other._cost_fn; + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + + return *this; + } + + ~cost_view() + { + Ntk::events().release_add_event( add_event ); + } + + /*! \brief Returns the context of node n */ + context_t get_context( node const& n ) const + { + return context[n]; + } + + /*! \brief Assigns the context of node n */ + void set_context( node const& n, context_t cost_val ) + { + context[n] = cost_val; + this->set_visited( n, this->trav_id() ); + } + + /*! \brief Returns the cost of the entire network */ + uint32_t get_cost() const + { + return _cost; + } + + /*! \brief Returns the cost of node n's fanin cone */ + uint32_t get_cost( node const& n ) + { + uint32_t _c = 0u; + this->incr_trav_id(); + compute_cost( n, _c ); + return _c; + } + + /*! \brief Returns the cost between node n and divs */ + uint32_t get_cost( node const& n, std::vector const& divs ) + { + uint32_t _c = 0u; + this->incr_trav_id(); + for ( signal s : divs ) + { + this->set_visited( this->get_node( s ), this->trav_id() ); + } + compute_cost( n, _c ); + return _c; + } + + /*! \brief Updates the context and cost of the entire network */ + void update_cost() + { + context.reset( context_t{} ); + this->incr_trav_id(); + compute_cost(); + } + + void on_add( node const& n ) + { + context.resize(); + + std::vector fanin_costs; + this->foreach_fanin( n, [&]( auto const& f ) { + fanin_costs.emplace_back( context[this->get_node( f )] ); + } ); + context[n] = _cost_fn( *this, n, fanin_costs ); + _cost_fn( *this, n, _cost, context[n] ); + } + + /*! \brief Creates a PI with context assigned */ + signal create_pi( context_t pi_cotext ) + { + signal s = Ntk::create_pi(); + context.resize(); + set_context( this->get_node( s ), pi_cotext ); + return s; + } + + signal create_pi() + { + signal s = Ntk::create_pi(); + context.resize(); + return s; + } + + void create_po( signal const& f ) + { + Ntk::create_po( f ); + } + +private: + context_t compute_cost( node const& n, uint32_t& _c ) + { + context_t _context{}; + if ( this->visited( n ) == this->trav_id() ) + { + _context = context[n]; // do not update context + _cost_fn( *this, n, _c, _context ); + return _context; + } + if ( this->is_constant( n ) ) + { + _context = context[n] = context_t{}; + } + else if ( this->is_pi( n ) ) + { + _context = context[n] = _cost_fn( *this, n ); + } + else + { + std::vector fanin_costs; + this->foreach_fanin( n, [&]( auto const& f ) { + fanin_costs.emplace_back( compute_cost( this->get_node( f ), _c ) ); + } ); + _context = context[n] = _cost_fn( *this, n, fanin_costs ); + } + _cost_fn( *this, n, _c, _context ); + this->set_visited( n, this->trav_id() ); + return _context; + } + void compute_cost() + { + _cost = 0u; /* must define the zero initialization */ + this->foreach_po( [&]( auto const& f ) { + compute_cost( this->get_node( f ), _cost ); + } ); + } + + node_map context; + uint32_t _cost; + RecCostFn _cost_fn; + + std::shared_ptr::add_event_type> add_event; +}; + +template +cost_view( T const& ) -> cost_view; + +template +cost_view( T const&, RecCostFn const& ) -> cost_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/cut_view.hpp b/third-party/mockturtle/include/mockturtle/views/cut_view.hpp new file mode 100644 index 00000000000..eb0b57ff4ad --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/cut_view.hpp @@ -0,0 +1,232 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cut_view.hpp + \brief Implements an isolated view on a single cut in a network + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "immutable_view.hpp" + +namespace mockturtle +{ + +/*! \brief Implements an isolated view on a single cut in a network. + * + * This view can create a network from a single cut in a largest network. This + * cut has a single output `root` and set of `leaves`. The view reimplements + * the methods `size`, `num_pis`, `num_pos`, `foreach_pi`, `foreach_po`, + * `foreach_node`, `foreach_gate`, `is_pi`, `node_to_index`, and + * `index_to_node`. + * + * This view assumes that all nodes' visited flags are set 0 before creating + * the view. The view guarantees that all the nodes in the view will have a 0 + * visited flag after the construction. + * + * **Required network functions:** + * - `set_visited` + * - `visited` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `make_signal` + */ +template +class cut_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr bool is_topologically_sorted = true; + +public: + explicit cut_view( Ntk const& ntk, std::vector const& leaves, signal const& root ) + : immutable_view( ntk ), _root( root ) + { + construct( leaves ); + } + + template>> + explicit cut_view( Ntk const& ntk, std::vector const& leaves, signal const& root ) + : immutable_view( ntk ), _root( root ) + { + construct( leaves ); + } + +private: + template + void construct( std::vector const& leaves ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( std::is_same_v || std::is_same_v, "leaves must be vector of either node or signal" ); + + this->incr_trav_id(); + + /* constants */ + add_constants(); + + /* primary inputs */ + for ( auto const& leaf : leaves ) + { + if constexpr ( std::is_same_v ) + { + add_leaf( leaf ); + } + else + { + add_leaf( this->get_node( leaf ) ); + } + } + + traverse( this->get_node( _root ) ); + + /* restore visited */ + // for ( auto const& n : _nodes ) + //{ + // this->set_visited( n, 0 ); + // } + } + +public: + inline auto size() const { return _nodes.size(); } + inline auto num_pis() const { return _num_leaves; } + inline auto num_pos() const { return 1; } + inline auto num_gates() const { return _nodes.size() - _num_leaves - _num_constants; } + + inline auto node_to_index( const node& n ) const { return _node_to_index.at( n ); } + inline auto index_to_node( uint32_t index ) const { return _nodes[index]; } + + template + void foreach_po( Fn&& fn ) const + { + std::vector roots{ { _root } }; + detail::foreach_element( roots.begin(), roots.end(), fn ); + } + + inline bool is_pi( node const& pi ) const + { + const auto beg = _nodes.begin() + _num_constants; + return std::find( beg, beg + _num_leaves, pi ) != beg + _num_leaves; + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _nodes.begin() + _num_constants, _nodes.begin() + _num_constants + _num_leaves, fn ); + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( _nodes.begin(), _nodes.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( _nodes.begin() + _num_constants + _num_leaves, _nodes.end(), fn ); + } + +private: + inline void add_constants() + { + add_node( this->get_node( this->get_constant( false ) ) ); + this->set_visited( this->get_node( this->get_constant( false ) ), this->trav_id() ); + if ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ) + { + add_node( this->get_node( this->get_constant( true ) ) ); + this->set_visited( this->get_node( this->get_constant( true ) ), this->trav_id() ); + ++_num_constants; + } + } + + inline void add_leaf( node const& leaf ) + { + if ( this->visited( leaf ) == this->trav_id() ) + return; + + add_node( leaf ); + this->set_visited( leaf, this->trav_id() ); + ++_num_leaves; + } + + inline void add_node( node const& n ) + { + _node_to_index[n] = static_cast( _nodes.size() ); + _nodes.push_back( n ); + } + + void traverse( node const& n ) + { + if ( this->visited( n ) == this->trav_id() ) + return; + + this->foreach_fanin( n, [&]( const auto& f ) { + traverse( this->get_node( f ) ); + } ); + + add_node( n ); + this->set_visited( n, this->trav_id() ); + } + +public: + unsigned _num_constants{ 1 }; + unsigned _num_leaves{ 0 }; + std::vector _nodes; + phmap::flat_hash_map _node_to_index; + signal _root; +}; + +template +cut_view( T const&, std::vector> const&, signal const& ) -> cut_view; + +template>> +cut_view( T const&, std::vector> const&, signal const& ) -> cut_view; + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/views/depth_view.hpp b/third-party/mockturtle/include/mockturtle/views/depth_view.hpp new file mode 100644 index 00000000000..d8c11a24abf --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/depth_view.hpp @@ -0,0 +1,363 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file depth_view.hpp + \brief Implements depth and level for a network + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/events.hpp" +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "immutable_view.hpp" + +#include +#include + +namespace mockturtle +{ + +struct depth_view_params +{ + /*! \brief Take complemented edges into account for depth computation. */ + bool count_complements{ false }; + + /*! \brief Whether PIs have costs. */ + bool pi_cost{ false }; +}; + +/*! \brief Implements `depth` and `level` methods for networks. + * + * This view computes the level of each node and also the depth of + * the network. It implements the network interface methods + * `level` and `depth`. The levels are computed at construction + * and can be recomputed by calling the `update_levels` method. + * + * It also automatically updates levels, and depth when creating nodes or + * creating a PO on a depth_view, however, it does not update the information, + * when modifying or deleting nodes, neither will the critical paths be + * recalculated (due to efficiency reasons). In order to recalculate levels, + * depth, and critical paths, one can call `update_levels` instead. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `visited` + * - `set_visited` + * - `foreach_fanin` + * - `foreach_po` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // create a depth view on the network + depth_view aig_depth{aig}; + + // print depth + std::cout << "Depth: " << aig_depth.depth() << "\n"; + \endverbatim + */ +template, bool has_depth_interface = has_depth_v&& has_level_v&& has_update_levels_v> +class depth_view +{ +}; + +template +class depth_view : public Ntk +{ +public: + depth_view( Ntk const& ntk, depth_view_params const& ps = {} ) : Ntk( ntk ) + { + (void)ps; + } +}; + +template +class depth_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit depth_view( NodeCostFn const& cost_fn = {}, depth_view_params const& ps = {} ) + : Ntk(), _ps( ps ), _levels( *this ), _crit_path( *this ), _cost_fn( cost_fn ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Standard constructor. + * + * \param ntk Base network + */ + explicit depth_view( Ntk const& ntk, NodeCostFn const& cost_fn = {}, depth_view_params const& ps = {} ) + : Ntk( ntk ), _ps( ps ), _levels( ntk ), _crit_path( ntk ), _cost_fn( cost_fn ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + update_levels(); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Copy constructor. */ + explicit depth_view( depth_view const& other ) + : Ntk( other ), _ps( other._ps ), _levels( other._levels ), _crit_path( other._crit_path ), _depth( other._depth ), _cost_fn( other._cost_fn ) + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + depth_view& operator=( depth_view const& other ) + { + /* delete the event of this network */ + Ntk::events().release_add_event( add_event ); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + _ps = other._ps; + _levels = other._levels; + _crit_path = other._crit_path; + _depth = other._depth; + _cost_fn = other._cost_fn; + + /* register new event in the other network */ + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + + return *this; + } + + ~depth_view() + { + Ntk::events().release_add_event( add_event ); + } + + uint32_t depth() const + { + return _depth; + } + + uint32_t level( node const& n ) const + { + return _levels[n]; + } + + bool is_on_critical_path( node const& n ) const + { + return _crit_path[n]; + } + + void set_level( node const& n, uint32_t level ) + { + _levels[n] = level; + } + + void set_depth( uint32_t level ) + { + _depth = level; + } + + void update_levels() + { + _levels.reset( 0 ); + _crit_path.reset( false ); + + this->incr_trav_id(); + compute_levels(); + } + + void resize_levels() + { + _levels.resize(); + } + + void create_po( signal const& f ) + { + Ntk::create_po( f ); + _depth = std::max( _depth, _levels[f] ); + } + +private: + uint32_t compute_levels( node const& n ) + { + if ( this->visited( n ) == this->trav_id() ) + { + return _levels[n]; + } + this->set_visited( n, this->trav_id() ); + + if ( this->is_constant( n ) ) + { + return _levels[n] = 0; + } + if ( this->is_ci( n ) ) + { + assert( !_ps.pi_cost || _cost_fn( *this, n ) >= 1 ); + return _levels[n] = _ps.pi_cost ? _cost_fn( *this, n ) - 1 : 0; + } + + uint32_t level{ 0 }; + this->foreach_fanin( n, [&]( auto const& f ) { + auto clevel = compute_levels( this->get_node( f ) ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + level = std::max( level, clevel ); + } ); + + return _levels[n] = level + _cost_fn( *this, n ); + } + + void compute_levels() + { + _depth = 0; + this->foreach_po( [&]( auto const& f ) { + auto clevel = compute_levels( this->get_node( f ) ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + _depth = std::max( _depth, clevel ); + } ); + + if constexpr ( has_foreach_ri_v ) + { + this->foreach_ri( [&]( auto const& f ) { + auto clevel = compute_levels( this->get_node( f ) ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + _depth = std::max( _depth, clevel ); + } ); + } + + this->foreach_po( [&]( auto const& f ) { + const auto n = this->get_node( f ); + if ( _levels[n] == _depth ) + { + set_critical_path( n ); + } + } ); + + if constexpr ( has_foreach_ri_v ) + { + this->foreach_ri( [&]( auto const& f ) { + const auto n = this->get_node( f ); + if ( _levels[n] == _depth ) + { + set_critical_path( n ); + } + } ); + } + } + + void set_critical_path( node const& n ) + { + _crit_path[n] = true; + if ( !this->is_constant( n ) && !( _ps.pi_cost && this->is_pi( n ) ) ) + { + const auto lvl = _levels[n]; + this->foreach_fanin( n, [&]( auto const& f ) { + const auto cn = this->get_node( f ); + auto offset = _cost_fn( *this, n ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + offset++; + } + if ( _levels[cn] + offset == lvl && !_crit_path[cn] ) + { + set_critical_path( cn ); + } + } ); + } + } + + void on_add( node const& n ) + { + _levels.resize(); + + uint32_t level{ 0 }; + this->foreach_fanin( n, [&]( auto const& f ) { + auto clevel = _levels[f]; + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + level = std::max( level, clevel ); + } ); + + _levels[n] = level + _cost_fn( *this, n ); + } + + depth_view_params _ps; + node_map _levels; + node_map _crit_path; + uint32_t _depth{}; + NodeCostFn _cost_fn; + + std::shared_ptr::add_event_type> add_event; +}; + +template +depth_view( T const& ) -> depth_view; + +template> +depth_view( T const&, NodeCostFn const&, depth_view_params const& ) -> depth_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/dont_care_view.hpp b/third-party/mockturtle/include/mockturtle/views/dont_care_view.hpp new file mode 100644 index 00000000000..f08d605f496 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/dont_care_view.hpp @@ -0,0 +1,481 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dont_care_view.hpp + \brief Implements methods to store external don't-cares + + \author Siang-Yun Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../algorithms/simulation.hpp" +#include "../algorithms/cnf.hpp" +#include "../utils/node_map.hpp" +#include "../utils/window_utils.hpp" +#include "../views/color_view.hpp" +#include "../views/window_view.hpp" + +#include + +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief A view holding external don't care information of a network + * + * This view helps storing and managing external don't care information. + * There are two types of external don't cares that may be given together + * or independently: + * + * External controllability don't cares (EXCDCs) are primary input patterns + * that will never happen or can be ignored. They are given as another + * network `cdc_ntk` having the same number of PIs as the main network and + * one PO. An input assignment making `cdc_ntk` output 1 is an EXCDC. + * + * External observability don't cares (EXODCs) are conditions for one or + * more primary outputs under which their values can be altered. EXODCs may + * be given for a PO as value combinations of other POs (for example, “the + * second PO is don't care whenever the first PO is 1”). They may also be + * given as pairs of observably equivalent PO values (for example, “the output + * values 01 and 10 are considered equivalent and interchangable”). + * + * By wrapping a network with this view and giving some external don't care + * conditions, some algorithms supporting the consideration of external don't + * cares can make use of them. Currently, only `sim_resubstitution` + * (which makes use of `circuit_validator`) and `equivalence_checking_bill` + * support external don't cares. + * + * \tparam hasEXCDC Enables interfaces and data structure holding external + * controllability don't cares (external don't cares at the primary inputs) + * \tparam hasEXODC Enables interfaces and data structure holding external + * observability don't cares (external dont cares at the primary outputs) + */ +template +class dont_care_view; + +namespace detail +{ + +/*! \brief A manager to classify bit-strings into equivalence classes + * + * This data structure holds and manages equivalence classes of + * bit-strings of the same length (i.e. complete or partial binary + * truth tables). + * The three properties of an equivalence relation are maintained: + * - Reflexive (x = x) + * - Symmetric (if x = y then y = x) + * - Transitive (if x = y and y = z then x = z) + * + * In the current implementation, the big-string length may not be + * larger than 31. + */ +class equivalence_classes_mgr +{ +public: + equivalence_classes_mgr() {} + + equivalence_classes_mgr( uint32_t num_bits ) : _num_bits( num_bits ) + { + assert( num_bits < 32 ); + uint32_t max_val = 1u << num_bits; + _classes.resize( max_val ); + for ( uint32_t i = 0u; i < max_val; ++i ) + { + _classes[i] = i; + } + } + + equivalence_classes_mgr& operator=( equivalence_classes_mgr const& other ) + { + _num_bits = other._num_bits; + _classes = other._classes; + return *this; + } + + void set_equivalent( uint32_t const& a, uint32_t const& b ) + { + uint32_t repr_class = _classes.at( a ); + uint32_t to_be_replaced = _classes.at( b ); + for ( auto i = 0u; i < _classes.size(); ++i ) + { + if ( _classes[i] == to_be_replaced ) + _classes[i] = repr_class; + } + } + + /*! \brief Set two bit strings to be equivalent. */ + void set_equivalent( std::vector const& a, std::vector const& b ) + { + set_equivalent( vector_bool_to_uint32( a ), vector_bool_to_uint32( b ) ); + } + + /*! \brief Check equivalence of fully-assigned bit-strings */ + bool are_equivalent( uint32_t const& a, uint32_t const& b ) const + { + return _classes.at( a ) == _classes.at( b ); + } + + /*! \brief Check equivalence of fully-assigned bit-strings */ + bool are_equivalent( std::vector const& a, std::vector const& b ) const + { + return are_equivalent( vector_bool_to_uint32( a ), vector_bool_to_uint32( b ) ); + } + + /*! \brief Check equivalence of partially-assigned bit-strings + * + * The don't-care bit positions in the two cubes should be the same. + * Two cubes are equivalent if for all possible assignments to the + * don't-care bits, they are always equivalent. + */ + bool are_equivalent( kitty::cube const& a, kitty::cube const& b ) const + { + assert( a._mask == b._mask && "The don't-care bit positions in the two cubes should be the same." ); + return are_equivalent_rec( a, b, 0 ); + } + + uint32_t num_classes() const + { + std::set unique_ids; + for ( auto const& id : _classes ) + { + unique_ids.insert( id ); + } + return unique_ids.size(); + } + + template + void foreach_class( Fn&& fn ) const + { + std::unordered_map> class2pats; + for ( auto pat = 0u; pat < _classes.size(); ++pat ) + { + auto const& id = _classes[pat]; + class2pats.try_emplace( id ); + class2pats[id].emplace_back( pat ); + } + + for ( auto const& p : class2pats ) + { + if ( !fn( p.second ) ) + break; + } + } + +private: + bool are_equivalent_rec( kitty::cube const& a, kitty::cube const& b, uint32_t i ) const + { + if ( i == _num_bits ) + { + return are_equivalent( cube_to_uint32( a ), cube_to_uint32( b ) ); + } + + if ( a.get_mask( i ) ) + { + return are_equivalent_rec( a, b, i + 1 ); + } + else + { + kitty::cube a0 = a; + a0.set_mask( i ); + kitty::cube b0 = b; + b0.set_mask( i ); + if ( !are_equivalent_rec( a0, b0, i + 1 ) ) + return false; + a0.set_bit( i ); + b0.set_bit( i ); + return are_equivalent_rec( a0, b0, i + 1 ); + } + } + + uint32_t vector_bool_to_uint32( std::vector const& vec ) const + { + assert( vec.size() == _num_bits ); + uint32_t res{0u}; + for ( auto i = 0u; i < _num_bits; ++i ) + { + if ( vec[i] ) + res |= 1u << i; + } + return res; + } + + uint32_t cube_to_uint32( kitty::cube const& c ) const + { + assert( c.num_literals() == _num_bits ); // fully assigned + return c._bits; + } + +private: + uint32_t _num_bits; + std::vector _classes; +}; // equivalence_classes_mgr + +template +class dont_care_view_impl : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + template> + dont_care_view_impl( Ntk const& ntk ) + : Ntk( ntk ) + {} + + template> + dont_care_view_impl( Ntk const& ntk, Ntk const& cdc_ntk ) + : Ntk( ntk ), _excdc( cdc_ntk ) + { + assert( cdc_ntk.num_pis() == ntk.num_pis() ); + assert( cdc_ntk.num_pos() == 1 ); + } + + /*! \brief Checks whether an input pattern is EXCDC + * + * \param pattern The PI value combination to be checked + * \return Whether `pattern` is EXCDC + */ + template> + bool pattern_is_EXCDC( std::vector const& pattern ) const + { + assert( pattern.size() == this->num_pis() ); + + default_simulator sim( pattern ); + auto const vals = simulate( _excdc, sim ); + return vals[0]; + } + + template> + void add_EXCDC_clauses( solver_t& solver ) const + { + using add_clause_fn_t = std::function const& )>; + add_clause_fn_t const add_clause_fn = [&]( auto const& clause ) { solver.add_clause( clause ); }; + + // topological order of the gates in _excdc is assumed + node_map cdc_lits( _excdc ); + cdc_lits[_excdc.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( _excdc.get_node( _excdc.get_constant( false ) ) != _excdc.get_node( _excdc.get_constant( true ) ) ) + { + cdc_lits[_excdc.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + _excdc.foreach_pi( [&]( auto const& n, auto i ) { + cdc_lits[n] = bill::lit_type( i + 1, bill::lit_type::polarities::positive ); + } ); + + _excdc.foreach_gate( [&]( auto const& n ){ + cdc_lits[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + }); + + auto out_lits = generate_cnf( _excdc, add_clause_fn, cdc_lits ); + solver.add_clause( {~out_lits[0]} ); + } + +private: + Ntk _excdc; +}; /* dont_care_view_impl */ +} // namespace detail + +template +class dont_care_view : public detail::dont_care_view_impl +{ +public: + static constexpr bool has_EXCDC_interface = hasEXCDC; + static constexpr bool has_EXODC_interface = false; + +public: + template> + dont_care_view( Ntk const& ntk ) + : detail::dont_care_view_impl( ntk ) + {} + + template> + dont_care_view( Ntk const& ntk, Ntk const& cdc_ntk ) + : detail::dont_care_view_impl( ntk, cdc_ntk ) + {} +}; + +template +class dont_care_view : public detail::dont_care_view_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr bool has_EXCDC_interface = hasEXCDC; + static constexpr bool has_EXODC_interface = true; + +public: + /*! \brief Constructor when no EXCDC is provided + * + * \param ntk The main network + */ + template> + dont_care_view( Ntk const& ntk ) + : detail::dont_care_view_impl( ntk ), _exoec( ntk.num_pos() ) + {} + + /*! \brief Constructor when EXCDC is provided + * + * \param ntk The main network + * \param cdc_ntk The network representing EXCDC conditions, having the same + * number of PIs as `ntk` and one PO + */ + template> + dont_care_view( Ntk const& ntk, Ntk const& cdc_ntk ) + : detail::dont_care_view_impl( ntk, cdc_ntk ), _exoec( ntk.num_pos() ) + {} + + /*! \brief Adds an EXODC condition for a PO in terms of other POs + * + * \param cond Condition in terms of other POs for the concerned PO to be don't care + * \param po_id Index of the concerned PO + */ + void add_EXODC( kitty::cube const& cond, uint32_t po_id ) + { + cond.foreach_minterm( this->num_pos(), [&]( kitty::cube const& c ){ + assert( c.num_literals() == this->num_pos() ); + if ( c.get_bit( po_id ) ) return true; + kitty::cube c2 = c; + c2.set_bit( po_id ); + _exoec.set_equivalent( c._bits, c2._bits ); + return true; + }); + } + + /*! \brief Adds a pair of PO values that are considered observably equivalent + * + * \param pat1 The first PO value combination + * \param pat2 The second PO value combination + */ + void add_EXOEC_pair( std::vector const& pat1, std::vector const& pat2 ) + { + _exoec.set_equivalent( pat1, pat2 ); + } + + /*! \brief Checks whether a pair of PO value assignments are observably equivalent + * + * \param pat1 The first PO value combination + * \param pat2 The second PO value combination + * \return Whether `pat1` and `pat2` are observably equivalent + */ + bool are_observably_equivalent( std::vector const& pat1, std::vector const& pat2 ) const + { + return _exoec.are_equivalent( pat1, pat2 ); + } + + /*! \brief Checks whether a pair of partial PO value assignments are observably equivalent + * + * For a pair of partial assignments to be equivalent, all pairs of expansions of + * the partial assignments have to be equivalent. + * + * \param pat1 The first partial PO value + * \param pat2 The second partial PO value + * \return Whether `pat1` and `pat2` are observably equivalent + */ + bool are_observably_equivalent( kitty::cube const& pat1, kitty::cube const& pat2 ) const + { + return _exoec.are_equivalent( pat1, pat2 ); + } + + /*! \brief Builds an observability-equivalence miter network + * + * An observability-equivalence miter network is a generalization of a miter network. + * This network takes two PO value combinations as inputs (thus it has + * `2 * ntk.num_pos()` PIs) and outputs 1 if the two PO value combinations are + * _not_ observably equivalent. + * + * \param miter An empty network where the miter will be built + */ + void build_oe_miter( Ntk& miter ) const + { + std::vector pos1, pos2; + for ( auto i = 0u; i < this->num_pos(); ++i ) + { + pos1.emplace_back( miter.create_pi() ); + } + for ( auto i = 0u; i < this->num_pos(); ++i ) + { + pos2.emplace_back( miter.create_pi() ); + } + build_oe_miter( miter, pos1, pos2 ); + } + + /*! \brief Builds an observability-equivalence miter network + * + * An observability-equivalence miter network is a generalization of a miter network. + * This network takes two PO value combinations as inputs (thus it has + * `2 * ntk.num_pos()` PIs) and outputs 1 if the two PO value combinations are + * _not_ observably equivalent. + * + * \param miter The network where the miter will be built + * \param pos1 Signals of the first set of (main network's) POs + * \param pos2 Signals of the second set of (main network's) POs + */ + template + void build_oe_miter( NtkMiter& miter, std::vector const& pos1, std::vector const& pos2 ) const + { + assert( pos1.size() == this->num_pos() ); + assert( pos2.size() == this->num_pos() ); + + std::vector are_both_in_class_i; + std::vector is_in_class1, is_in_class2; + std::vector ins1, ins2; + ins1.resize( this->num_pos() ); + ins2.resize( this->num_pos() ); + _exoec.foreach_class( [&]( std::vector const& pats ){ + is_in_class1.clear(); + is_in_class2.clear(); + for ( uint32_t pat : pats ) + { + for ( auto i = 0u; i < this->num_pos(); ++i ) + { + ins1[i] = ( pat & 0x1 ) ? pos1[i] : !pos1[i]; + ins2[i] = ( pat & 0x1 ) ? pos2[i] : !pos2[i]; + pat >>= 1; + } + is_in_class1.emplace_back( miter.create_nary_and( ins1 ) ); + is_in_class2.emplace_back( miter.create_nary_and( ins2 ) ); + } + are_both_in_class_i.emplace_back( miter.create_and( miter.create_nary_or( is_in_class1 ), miter.create_nary_or( is_in_class2 ) ) ); + return true; + }); + miter.create_po( !miter.create_nary_or( are_both_in_class_i ) ); + /* miter output = 1 <=> there is no class i that pos1 and pos2 are both in <=> pos1 and pos2 are not OE */ + } + +private: + detail::equivalence_classes_mgr _exoec; +}; /* dont_care_view */ + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/views/dont_touch_view.hpp b/third-party/mockturtle/include/mockturtle/views/dont_touch_view.hpp new file mode 100644 index 00000000000..7f70712c78e --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/dont_touch_view.hpp @@ -0,0 +1,142 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dont_touch_view.hpp + \brief Select nodes to be "don't touch" + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Mark nodes as "don't touch". + * + * This view adds methods to mark nodes as don't touch. A don't touch node + * will be skipped during logic optimization or mapping. + * It always adds the functions `select_dont_touch`, `remove_dont_touch`, + * `is_dont_touch`. + * + * **Required network functions:** + * - `size` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + klut_network klut = ...; + dont_touch_view klut_dont_touch{ klut }; + + // select dont touch nodes + klut_dont_touch.select_dont_touch( 20 ); + + // call technology mapping to map the rest of the network + binding_view res = emap( klut_dont_touch, tech_lib ); + \endverbatim + */ +template +class dont_touch_view : public Ntk +{ +public: + using node = typename Ntk::node; + +public: + explicit dont_touch_view() + : Ntk(), _dont_touch() + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + explicit dont_touch_view( Ntk const& ntk ) + : Ntk( ntk ), _dont_touch() + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + } + + dont_touch_view& operator=( dont_touch_view const& dont_touch_ntk ) + { + Ntk::operator=( dont_touch_ntk ); + _dont_touch = dont_touch_ntk._dont_touch; + return *this; + } + + void select_dont_touch( node const& n ) + { + _dont_touch.insert( Ntk::node_to_index( n ) ); + } + + void remove_dont_touch( node const& n ) + { + if ( auto it = _dont_touch.find( Ntk::node_to_index( n ) ); it != _dont_touch.end() ) + { + _dont_touch.erase( it ); + } + } + + bool is_dont_touch( node const& n ) const + { + return _dont_touch.find( Ntk::node_to_index( n ) ) != _dont_touch.end(); + } + + template + void foreach_dont_touch( Fn&& fn ) const + { + constexpr auto is_bool_f = std::is_invocable_r_v; + constexpr auto is_void_f = std::is_invocable_r_v; + + for ( auto el : _dont_touch ) + { + if constexpr ( is_bool_f ) + { + if ( !fn( Ntk::index_to_node( el ) ) ) + return; + } + else + { + fn( Ntk::index_to_node( el ) ); + } + } + } + +private: + std::unordered_set _dont_touch; +}; /* dont_touch_view */ + +template +dont_touch_view( T const& ) -> dont_touch_view; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/views/fanout_limit_view.hpp b/third-party/mockturtle/include/mockturtle/views/fanout_limit_view.hpp new file mode 100644 index 00000000000..acbef80a589 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/fanout_limit_view.hpp @@ -0,0 +1,293 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file fanout_limit_view.hpp + \brief View that replicates nodes whose fanout size exceed a limit + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../utils/node_map.hpp" + +namespace mockturtle +{ + +struct fanout_limit_view_params +{ + uint64_t fanout_limit{ 16 }; +}; + +template +class fanout_limit_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + fanout_limit_view( fanout_limit_view_params const ps = {} ) + : replicas( *this ), ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + assert( ps.fanout_limit > 0u ); + } + + uint32_t create_po( signal const& f ) + { + if ( Ntk::is_maj( Ntk::get_node( f ) ) && Ntk::fanout_size( Ntk::get_node( f ) ) + 1 > ps.fanout_limit ) + { + return Ntk::create_po( replicate_node( f ) ); + } + else + { + return Ntk::create_po( f ); + } + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + std::array fanins; + fanins[0u] = ( Ntk::is_maj( Ntk::get_node( a ) ) && Ntk::fanout_size( Ntk::get_node( a ) ) > ps.fanout_limit - 1 ) ? replicate_node( a ) : a; + fanins[1u] = ( Ntk::is_maj( Ntk::get_node( b ) ) && Ntk::fanout_size( Ntk::get_node( b ) ) > ps.fanout_limit - 1 ) ? replicate_node( b ) : b; + fanins[2u] = ( Ntk::is_maj( Ntk::get_node( c ) ) && Ntk::fanout_size( Ntk::get_node( c ) ) > ps.fanout_limit - 1 ) ? replicate_node( c ) : c; + return Ntk::create_maj( fanins[0u], fanins[1u], fanins[2u] ); + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( Ntk::get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( Ntk::get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + const auto f = create_maj( a, !b, c ); + const auto g = create_maj( a, b, !c ); + return create_maj( !a, f, g ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), Ntk::get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), Ntk::get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), Ntk::get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( mig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 3u ); + return create_maj( children[0u], children[1u], children[2u] ); + } +#pragma endregion + + signal replicate_node( signal const& s ) + { + return Ntk::is_complemented( s ) ? !replicate_node( Ntk::get_node( s ) ) : replicate_node( Ntk::get_node( s ) ); + } + + signal replicate_node( node const& n ) + { + if ( replicas.has( n ) ) + { + auto replica = replicas[n]; + if ( Ntk::fanout_size( replica ) < ps.fanout_limit ) + { + return Ntk::make_signal( replica ); + } + } + + std::array fanins; + Ntk::foreach_fanin( n, [&]( signal const& f, auto index ) { + fanins[index] = ( Ntk::is_maj( Ntk::get_node( f ) ) && Ntk::fanout_size( Ntk::get_node( f ) ) > ps.fanout_limit - 1u ) ? replicate_node( f ) : f; + } ); + + auto const new_signal = create_maj_overwrite_strash( fanins[0u], fanins[1u], fanins[2u] ); + replicas[n] = Ntk::get_node( new_signal ); + return new_signal; + } + + uint32_t num_gates() const + { + return Ntk::num_gates() + count_hash_overwrites; + } + +protected: + signal create_maj_overwrite_strash( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + else + { + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + typename storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + if ( true ) + { + const auto it = Ntk::_storage->hash.find( node ); + if ( it != Ntk::_storage->hash.end() ) + { + ++count_hash_overwrites; + } + } + + const auto index = Ntk::_storage->nodes.size(); + + if ( index >= .9 * Ntk::_storage->nodes.capacity() ) + { + Ntk::_storage->nodes.reserve( static_cast( 3.1415f * index ) ); + Ntk::_storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + Ntk::_storage->nodes.push_back( node ); + Ntk::_storage->hash[node] = index; + + /* increase ref-count to children */ + Ntk::_storage->nodes[a.index].data[0].h1++; + Ntk::_storage->nodes[b.index].data[0].h1++; + Ntk::_storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : Ntk::_events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + +protected: + uint32_t count_hash_overwrites{ 0 }; + unordered_node_map replicas; + fanout_limit_view_params const ps; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/fanout_view.hpp b/third-party/mockturtle/include/mockturtle/views/fanout_view.hpp new file mode 100644 index 00000000000..257b31c06c9 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/fanout_view.hpp @@ -0,0 +1,350 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file fanout_view.hpp + \brief Implements fanout for a network + + \author Alessandro Tempia Calvino + \author Hanyu Wang + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../networks/detail/foreach.hpp" +#include "../networks/events.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "immutable_view.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +struct fanout_view_params +{ + bool update_on_add{ true }; + bool update_on_modified{ true }; + bool update_on_delete{ true }; +}; + +/*! \brief Implements `foreach_fanout` methods for networks. + * + * This view computes the fanout of each node of the network. + * It implements the network interface method `foreach_fanout`. The + * fanout are computed at construction and can be recomputed by + * calling the `update_fanout` method. + * + * **Required network functions:** + * - `foreach_node` + * - `foreach_fanin` + * + */ +template> +class fanout_view +{ +}; + +template +class fanout_view : public Ntk +{ +public: + fanout_view( Ntk const& ntk, fanout_view_params const& ps = {} ) : Ntk( ntk ) + { + (void)ps; + } +}; + +template +class fanout_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit fanout_view( fanout_view_params const& ps = {} ) + : Ntk(), _fanout( *this ), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + update_fanout(); + + register_events(); + } + + explicit fanout_view( Ntk const& ntk, fanout_view_params const& ps = {} ) + : Ntk( ntk ), _fanout( ntk ), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + update_fanout(); + + register_events(); + } + + /*! \brief Copy constructor. */ + fanout_view( fanout_view const& other ) + : Ntk( other ), _fanout( other._fanout ), _ps( other._ps ) + { + register_events(); + } + + fanout_view& operator=( fanout_view const& other ) + { + release_events(); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + _ps = other._ps; + _fanout = other._fanout; + + register_events(); + + return *this; + } + + ~fanout_view() + { + release_events(); + } + + template + void foreach_fanout( node const& n, Fn&& fn ) const + { + assert( n < this->size() ); + detail::foreach_element( _fanout[n].begin(), _fanout[n].end(), fn ); + } + + void update_fanout() + { + compute_fanout(); + } + + std::vector fanout( node const& n ) const /* deprecated */ + { + return _fanout[n]; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + if ( Ntk::get_node( new_signal ) == old_node && !Ntk::is_complemented( new_signal ) ) + return; + + if ( Ntk::is_dead( Ntk::get_node( new_signal ) ) ) + { + Ntk::revive_node( Ntk::get_node( new_signal ) ); + } + + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( Ntk::is_dead( Ntk::get_node( _new ) ) ) + { + auto it = old_to_new.find( Ntk::get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = Ntk::is_complemented( _new ) ? Ntk::create_not( it->second ) : it->second; + it = old_to_new.find( Ntk::get_node( _new ) ); + } + } + /* revive */ + if ( Ntk::is_dead( Ntk::get_node( _new ) ) ) + { + Ntk::revive_node( Ntk::get_node( _new ) ); + } + + if ( Ntk::get_node( _new ) == _old && !Ntk::is_complemented( _new ) ) + continue; + + const auto parents = _fanout[_old]; + for ( auto n : parents ) + { + if ( const auto repl = Ntk::replace_in_node( n, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + Ntk::replace_in_outputs( _old, _new ); + + /* reset fan-in of old node */ + if ( _old != Ntk::get_node( _new ) ) /* substitute a node using itself*/ + { + old_to_new.insert( { _old, _new } ); + Ntk::take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( Ntk::get_node( new_signal ) == old_node && !Ntk::is_complemented( new_signal ) ) + return; + + if ( Ntk::is_dead( Ntk::get_node( new_signal ) ) ) + { + Ntk::revive_node( Ntk::get_node( new_signal ) ); + } + + const auto parents = _fanout[old_node]; + for ( auto n : parents ) + { + Ntk::replace_in_node_no_restrash( n, old_node, new_signal ); + } + + /* check outputs */ + Ntk::replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + Ntk::take_out_node( old_node ); + } + } + +private: + void register_events() + { + if ( _ps.update_on_add ) + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + _fanout.resize(); + Ntk::foreach_fanin( n, [&, this]( auto const& f ) { + _fanout[f].push_back( n ); + } ); + } ); + } + + if ( _ps.update_on_modified ) + { + modified_event = Ntk::events().register_modified_event( [this]( auto const& n, auto const& previous ) { + (void)previous; + for ( auto const& f : previous ) + { + _fanout[f].erase( std::remove( _fanout[f].begin(), _fanout[f].end(), n ), _fanout[f].end() ); + } + Ntk::foreach_fanin( n, [&, this]( auto const& f ) { + _fanout[f].push_back( n ); + } ); + } ); + } + + if ( _ps.update_on_delete ) + { + delete_event = Ntk::events().register_delete_event( [this]( auto const& n ) { + _fanout[n].clear(); + Ntk::foreach_fanin( n, [&, this]( auto const& f ) { + _fanout[f].erase( std::remove( _fanout[f].begin(), _fanout[f].end(), n ), _fanout[f].end() ); + } ); + } ); + } + } + + void release_events() + { + if ( add_event ) + { + Ntk::events().release_add_event( add_event ); + } + + if ( modified_event ) + { + Ntk::events().release_modified_event( modified_event ); + } + + if ( delete_event ) + { + Ntk::events().release_delete_event( delete_event ); + } + } + + void compute_fanout() + { + _fanout.reset(); + + /* Compute fanout also for buffers in buffered networks */ + if constexpr ( is_buffered_network_type_v ) + { + this->foreach_node( [&]( auto const& n ) { + if ( this->is_pi( n ) || this->is_constant( n ) ) + return true; + this->foreach_fanin( n, [&]( auto const& c ) { + auto& fanout = _fanout[c]; + if ( std::find( fanout.begin(), fanout.end(), n ) == fanout.end() ) + { + fanout.push_back( n ); + } + } ); + return true; + } ); + } + else + { + this->foreach_gate( [&]( auto const& n ) { + this->foreach_fanin( n, [&]( auto const& c ) { + auto& fanout = _fanout[c]; + if ( std::find( fanout.begin(), fanout.end(), n ) == fanout.end() ) + { + fanout.push_back( n ); + } + } ); + } ); + } + } + + node_map, Ntk> _fanout; + fanout_view_params _ps; + + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +template +fanout_view( T const&, fanout_view_params const& ps = {} ) -> fanout_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/immutable_view.hpp b/third-party/mockturtle/include/mockturtle/views/immutable_view.hpp new file mode 100644 index 00000000000..f5ee1186d49 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/immutable_view.hpp @@ -0,0 +1,89 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file immutable_view.hpp + \brief Disables all methods to change the network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Deletes all methods that can change the network. + * + * This view deletes all methods that can change the network structure such as + * `create_not`, `create_and`, or `create_node`. This view is convenient to + * use as a base class for other views that make some computations based on the + * structure when being constructed. Then, changes to the structure invalidate + * these data. + */ +template +class immutable_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /*! \brief Default constructor. + * + * Constructs immutable view on another network. + */ + immutable_view( Ntk const& ntk ) : Ntk( ntk ) + { + } + + signal create_pi() = delete; + void create_po( signal const& s ) = delete; + signal create_ro() = delete; + void create_ri( signal const& s ) = delete; + signal create_buf( signal const& f ) = delete; + signal create_not( signal const& f ) = delete; + signal create_and( signal const& f, signal const& g ) = delete; + signal create_nand( signal const& f, signal const& g ) = delete; + signal create_or( signal const& f, signal const& g ) = delete; + signal create_nor( signal const& f, signal const& g ) = delete; + signal create_lt( signal const& f, signal const& g ) = delete; + signal create_le( signal const& f, signal const& g ) = delete; + signal create_gt( signal const& f, signal const& g ) = delete; + signal create_ge( signal const& f, signal const& g ) = delete; + signal create_xor( signal const& f, signal const& g ) = delete; + signal create_xnor( signal const& f, signal const& g ) = delete; + signal create_maj( signal const& f, signal const& g, signal const& h ) = delete; + signal create_ite( signal const& cond, signal const& f_then, signal const& f_else ) = delete; + signal create_node( std::vector const& fanin, kitty::dynamic_truth_table const& function ) = delete; + signal clone_node( immutable_view const& other, node const& source, std::vector const& fanin ) = delete; + void substitute_node( node const& old_node, node const& new_node ) = delete; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/mapping_view.hpp b/third-party/mockturtle/include/mockturtle/views/mapping_view.hpp new file mode 100644 index 00000000000..075e61dc06b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/mapping_view.hpp @@ -0,0 +1,276 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mapping_view.hpp + \brief Implements mapping methods to create mapped networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/truth_table_cache.hpp" +#include "../views/immutable_view.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +struct mapping_view_storage; + +template<> +struct mapping_view_storage +{ + std::vector mappings; + uint32_t mapping_size{ 0 }; + std::vector functions; + truth_table_cache cache; +}; + +template<> +struct mapping_view_storage +{ + std::vector mappings; + uint32_t mapping_size{ 0 }; +}; + +} // namespace detail + +template +inline constexpr bool implements_mapping_interface_v = has_has_mapping_v && ( !StoreFunction || has_cell_function_v ); + +/*! \brief Adds mapping API methods to network. + * + * This view adds methods of the mapping API methods to a network. It always + * adds the functions `has_mapping`, `is_cell_root`, `clear_mapping`, + * `num_cells`, `add_to_mapping`, `remove_from_mapping`, and + * `foreach_cell_fanin`. If the template argument `StoreFunction` is set to + * `true`, it also adds functions for `cell_function` and `set_cell_function`. + * For the latter case, this view requires more memory to also store the cells' + * truth tables. + * + * These methods are used to represent a mapping that is annotated to a + * subject graph. The interface can, e.g., be used for LUT mapping or standard + * cell mapping. For a common terminology, we call a collection of nodes that + * belong to the same unit a cell, which has a single root. The *mapped node* is + * the cell root. A cell root, and therefore the cell it represents, may be + * assigned a function by means of a truth table. + * + * **Required network functions:** + * - `size` + * - `node_to_index` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // in order to apply mapping, wrap network in mapping view + mapping_view mapped_aig{aig}; + + // call LUT mapping algorithm + lut_mapping( mapped_aig ); + + // nodes of aig and mapped_aig are the same + aig.foreach_node( [&]( auto n ) { + std::cout << n << " has mapping? " << mapped_aig.is_cell_root( n ) << "\n"; + } ); + \endverbatim + */ +template> +class mapping_view +{ +}; + +template +class mapping_view : public Ntk +{ +public: + mapping_view( Ntk const& ntk ) : Ntk( ntk ) + { + } +}; + +template +class mapping_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /*! \brief Default constructor. + * + * Constructs mapping view on another network. + */ + mapping_view( Ntk const& ntk ) + : immutable_view( ntk ), + _mapping_storage( std::make_shared() ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + + _mapping_storage->mappings.resize( ntk.size(), 0 ); + + if constexpr ( StoreFunction ) + { + /* insert 0 truth table */ + _mapping_storage->cache.insert( kitty::dynamic_truth_table( 0 ) ); + + /* default each truth table to 0 */ + _mapping_storage->functions.resize( ntk.size(), 0 ); + } + } + + /*! \brief Returns true, if network has a mapping. */ + bool has_mapping() const + { + return _mapping_storage->mapping_size > 0; + } + + /*! \brief Returns true, if node is the root of a mapped cell. */ + bool is_cell_root( node const& n ) const + { + return _mapping_storage->mappings[this->node_to_index( n )] != 0; + } + + /*! \brief Clears a mapping. */ + void clear_mapping() + { + _mapping_storage->mappings.clear(); + _mapping_storage->mappings.resize( this->size(), 0 ); + _mapping_storage->mapping_size = 0; + } + + /*! \brief Number of cells, i.e, mapped nodes. */ + uint32_t num_cells() const + { + return _mapping_storage->mapping_size; + } + + /*! \brief Adds a node to the mapping. */ + template + void add_to_mapping( node const& n, LeavesIterator begin, LeavesIterator end ) + { + auto& mindex = _mapping_storage->mappings[this->node_to_index( n )]; + + /* increase mapping size? */ + if ( mindex == 0 ) + { + _mapping_storage->mapping_size++; + } + + /* set starting index of leafs */ + mindex = static_cast( _mapping_storage->mappings.size() ); + + /* insert number of leafs */ + _mapping_storage->mappings.push_back( static_cast( std::distance( begin, end ) ) ); + + /* insert leaf indexes */ + while ( begin != end ) + { + _mapping_storage->mappings.push_back( this->node_to_index( *begin++ ) ); + } + } + + /*! \brief Remove from mapping. */ + void remove_from_mapping( node const& n ) + { + auto& mindex = _mapping_storage->mappings[this->node_to_index( n )]; + + if ( mindex != 0 ) + { + _mapping_storage->mapping_size--; + } + + _mapping_storage->mappings[this->node_to_index( n )] = 0; + } + + /*! \brief Gets function of the cell. + * + * The parameter `n` is a node that must be a cell root. + */ + template && enabled>> + kitty::dynamic_truth_table cell_function( node const& n ) const + { + return _mapping_storage->cache[_mapping_storage->functions[this->node_to_index( n )]]; + } + + /*! \brief Sets cell function. + * + * The parameter `n` is a node that must be a cell root. + */ + template && enabled>> + void set_cell_function( node const& n, kitty::dynamic_truth_table const& function ) + { + _mapping_storage->functions[this->node_to_index( n )] = _mapping_storage->cache.insert( function ); + } + + /*! \brief Iterators over cell's fan-ins. + * The parameter `n` is a node that must be a cell root. + * The parameter ``fn`` is any callable that must have one of the + * following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + */ + template + void foreach_cell_fanin( node const& n, Fn&& fn ) const + { + auto it = _mapping_storage->mappings.begin() + _mapping_storage->mappings[this->node_to_index( n )]; + const auto size = *it++; + using IteratorType = decltype( it ); + detail::foreach_element_transform( + it, it + size, + [&]( auto i ) { return this->index_to_node( i ); }, fn ); + } + +private: + std::shared_ptr> _mapping_storage; +}; + +template +mapping_view( T const& ) -> mapping_view; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/views/mffc_view.hpp b/third-party/mockturtle/include/mockturtle/views/mffc_view.hpp new file mode 100644 index 00000000000..8ab26395ccc --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/mffc_view.hpp @@ -0,0 +1,310 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mffc_view.hpp + \brief Implements an isolated view on a single cut in a network + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "immutable_view.hpp" + +namespace mockturtle +{ + +/*! \brief Implements an isolated view on the MFFC of a node. + * + * The network is constructed from a given root node which is traversed towards + * the primary inputs. Nodes are collected as long they only fanout into nodes + * which are already among the visited nodes. Therefore the final view only + * has outgoing edges to nodes not in the view from the given root node or from + * the newly generated primary inputs. + * + * The view reimplements the methods `size`, `num_pis`, `num_pos`, `foreach_pi`, + * `foreach_po`, `foreach_node`, `foreach_gate`, `is_pi`, `node_to_index`, and + * `index_to_node`. + * + * The view requires that the nodes' values contain their reference counts, + * i.e., they are assigned their fanout size. The values are restored by the + * view. + * + * **Required network functions:** + * - `get_node` + * - `decr_value` + * - `value` + * - `foreach_fanin` + * - `is_constant` + * - `node_to_index` + */ +template +class mffc_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit mffc_view( Ntk const& ntk, node const& root ) + : immutable_view( ntk ), _root( root ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + + const auto c0 = this->get_node( this->get_constant( false ) ); + _constants.push_back( c0 ); + _node_to_index.emplace( c0, _node_to_index.size() ); + + const auto c1 = this->get_node( this->get_constant( true ) ); + if ( c1 != c0 ) + { + _constants.push_back( c1 ); + _node_to_index.emplace( c1, _node_to_index.size() ); + ++_num_constants; + } + + _leaves.reserve( 16 ); + _nodes.reserve( _limit ); + _inner.reserve( _limit ); + update_mffcs(); + } + + inline auto size() const { return _num_constants + _num_leaves + _inner.size(); } + inline auto num_pis() const { return _num_leaves; } + inline auto num_pos() const { return _empty ? 0u : 1u; } + inline auto num_gates() const { return _inner.size(); } + + inline bool is_pi( node const& pi ) const + { + return std::find( _leaves.begin(), _leaves.end(), pi ) != _leaves.end(); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _leaves.begin(), _leaves.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + if ( _empty ) + return; + std::vector signals( 1, this->make_signal( _root ) ); + detail::foreach_element( signals.begin(), signals.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( _inner.begin(), _inner.end(), fn ); + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( _constants.begin(), _constants.end(), fn ); + detail::foreach_element( _leaves.begin(), _leaves.end(), fn, _num_constants ); + detail::foreach_element( _inner.begin(), _inner.end(), fn, _num_constants + _num_leaves ); + } + + inline auto index_to_node( uint32_t index ) const + { + if ( index < _num_constants ) + { + return _constants[index]; + } + if ( index < _num_constants + _num_leaves ) + { + return _leaves[index - _num_constants]; + } + return _inner[index - _num_constants - _num_leaves]; + } + + inline auto node_to_index( node const& n ) const { return _node_to_index.at( n ); } + + void update_mffcs() + { + _leaves.clear(); + _inner.clear(); + if ( collect( _root ) ) + { + _empty = false; + compute_sets(); + } + else + { + _empty = true; + } + _num_leaves = static_cast( _leaves.size() ); + + /* restore ref counts */ + for ( auto const& n : _nodes ) + { + this->incr_fanout_size( n ); + } + } + +private: + bool collect( node const& n ) + { + if ( Ntk::is_constant( n ) ) + return true; + + if ( Ntk::is_pi( n ) ) + { + return true; + } + + /* we break from the loop over the fanins, if we find that _nodes contains + too many nodes; the return value is stored in ret_val */ + bool ret_val = true; + this->foreach_fanin( n, [&]( auto const& f ) { + _nodes.push_back( this->get_node( f ) ); + if ( this->decr_fanout_size( this->get_node( f ) ) == 0 && ( _nodes.size() > _limit || !collect( this->get_node( f ) ) ) ) + { + ret_val = false; + return false; + } + return true; + } ); + + return ret_val; + } + + void compute_sets() + { + // std::stable_sort( _nodes.begin(), _nodes.end(), + // [&]( auto const& n1, auto const& n2 ) { return static_cast( this )->node_to_index( n1 ) < static_cast( this )->node_to_index( n2 ); } ); + std::stable_sort( _nodes.begin(), _nodes.end() ); + + for ( auto const& n : _nodes ) + { + if ( Ntk::is_constant( n ) ) + { + continue; + } + + if ( this->fanout_size( n ) > 0 || Ntk::is_pi( n ) ) /* PI candidate */ + { + if ( _leaves.empty() || _leaves.back() != n ) + { + _leaves.push_back( n ); + } + } + else + { + if ( _inner.empty() || _inner.back() != n ) + { + _inner.push_back( n ); + } + } + } + + for ( auto const& n : _leaves ) + { + _node_to_index.emplace( n, _node_to_index.size() ); + } + for ( auto const& n : _inner ) + { + _node_to_index.emplace( n, _node_to_index.size() ); + } + + _inner.push_back( _root ); + _node_to_index.emplace( _root, _node_to_index.size() ); + + /* sort topologically */ + _topo.clear(); + _colors.clear(); + const auto _size = _num_constants + _inner.size() + _leaves.size(); + _colors.resize( _size, 0 ); + std::for_each( _leaves.begin(), _leaves.end(), [&]( auto& l ) { _colors[_node_to_index[l]] = 2u; } ); + for ( auto i = 0u; i < _num_constants; ++i ) + { + _colors[i] = 2u; + } + topo_sort_rec( _root ); + + assert( _inner.size() == _topo.size() ); + _inner = _topo; + } + + void topo_sort_rec( node const& n ) + { + const auto idx = _node_to_index[n]; + + /* is permanently marked? */ + if ( _colors[idx] == 2u ) + return; + + /* mark node temporarily */ + _colors[idx] = 1u; + + /* mark children */ + Ntk::foreach_fanin( n, [&]( auto const& f ) { + topo_sort_rec( Ntk::get_node( f ) ); + } ); + + /* mark node n permanently */ + _colors[idx] = 2u; + + _topo.push_back( n ); + } + +public: + std::vector _nodes, _constants, _leaves, _inner, _topo; + std::vector _colors; + unsigned _num_constants{ 1 }, _num_leaves{ 0 }; + phmap::flat_hash_map _node_to_index; + node _root; + bool _empty{ true }; + uint32_t _limit{ 100 }; +}; + +template +mffc_view( T const&, typename T::node const& ) -> mffc_view; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/names_view.hpp b/third-party/mockturtle/include/mockturtle/views/names_view.hpp new file mode 100644 index 00000000000..e633b912b04 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/names_view.hpp @@ -0,0 +1,210 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file names_view.hpp + \brief Implements methods to declare names for network signals + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +template +class names_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + template + names_view( Ntk const& ntk = Ntk(), StrType name = "" ) + : Ntk( ntk ), _network_name{ name } + { + } + + names_view( names_view const& named_ntk ) + : Ntk( named_ntk ), _network_name( named_ntk._network_name ), _signal_names( named_ntk._signal_names ), _output_names( named_ntk._output_names ) + { + } + + names_view& operator=( names_view const& named_ntk ) + { + if ( this != &named_ntk ) // Check for self-assignment + { + Ntk::operator=( named_ntk ); + _signal_names = named_ntk._signal_names; + _network_name = named_ntk._network_name; + _output_names = named_ntk._output_names; + } + return *this; + } + + /*! \brief Creates a primary input and set its name. + * + * \param name Name of the created primary input + */ + signal create_pi( std::string const& name = {} ) + { + const auto s = Ntk::create_pi(); + if ( !name.empty() ) + { + set_name( s, name ); + } + return s; + } + + /*! \brief Creates a primary output and set its name. + * + * \param s Signal that drives the created primary output + * \param name Name of the created primary output + */ + void create_po( signal const& s, std::string const& name = {} ) + { + const auto index = Ntk::num_pos(); + Ntk::create_po( s ); + if ( !name.empty() ) + { + set_output_name( index, name ); + } + } + + /*! \brief Sets network name. + * + * \param name Name of the network + */ + template + void set_network_name( StrType name ) noexcept + { + _network_name = name; + } + + /*! \brief Gets network name. + * + * \return Network name + */ + std::string get_network_name() const noexcept + { + return _network_name; + } + + /*! \brief Checks if a signal has a name. + * + * Note that complemented signals may have different names. + * + * \param s Signal to be checked + * \return Whether the signal has a name in record + */ + bool has_name( signal const& s ) const + { + return ( _signal_names.find( s ) != _signal_names.end() ); + } + + /*! \brief Sets the name for a signal. + * + * Note that names are set separately for complemented signals. + * + * \param s Signal to be set a name + * \param name Name of the signal + */ + void set_name( signal const& s, std::string const& name ) + { + _signal_names[s] = name; + } + + /*! \brief Gets signal name. + * + * Note that complemented signals may have different names. + * + * \param s Signal to be queried + * \return Name of the signal + */ + std::string get_name( signal const& s ) const + { + return _signal_names.at( s ); + } + + /*! \brief Checks if a primary output has a name. + * + * \param index Index of the primary output to be checked + * \return Whether the primary output has a name in record + */ + bool has_output_name( uint32_t index ) const + { + return ( _output_names.find( index ) != _output_names.end() ); + } + + /*! \brief Sets the name for a primary output. + * + * Note that even if two primary outputs are driven by + * the same signal, they may have different names. + * + * \param index Index of the primary output to set a name + * \param name Name of the primary output + */ + void set_output_name( uint32_t index, std::string const& name ) + { + _output_names[index] = name; + } + + /*! \brief Gets the name of a primary output. + * + * Note that even if two primary outputs are driven by + * the same signal, they may have different names. + * + * \param index Index of the primary output to be queried + * \return Name of the primary output + */ + std::string get_output_name( uint32_t index ) const + { + return _output_names.at( index ); + } + +private: + std::string _network_name; + std::map _signal_names; + std::map _output_names; +}; /* names_view */ + +template +names_view( T const& ) -> names_view; + +template +names_view( T const&, typename T::signal const& ) -> names_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/rank_view.hpp b/third-party/mockturtle/include/mockturtle/views/rank_view.hpp new file mode 100644 index 00000000000..ecfef633f2c --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/rank_view.hpp @@ -0,0 +1,384 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file rank_view.hpp + \brief Implements rank orders for a network + + \author Marcel Walter +*/ + +#pragma once + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "depth_view.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Implements rank orders for a network. +* +* This view assigns manipulable relative orders to the nodes of each level. +* A sequence of nodes in the same level is called a rank. The width +* of a rank is thereby defined as the number of nodes in the rank. The width of +* the network is equal to the width of the widest rank. This view implements +* functions to retrieve the assigned node position within a rank (`rank_position`) +* as well as to fetch the node in a certain rank at a certain position (`at_rank_position`). +* The ranks are assigned at construction and can be manipulated by calling the `swap` function. +* +* This view also automatically inserts new nodes into their respective rank (at the end), +* however, it does not update the information, when modifying or deleting nodes. +* +* **Required network functions:** +* - `foreach_node` +* - `get_node` +* - `num_pis` +* - `is_ci` +* - `is_constant` +* +* Example +* + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // create a rank view on the network + rank_view aig_rank{aig_depth}; + + // print width + std::cout << "Width: " << aig_rank.width() << "\n"; + \endverbatim +*/ +template&& has_at_rank_position_v&& has_swap_v&& has_width_v&& has_foreach_node_in_rank_v&& has_foreach_gate_in_rank_v> +class rank_view +{ +}; + +template +class rank_view : public depth_view +{ +public: + rank_view( Ntk const& ntk ) : depth_view( ntk ) + { + } +}; + +template +class rank_view : public depth_view +{ +public: + static constexpr bool is_topologically_sorted = true; + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit rank_view() + : depth_view(), rank_pos{ *this }, ranks{}, max_rank_width{ 0 } + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Standard constructor. + * + * \param ntk Base network + */ + explicit rank_view( Ntk const& ntk ) + : depth_view{ ntk }, rank_pos{ ntk }, ranks{ this->depth() + 1 }, max_rank_width{ 0 } + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + + init_ranks(); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Copy constructor. */ + rank_view( rank_view const& other ) + : depth_view( other ), rank_pos{ other.rank_pos }, ranks{ other.ranks }, max_rank_width{ other.max_rank_width } + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + rank_view& operator=( rank_view const& other ) + { + /* delete the event of this network */ + Ntk::events().release_add_event( add_event ); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + rank_pos = other.rank_pos; + ranks = other.ranks; + max_rank_width = other.max_rank_width; + + /* register new event in the other network */ + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + + return *this; + } + + ~rank_view() + { + Ntk::events().release_add_event( add_event ); + } + /** + * \brief Returns the rank position of a node. + * + * @param n Node to get the rank position of. + * @return Rank position of node `n`. + */ + uint32_t rank_position( node const& n ) const noexcept + { + assert( !this->is_constant( n ) && "node must not be constant" ); + + return rank_pos[n]; + } + /** + * \brief Returns the node at a certain rank position. + * + * @param level Level in the network, i.e., rank to get the node from. + * @param pos Position in the rank to get the node from. + * @return Node at position `pos` in rank `level`. + */ + node at_rank_position( uint32_t const level, uint32_t const pos ) const noexcept + { + assert( level < ranks.size() && "level must be less than the number of ranks" ); + assert( pos < ranks[level].size() && "pos must be less than the number of nodes in rank" ); + + return ranks[level][pos]; + } + /** + * \brief Returns the width of the widest rank in the network. + * + * @return Width of the widest rank in the network. + */ + uint32_t width() const noexcept + { + return max_rank_width; + } + /** + * \brief Swaps the positions of two nodes in the same rank. + * + * @param n1 First node to swap. + * @param n2 Second node to swap. + */ + void swap( node const& n1, node const& n2 ) noexcept + { + assert( this->level( n1 ) == this->level( n2 ) && "nodes must be in the same rank" ); + + auto& pos1 = rank_pos[n1]; + auto& pos2 = rank_pos[n2]; + + std::swap( ranks[this->level( n1 )][pos1], ranks[this->level( n2 )][pos2] ); + std::swap( pos1, pos2 ); + } + /** + * \brief Sorts the given rank according to a comparator. + * + * @tparam Cmp Functor type that compares two nodes. It needs to fulfill the requirements of `Compare` (named C++ requirement). + * @param level The level of the rank to sort. + * @param cmp The comparator to use. + */ + template + void sort_rank( uint32_t const level, Cmp const& cmp ) + { + // level must be less than the number of ranks + if ( level < ranks.size() ) + { + auto& rank = ranks[level]; + + std::stable_sort( rank.begin(), rank.end(), cmp ); + std::for_each( rank.cbegin(), rank.cend(), [this, i = 0u]( auto const& n ) mutable { rank_pos[n] = i++; } ); + } + } + /** + * \brief Applies a given function to each node in the rank level in order. + * + * @tparam Fn Functor type. + * @param level The rank to apply fn to. + * @param fn The function to apply. + */ + template + void foreach_node_in_rank( uint32_t const level, Fn&& fn ) const + { + // level must be less than the number of ranks + if ( level < ranks.size() ) + { + auto const& rank = ranks[level]; + + detail::foreach_element( rank.cbegin(), rank.cend(), std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each node in rank order. + * + * This function overrides the `foreach_node` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_node( Fn&& fn ) const + { + for ( auto l = 0; l < ranks.size(); ++l ) + { + foreach_node_in_rank( l, std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each gate in the rank level in order. + * + * @tparam Fn Functor type. + * @param level The rank to apply fn to. + * @param fn The function to apply. + */ + template + void foreach_gate_in_rank( uint32_t const level, Fn&& fn ) const + { + // level must be less than the number of ranks + if ( level < ranks.size() ) + { + auto const& rank = ranks[level]; + + detail::foreach_element_if( + rank.cbegin(), rank.cend(), [this]( auto const& n ) { return !this->is_ci( n ); }, std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each gate in rank order. + * + * This function overrides the `foreach_gate` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_gate( Fn&& fn ) const + { + for ( auto l = 0; l < ranks.size(); ++l ) + { + foreach_gate_in_rank( l, std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each PI in rank order. + * + * This function overrides the `foreach_pi` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_pi( Fn&& fn ) const + { + std::vector pis{}; + pis.reserve( this->num_pis() ); + + depth_view::foreach_pi( [&pis]( auto const& pi ) { pis.push_back( pi ); } ); + std::stable_sort( pis.begin(), pis.end(), [this]( auto const& n1, auto const& n2 ) { return rank_pos[n1] < rank_pos[n2]; } ); + detail::foreach_element( pis.cbegin(), pis.cend(), std::forward( fn ) ); + } + /** + * Overrides the base class method to also call the add_event on create_pi(). + * + * @note This can (and in fact will) lead to issues if Ntk already calls add_event functions on create_pi()! + * + * @return Newly created PI signal. + */ + signal create_pi() + { + auto const n = depth_view::create_pi(); + this->resize_levels(); + on_add( this->get_node( n ) ); + return n; + } + +private: + node_map rank_pos; + std::vector> ranks; + uint32_t max_rank_width; + + std::shared_ptr::add_event_type> add_event; + + void insert_in_rank( node const& n ) noexcept + { + auto& rank = ranks[this->level( n )]; + rank_pos[n] = rank.size(); + rank.push_back( n ); + max_rank_width = std::max( max_rank_width, static_cast( rank.size() ) ); + } + + void on_add( node const& n ) noexcept + { + if ( this->level( n ) >= ranks.size() ) + { + // add sufficient ranks to store the new node + ranks.insert( ranks.end(), this->level( n ) - ranks.size() + 1, {} ); + } + rank_pos.resize(); + + insert_in_rank( n ); + } + + void init_ranks() noexcept + { + depth_view::foreach_node( [this]( auto const& n ) { + if (!this->is_constant(n)) + { + insert_in_rank(n); + } } ); + } +}; + +template +rank_view( T const& ) -> rank_view; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/views/topo_view.hpp b/third-party/mockturtle/include/mockturtle/views/topo_view.hpp new file mode 100644 index 00000000000..227e9c36e2a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/topo_view.hpp @@ -0,0 +1,324 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file topo_view.hpp + \brief Reimplements foreach_node to guarantee topological order + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Max Austin +*/ + +#pragma once + +#include +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "immutable_view.hpp" + +namespace mockturtle +{ + +/*! \brief Ensures topological order for of all nodes reachable from the outputs. + * + * Overrides the interface methods `foreach_node`, `foreach_gate`, + * `size`, `num_gates`. + * + * This class computes *on construction* a topological order of the nodes which + * are reachable from the outputs. Constant nodes and primary inputs will also + * be considered even if they are not reachable from the outputs. Further, + * constant nodes and primary inputs will be visited first before any gate node + * is visited. Constant nodes precede primary inputs, and primary inputs are + * visited in the same order in which they were created. + * + * Since the topological order is computed only once when creating an instance, + * this view disables changes to the network interface. Also, since only + * reachable nodes are traversed, not all network nodes may be called in + * `foreach_node` and `foreach_gate`. + * + * **Required network functions:** + * - `get_constant` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_fanin` + * - `incr_trav_id` + * - `set_visited` + * - `trav_id` + * - `visited` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow; aig may not be in topological order + aig_network aig = ...; + + // create a topological view on the network + topo_view aig_topo{aig}; + + // call algorithm that requires topological order + cut_enumeration( aig_topo ); + \endverbatim + */ +template> +class topo_view +{ +}; + +template +class topo_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr bool is_topologically_sorted = true; + + /*! \brief Default constructor. + * + * Constructs topological view on another network. + */ + topo_view( Ntk const& ntk ) : immutable_view( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + update_topo(); + } + + /*! \brief Default constructor. + * + * Constructs topological view, but only for the transitive fan-in starting + * from a given start signal. + */ + topo_view( Ntk const& ntk, typename Ntk::signal const& start_signal ) + : immutable_view( ntk ), + start_signal( start_signal ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + update_topo(); + } + + /*! \brief Reimplementation of `size`. */ + auto size() const + { + return static_cast( topo_order.size() ); + } + + /*! \brief Reimplementation of `num_gates`. */ + auto num_gates() const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + return static_cast( topo_order.size() - offset ); + } + + /*! \brief Reimplementation of `node_to_index`. */ + uint32_t node_to_index( node const& n ) const + { + return std::distance( std::begin( topo_order ), std::find( std::begin( topo_order ), std::end( topo_order ), n ) ); + } + + /*! \brief Reimplementation of `index_to_node`. */ + node index_to_node( uint32_t index ) const + { + return topo_order.at( index ); + } + + /*! \brief Reimplementation of `foreach_node`. */ + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( topo_order.begin(), + topo_order.end(), + fn ); + } + + /*! \brief Implementation of `foreach_node` in reverse topological order. */ + template + void foreach_node_reverse( Fn&& fn ) const + { + detail::foreach_element( topo_order.rbegin(), + topo_order.rend(), + fn ); + } + + /*! \brief Reimplementation of `foreach_gate`. */ + template + void foreach_gate( Fn&& fn ) const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + detail::foreach_element( topo_order.begin() + offset, + topo_order.end(), + fn ); + } + + /*! \brief Implementation of `foreach_gate` in reverse topological order. */ + template + void foreach_gate_reverse( Fn&& fn ) const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + detail::foreach_element( topo_order.rbegin(), + topo_order.rend() - offset, + fn ); + } + + /*! \brief Reimplementation of `foreach_po`. + * + * If `start_signal` is provided in constructor, only this is returned as + * primary output, otherwise reverts to original `foreach_po` implementation. + */ + template + void foreach_po( Fn&& fn ) const + { + if ( start_signal ) + { + std::vector signals( 1, *start_signal ); + detail::foreach_element( signals.begin(), signals.end(), fn ); + } + else + { + Ntk::foreach_po( fn ); + } + } + + uint32_t num_pos() const + { + return start_signal ? 1 : Ntk::num_pos(); + } + + void update_topo() + { + this->incr_trav_id(); + this->incr_trav_id(); + topo_order.reserve( this->size() ); + + /* constants and PIs */ + const auto c0 = this->get_node( this->get_constant( false ) ); + topo_order.push_back( c0 ); + this->set_visited( c0, this->trav_id() ); + + if ( const auto c1 = this->get_node( this->get_constant( true ) ); this->visited( c1 ) != this->trav_id() ) + { + topo_order.push_back( c1 ); + this->set_visited( c1, this->trav_id() ); + } + + this->foreach_ci( [this]( auto n ) { + if ( this->visited( n ) != this->trav_id() ) + { + topo_order.push_back( n ); + this->set_visited( n, this->trav_id() ); + } + } ); + + if ( start_signal ) + { + if ( this->visited( this->get_node( *start_signal ) ) == this->trav_id() ) + return; + create_topo_rec( this->get_node( *start_signal ) ); + } + else + { + Ntk::foreach_co( [this]( auto f ) { + /* node was already visited */ + if ( this->visited( this->get_node( f ) ) == this->trav_id() ) + return; + + create_topo_rec( this->get_node( f ) ); + } ); + } + } + +private: + void create_topo_rec( node const& n ) + { + /* is permanently marked? */ + if ( this->visited( n ) == this->trav_id() ) + return; + + /* ensure that the node is not temporarily marked */ + assert( this->visited( n ) != this->trav_id() - 1 ); + + /* mark node temporarily */ + this->set_visited( n, this->trav_id() - 1 ); + + /* mark children */ + this->foreach_fanin( n, [this]( signal const& f ) { + create_topo_rec( this->get_node( f ) ); + } ); + + /* mark node n permanently */ + this->set_visited( n, this->trav_id() ); + + /* visit node */ + topo_order.push_back( n ); + } + +private: + std::vector topo_order; + std::optional start_signal; +}; + +template +class topo_view : public Ntk +{ +public: + topo_view( Ntk const& ntk ) : Ntk( ntk ) + { + } +}; + +template +topo_view( T const& ) -> topo_view; + +template +topo_view( T const&, typename T::signal const& ) -> topo_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/window_view.hpp b/third-party/mockturtle/include/mockturtle/views/window_view.hpp new file mode 100644 index 00000000000..4fafb45e972 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/window_view.hpp @@ -0,0 +1,308 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file window_view.hpp + \brief Implements an isolated view on a window in a network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/window_utils.hpp" +#include "immutable_view.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Implements an isolated view on a window in a network. + * + * This view creates a network from a window in a large network. The + * window is specified by three parameters: + * 1.) `inputs` are the common support of all window nodes, they do + * not overlap with `gates` (i.e., the intersection of `inputs` and + * `gates` is the empty set). + * 2.) `gates` are the nodes in the window, supported by the + * `inputs` (i.e., `gates` are in the transitive fanout of the + * `inputs`, + * 3.) `outputs` are signals (regular or complemented nodes) + * pointing to nodes in `gates` or `inputs`. Not all fanouts + * of an output node are already part of the window. + * + * The second parameter `gates` has to be passed and is not + * automatically computed (for example in contrast to `cut_view`), + * because there are different strategies to construct a window from a + * support set. The outputs could be automatically computed. + * + * The window_view implements three new API methods: + * 1.) `belongs_to`: takes a node (or a signal) and returns true if and + * only if the corresponding node belongs to the window + * 2.) `foreach_internal_fanout`: takes a node and invokes a predicate + on all fanout nodes of the node that belong to the window + * 3.) `foreach_external_fanout`: takes a node and invokes a predicate + on all fanouts of the node that do not belong to the window + */ +template +class window_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + template>> + explicit window_view( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates ) + : immutable_view( ntk ), _inputs( inputs ), _outputs( outputs ) + { + construct( inputs, gates ); + } + + explicit window_view( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates ) + : immutable_view( ntk ), _inputs( inputs ) + { + construct( inputs, gates ); + + /* convert output nodes to signals */ + std::transform( std::begin( outputs ), std::end( outputs ), std::back_inserter( _outputs ), + [this]( node const& n ) { + return this->make_signal( n ); + } ); + } + +#pragma region Window + template>> + inline bool belongs_to( signal const& s ) const + { + return std::find( std::begin( _nodes ), std::end( _nodes ), get_node( s ) ) != std::end( _nodes ); + } + + inline bool belongs_to( node const& n ) const + { + return std::find( std::begin( _nodes ), std::end( _nodes ), n ) != std::end( _nodes ); + } +#pragma endregion + +#pragma region Structural properties + inline uint32_t size() const + { + return static_cast( _nodes.size() ); + } + + inline uint32_t num_cis() const + { + return num_pis(); + } + + inline uint32_t num_cos() const + { + return num_pos(); + } + + inline uint32_t num_pis() const + { + return static_cast( _inputs.size() ); + } + + inline uint32_t num_pos() const + { + return static_cast( _outputs.size() ); + } + + inline uint32_t num_registers() const + { + return 0u; + } + + inline uint32_t num_gates() const + { + return static_cast( _nodes.size() - _inputs.size() - 1u ); + } + + inline uint32_t fanout_size( node const& n ) const = delete; + + inline uint32_t node_to_index( node const& n ) const + { + return _node_to_index.at( n ); + } + + inline node index_to_node( uint32_t index ) const + { + return _nodes[index]; + } + + inline bool is_pi( node const& n ) const + { + return std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs ); + } + + inline bool is_ci( node const& n ) const + { + return is_pi( n ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _outputs.size() ); + return *( std::begin( _outputs ) + index ); + } + + signal co_at( uint32_t index ) const + { + return po_at( index ); + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( std::begin( _inputs ), std::end( _inputs ), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( std::begin( _outputs ), std::end( _outputs ), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + foreach_pi( fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + foreach_po( fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + (void)fn; + } + + template + void foreach_ri( Fn&& fn ) const + { + (void)fn; + } + + template + void foreach_register( Fn&& fn ) const + { + (void)fn; + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( std::begin( _nodes ), std::end( _nodes ), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( std::begin( _nodes ) + 1u + _inputs.size(), std::end( _nodes ), fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + /* constants and inputs do not have fanins */ + if ( this->is_constant( n ) || + std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs ) ) + { + return; + } + + /* if it's not a window input, the node has to be a window node */ + assert( std::find( std::begin( _nodes ) + 1 + _inputs.size(), std::end( _nodes ), n ) != std::end( _nodes ) ); + immutable_view::foreach_fanin( n, fn ); + } + + template + void foreach_internal_fanout( node const& n, Fn&& fn ) const + { + this->foreach_fanout( n, [&]( node const& fo ) { + if ( tbelongs_to( fo ) ) + { + fn( fo ); + } + } ); + } + + template + void foreach_external_fanout( node const& n, Fn&& fn ) const + { + this->foreach_fanout( n, [&]( node const& fo ) { + if ( !belongs_to( fo ) ) + { + fn( fo ); + } + } ); + } +#pragma endregion + +protected: + void construct( std::vector const& inputs, std::vector const& gates ) + { + /* copy constant to nodes */ + _nodes.emplace_back( this->get_node( this->get_constant( false ) ) ); + + /* copy inputs to nodes */ + std::copy( std::begin( inputs ), std::end( inputs ), std::back_inserter( _nodes ) ); + + /* copy gates to nodes */ + std::copy( std::begin( gates ), std::end( gates ), std::back_inserter( _nodes ) ); + + /* create a mapping from node id (index in the original network) to window index */ + for ( uint32_t index = 0; index < _nodes.size(); ++index ) + { + _node_to_index[_nodes.at( index )] = index; + } + } + +protected: + std::vector _inputs; + std::vector _outputs; + std::vector _nodes; + std::unordered_map _node_to_index; +}; /* window_view */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/affine.hpp b/third-party/mockturtle/lib/kitty/kitty/affine.hpp new file mode 100644 index 00000000000..9020794ec09 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/affine.hpp @@ -0,0 +1,217 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file affine.hpp + \brief Implements affine canonization algorithms + + \author Mathias Soeken +*/ + +#pragma once + +#include "dynamic_truth_table.hpp" +#include "operators.hpp" +#include "static_truth_table.hpp" +#include "spectral.hpp" +#include "traits.hpp" + +/*! \cond PRIVATE */ +#include "detail/linear_constants.hpp" +/*! \endcond */ + +namespace kitty +{ + +/*! \cond PRIVATE */ +namespace detail +{ +/* all delta-swap operations have been optimized to work with integer masks omega */ +template +inline void delta_swap_inplace_opt( TT& tt, uint64_t delta, uint64_t omega ) +{ + assert( tt.num_vars() <= 6 ); + const uint64_t y = ( tt._bits[0] ^ ( tt._bits[0] >> delta ) ) & omega; + tt._bits[0] = tt._bits[0] ^ y ^ ( y << delta ); +} + +template +inline void delta_swap_inplace_opt( static_truth_table& tt, uint64_t delta, uint64_t omega ) +{ + assert( NumVars <= 6 ); + const uint64_t y = ( tt._bits ^ ( tt._bits >> delta ) ) & omega; + tt._bits = tt._bits ^ y ^ ( y << delta ); +} + +template +void permute_with_masks_inplace_opt( TT& tt, uint64_t const* masks ) +{ + for ( auto k = 0u; k < tt.num_vars(); ++k ) + { + delta_swap_inplace_opt( tt, uint64_t( 1 ) << k, masks[k] ); + } + + for ( int k = tt.num_vars() - 2, i = tt.num_vars(); k >= 0; --k, ++i ) + { + delta_swap_inplace_opt( tt, uint64_t( 1 ) << k, masks[i] ); + } +} + +template +TT permute_with_masks_opt( const TT& tt, uint64_t const* masks ) +{ + auto copy = tt; + permute_with_masks_inplace_opt( copy, masks ); + return copy; +} + +template +inline void for_each_permutation_mask( unsigned num_vars, Fn&& fn ) +{ + assert( num_vars >= 2 && num_vars <= 4 ); + + const auto offset = 2 * num_vars - 1; + + const auto s = detail::masks_start[num_vars - 2u]; + const auto e = detail::masks_end[num_vars - 2u]; + + for ( auto i = s; i < e; i += offset ) + { + fn( &detail::linear_masks[i] ); + } +} +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Applies exact linear classification + + This algorithms applies all linear input transformations to a function and + returns the function with the smallest integer representation as + representative of the equivalence class. + + \param tt Truth table +*/ +template +TT exact_linear_canonization( const TT& tt, Callback&& fn = detail::exact_spectral_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + detail::miller_spectral_canonization_impl impl( tt, false, false, false ); + return impl.run( fn ).first; +} + +/*! \cond PRIVATE */ +template +TT exact_linear_canonization_old( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + auto min = tt; + + detail::for_each_permutation_mask( tt.num_vars(), [&min, &tt]( const auto* mask ) + { min = std::min( min, detail::permute_with_masks_opt( tt, mask ) ); } ); + + return min; +} +/*! \endcond */ + +/*! \brief Applies exact linear classification and output negation + + This algorithms applies all linear input transformations to a function and its + complement and returns the function with the smallest integer representation + as representative of the equivalence class. + + \param tt Truth table +*/ +template +TT exact_linear_output_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + return std::min( exact_linear_canonization_old( tt ), exact_linear_canonization_old( ~tt ) ); +} + +/*! \brief Applies exact affine classification + + This algorithms applies all linear input transformations and input + complementations to a function and returns the function with the smallest + integer representation as representative of the equivalence class. + + \param tt Truth table +*/ +template +TT exact_affine_canonization( const TT& tt, Callback&& fn = detail::exact_spectral_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + detail::miller_spectral_canonization_impl impl( tt, true, false, false ); + return impl.run( fn ).first; +} + +/*! \cond PRIVATE */ +template +TT exact_affine_canonization_old( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + assert( num_vars >= 2 && num_vars <= 4 ); + + auto copy = tt; + + const auto& flips = detail::flips[num_vars - 2u]; + + auto min = exact_linear_canonization_old( copy ); + + for ( int j = static_cast( flips.size() ) - 1; j >= 0; --j ) + { + const auto pos = flips[j]; + flip_inplace( copy, pos ); + min = std::min( min, exact_linear_canonization_old( copy ) ); + } + + return min; +} +/*! \endcond */ + +/*! \brief Applies exact affine classification and output negation + + This algorithms applies all linear input transformations and input + complementations to a function and its complement and returns the function + with the smallest integer representation as representative of the equivalence + class. + + \param tt Truth table +*/ +template +TT exact_affine_output_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + return std::min( exact_affine_canonization_old( tt ), exact_affine_canonization_old( ~tt ) ); +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/algorithm.hpp b/third-party/mockturtle/lib/kitty/kitty/algorithm.hpp new file mode 100644 index 00000000000..d89e6ec2a37 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/algorithm.hpp @@ -0,0 +1,489 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file algorithm.hpp + \brief Implements several generic algorithms on truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include "bit_operations.hpp" +#include "detail/constants.hpp" +#include "static_truth_table.hpp" +#include "partial_truth_table.hpp" + +#include +#include + +namespace kitty +{ + +/*! \brief Perform bitwise unary operation on truth table + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto unary_operation( const TT& tt, Fn&& op ) +{ + auto result = tt.construct(); + std::transform( tt.cbegin(), tt.cend(), result.begin(), op ); + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto unary_operation( const static_truth_table& tt, Fn&& op ) +{ + auto result = tt.construct(); + result._bits = op( tt._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +/*! + \param num_blocks_offset Number of blocks that don't need to be computed + (the first `num_blocks_offset` blocks of `result` will remain the same before computation) + */ +template +void unary_operation( partial_truth_table& result, const partial_truth_table& tt, Fn&& op, int const& num_blocks_offset = 0 ) +{ + result.resize( tt.num_bits() ); + std::transform( tt.cbegin() + num_blocks_offset, tt.cend(), result.begin() + num_blocks_offset, op ); +} +/*! \endcond */ + +/*! \brief Perform bitwise binary operation on two truth tables + + The dimensions of `first` and `second` must match. This is ensured + at compile-time for static truth tables, but at run-time for dynamic + truth tables. + + \param first First truth table + \param second Second truth table + \param op Binary operation that takes as input two words (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto binary_operation( const TT& first, const TT& second, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() ); + + auto result = first.construct(); + std::transform( first.cbegin(), first.cend(), second.cbegin(), result.begin(), op ); + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto binary_operation( const static_truth_table& first, const static_truth_table& second, Fn&& op ) +{ + auto result = first.construct(); + result._bits = op( first._bits, second._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +auto binary_operation( const partial_truth_table& first, const partial_truth_table& second, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() ); + + auto result = first.construct(); + std::transform( first.cbegin(), first.cend(), second.cbegin(), result.begin(), op ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +/*! + \param num_blocks_offset Number of blocks that don't need to be computed + (the first `num_blocks_offset` blocks of `result` will remain the same before computation) + */ +template +void binary_operation( partial_truth_table& result, const partial_truth_table& first, const partial_truth_table& second, Fn&& op, int const& num_blocks_offset = 0 ) +{ + assert( first.num_bits() == second.num_bits() ); + + result.resize( first.num_bits() ); + std::transform( first.cbegin() + num_blocks_offset, first.cend(), second.cbegin() + num_blocks_offset, result.begin() + num_blocks_offset, op ); +} +/*! \endcond */ + +/*! \brief Perform bitwise ternary operation on three truth tables + + The dimensions of `first`, `second`, and `third` must match. This + is ensured at compile-time for static truth tables, but at run-time + for dynamic truth tables. + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param op Ternary operation that takes as input three words (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto ternary_operation( const TT& first, const TT& second, const TT& third, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() && second.num_vars() == third.num_vars() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++ ); + } + + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto ternary_operation( const static_truth_table& first, const static_truth_table& second, const static_truth_table& third, Fn&& op ) +{ + auto result = first.construct(); + result._bits = op( first._bits, second._bits, third._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +auto ternary_operation( const partial_truth_table& first, const partial_truth_table& second, const partial_truth_table& third, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() && second.num_bits() == third.num_bits() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++ ); + } + + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \brief Perform bitwise quaternary operation on four truth tables + + The dimensions of all truth tables must match. This + is ensured at compile-time for static truth tables, but at run-time + for dynamic truth tables. + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param fourth Fourth truth table + \param op Quaternary operation that takes as input four words (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto quaternary_operation( const TT& first, const TT& second, const TT& third, const TT& fourth, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() && second.num_vars() == third.num_vars() && third.num_vars() == fourth.num_vars() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it4 = fourth.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++, *it4++ ); + } + + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto quaternary_operation( const static_truth_table& first, const static_truth_table& second, const static_truth_table& third, const static_truth_table& fourth, Fn&& op ) +{ + auto result = first.construct(); + result._bits = op( first._bits, second._bits, third._bits, fourth._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +auto quaternary_operation( const partial_truth_table& first, const partial_truth_table& second, const partial_truth_table& third, const partial_truth_table& fourth, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() && second.num_bits() == third.num_bits() && third.num_bits() == fourth.num_bits() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it4 = fourth.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++, *it4++ ); + } + + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \brief Computes a predicate based on two truth tables + + The dimensions of `first` and `second` must match. This is ensured + at compile-time for static truth tables, but at run-time for dynamic + truth tables. + + \param first First truth table + \param second Second truth table + \param op Binary operation that takes as input two words (`uint64_t`) and returns a Boolean + + \return true or false based on the predicate + */ +template +bool binary_predicate( const TT& first, const TT& second, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() ); + + return std::equal( first.begin(), first.end(), second.begin(), op ); +} + +/*! \cond PRIVATE */ +template +bool binary_predicate( const static_truth_table& first, const static_truth_table& second, Fn&& op ) +{ + return op( first._bits, second._bits ); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +bool binary_predicate( const partial_truth_table& first, const partial_truth_table& second, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() ); + + return std::equal( first.begin(), first.end(), second.begin(), op ); +} +/*! \endcond */ + +/*! \brief Computes a predicate based on three truth tables + + The dimensions of `first`, `second` and `third` must match. This is ensured + at compile-time for static truth tables, but at run-time for dynamic + truth tables. + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param op Ternary operation that takes as input three words (`uint64_t`) and returns a Boolean + + \return true or false based on the predicate + */ +template +bool ternary_predicate( const TT& first, const TT& second, const TT& third, Fn&& op ) +{ + assert( first.num_blocks() == second.num_blocks() && first.num_blocks() == third.num_blocks() ); + + for ( auto i = 0u; i < first.num_blocks(); ++i ) + { + if ( !op( first._bits[i], second._bits[i], third._bits[i] ) ) + { + return false; + } + } + return true; +} + +/*! \cond PRIVATE */ +template +bool ternary_predicate( const static_truth_table& first, const static_truth_table& second, const static_truth_table& third, Fn&& op ) +{ + return op( first._bits, second._bits, third._bits ); +} +/*! \endcond */ + +/*! \brief Assign computed values to bits + + The functor `op` computes bits which are assigned to the bits of the + truth table. + + \param tt Truth table + \param op Unary operation that takes no input and returns a word (`uint64_t`) +*/ +template +void assign_operation( TT& tt, Fn&& op ) +{ + std::generate( tt.begin(), tt.end(), op ); + tt.mask_bits(); +} + +/*! \cond PRIVATE */ +template +void assign_operation( static_truth_table& tt, Fn&& op ) +{ + tt._bits = op(); + tt.mask_bits(); +} +/*! \endcond */ + +/*! \brief Iterates through each block of a truth table + + The functor `op` is called for every block of the truth table. + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns void +*/ +template +void for_each_block( const TT& tt, Fn&& op ) +{ + std::for_each( tt.cbegin(), tt.cend(), op ); +} + +/*! \brief Iterates through each block of a truth table in reverse + order + + The functor `op` is called for every block of the truth table in + reverse order. + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns void +*/ +template +void for_each_block_reversed( const TT& tt, Fn&& op ) +{ + std::for_each( tt.crbegin(), tt.crend(), op ); +} + +/*! \cond PRIVATE */ +template +void for_each_one_bit_naive( const TT& tt, Fn&& op ) +{ + for ( uint64_t bit = 0u; bit < tt.num_bits(); ++bit ) + { + if ( get_bit( tt, bit ) ) + { + op( bit ); + } + } +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +void for_each_one_bit_jump( const TT& tt, Fn&& op ) +{ + uint64_t offset = 0, low_bit, value; + + for ( auto block : tt._bits ) + { + while ( block ) + { + low_bit = value = block - ( block & ( block - 1 ) ); + + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + op( offset + detail::de_bruijn64[( static_cast( ( value - ( value >> 1 ) ) * UINT64_C( 0x07EDD5E59A4E28C2 ) ) ) >> 58] ); + + block ^= low_bit; + } + offset += 64; + } +} + +template +void for_each_one_bit_jump( const static_truth_table& tt, Fn&& op ) +{ + uint64_t block = tt._bits; + + while ( block ) + { + uint64_t low_bit = block - ( block & ( block - 1 ) ); + uint64_t value = low_bit; + + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + op( detail::de_bruijn64[( static_cast( ( value - ( value >> 1 ) ) * UINT64_C( 0x07EDD5E59A4E28C2 ) ) ) >> 58] ); + + block ^= low_bit; + } +} +/*! \endcond */ + +/*! \brief Iterates through each 1-bit in the truth table + + The functor `op` is called for every bit position of the truth table + for which the bit is assigned 1. + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns void +*/ +template +inline void for_each_one_bit( const TT& tt, Fn&& op ) +{ + for_each_one_bit_naive( tt, op ); +} +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/bit_operations.hpp b/third-party/mockturtle/lib/kitty/kitty/bit_operations.hpp new file mode 100644 index 00000000000..31ac78cddd9 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/bit_operations.hpp @@ -0,0 +1,580 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bit_operations.hpp + \brief Implements bit manipulation on truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "detail/mscfix.hpp" +#include "partial_truth_table.hpp" +#include "quaternary_truth_table.hpp" +#include "static_truth_table.hpp" +#include "ternary_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Sets bit at index to true + + \param tt Truth table + \param index Bit index +*/ +template +void set_bit( TT& tt, uint64_t index ) +{ + tt._bits[index >> 6] |= uint64_t( 1 ) << ( index & 0x3f ); +} + +/*! \cond PRIVATE */ +template +void set_bit( static_truth_table& tt, uint64_t index ) +{ + tt._bits |= uint64_t( 1 ) << index; +} + +template +void set_bit( ternary_truth_table& tt, uint64_t index, bool value = true ) +{ + set_bit( tt._care, index ); + if ( value ) + set_bit( tt._bits, index ); + else + clear_bit( tt._bits, index ); +} + +template +void set_bit( quaternary_truth_table& tt, uint64_t index, bool value = true ) +{ + if ( value ) + { + set_bit( tt._onset, index ); + clear_bit( tt._offset, index ); + } + else + { + clear_bit( tt._onset, index ); + set_bit( tt._offset, index ); + } +} + +/*! \endcond */ + +/*! \brief Gets bit at index + + \param tt Truth table + \param index Bit index + + \return 1 if bit is set, otherwise 0 +*/ +template +auto get_bit( const TT& tt, uint64_t index ) +{ + return ( tt._bits[index >> 6] >> ( index & 0x3f ) ) & 0x1; +} + +/*! \cond PRIVATE */ +template +auto get_bit( const static_truth_table& tt, uint64_t index ) +{ + return ( tt._bits >> index ) & 0x1; +} + +/*! \brief Gets bit at index + +\param tt Ternary truth table +\param index Bit index + +\return 1 if bit is set, 0 if it is reset or if it is a don't care +*/ +template +auto get_bit( const ternary_truth_table& tt, uint64_t index ) +{ + return get_bit( tt._bits, index ); +} + +/*! \brief Gets bit at index + +\param tt Ternary truth table +\param index Bit index + +\return 1 if bit is set, 0 if it is reset, if it is a don't care, or if it is a don't know +*/ +template +auto get_bit( const quaternary_truth_table& tt, uint64_t index ) +{ + return get_bit( tt._onset, index ) && !get_bit( tt._offset, index ); +} + +/*! \endcond */ + +/*! \brief Clears bit at index (sets bit at index to false) + + \param tt Truth table + \param index Bit index +*/ +template +void clear_bit( TT& tt, uint64_t index ) +{ + tt._bits[index >> 6] &= ~( uint64_t( 1 ) << ( index & 0x3f ) ); +} + +/*! \cond PRIVATE */ +template +void clear_bit( static_truth_table& tt, uint64_t index ) +{ + tt._bits &= ~( uint64_t( 1 ) << index ); +} +/*! \endcond */ + +/*! \brief Flips bit at index + + \param tt Truth table + \param index Bit index +*/ +template +void flip_bit( TT& tt, uint64_t index ) +{ + tt._bits[index >> 6] ^= uint64_t( 1 ) << ( index & 0x3f ); +} + +template +void flip_bit( ternary_truth_table& tt, uint64_t index ) +{ + if ( !get_bit( tt._care, index ) ) + return; + flip_bit( tt._bits, index ); +} + +/*! \cond PRIVATE */ +template +void flip_bit( static_truth_table& tt, uint64_t index ) +{ + tt._bits ^= uint64_t( 1 ) << index; +} + +/*! \brief Checks if a bit in a ternary truth table is a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +bool is_dont_care( const ternary_truth_table& tt, uint64_t index ) +{ + return !get_bit( tt._care, index ); +} + +/*! \brief Checks if a bit in a quaternary truth table is a don't care + + \param tt Quaternary truth table + \param index Bit index +*/ +template +bool is_dont_care( const quaternary_truth_table& tt, uint64_t index ) +{ + return get_bit( tt._onset, index ) && get_bit( tt._offset, index ); +} + +/*! \cond PRIVATE */ +template +bool is_dont_care( const TT& tt, uint64_t index ) +{ + (void)tt; + (void)index; + return false; +} + +/*! \brief Sets a bit in a ternary truth table as a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +void set_dont_care( ternary_truth_table& tt, uint64_t index ) +{ + clear_bit( tt._care, index ); + clear_bit( tt._bits, index ); +} + +/*! \brief Sets a bit in a quaternary truth table as a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +void set_dont_care( quaternary_truth_table& tt, uint64_t index ) +{ + set_bit( tt._onset, index ); + set_bit( tt._offset, index ); +} + +/*! \brief Checks if a bit in a ternary truth table is a don't know + + \param tt Ternary truth table + \param index Bit index +*/ +template +bool is_dont_know( const ternary_truth_table& tt, uint64_t index ) +{ + return !get_bit( tt._care, index ); +} + +/*! \brief Checks if a bit in a quaternary truth table is a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +bool is_dont_know( const quaternary_truth_table& tt, uint64_t index ) +{ + return !get_bit( tt._onset, index ) && !get_bit( tt._offset, index ); +} + +/*! \cond PRIVATE */ +template +bool is_dont_know( const TT& tt, uint64_t index ) +{ + (void)tt; + (void)index; + return false; +} + +template +void set_dont_know( quaternary_truth_table& tt, uint64_t index ) +{ + clear_bit( tt._onset, index ); + clear_bit( tt._offset, index ); +} + +/*! Returns a block of bits vector. + */ +template +uint64_t get_block( const TT& tt, uint64_t block_index ) +{ + assert( block_index < tt.num_blocks() ); + return tt._bits[block_index]; +} + +template +uint64_t get_block( const static_truth_table& tt, uint64_t block_index ) +{ + assert( block_index == 0 ); + (void)block_index; + return tt._bits; +} + +/*! \brief Copies bit at index + + Copy the bit from `tt_from` at index `index_from` to `tt_to` at index `index_to`. + + \param tt_from Truth table to copy from + \param index_from Bit index to copy from + \param tt_to Truth table to write to + \param index_to Bit index to write to +*/ +template +void copy_bit( const TTfrom& tt_from, uint64_t index_from, TTto& tt_to, uint64_t index_to ) +{ + if ( get_bit( tt_from, index_from ) ) + { + set_bit( tt_to, index_to ); + } + else + { + clear_bit( tt_to, index_to ); + } +} + +/*! \brief Clears all bits + + \param tt Truth table +*/ +template +void clear( TT& tt ) +{ + std::fill( std::begin( tt._bits ), std::end( tt._bits ), 0 ); +} + +/*! \cond PRIVATE */ +template +void clear( static_truth_table& tt ) +{ + tt._bits = 0; +} +/*! \endcond */ + +/*! \brief Count ones in truth table + + \param tt Truth table +*/ +template +inline uint64_t count_ones( const TT& tt ) +{ + return std::accumulate( tt.cbegin(), tt.cend(), uint64_t( 0 ), + []( auto accu, auto word ) { + return accu + __builtin_popcountll( word ); + } ); +} + +/*! \cond PRIVATE */ +template +inline uint64_t count_ones( const static_truth_table& tt ) +{ + return __builtin_popcountll( tt._bits ); +} + +template +inline uint64_t count_ones( const ternary_truth_table& tt ) +{ + return count_ones( tt._bits & tt._care ); +} +/*! \endcond */ + +/*! \brief Count zeros in truth table + + \param tt Truth table +*/ +template +inline uint64_t count_zeros( const TT& tt ) +{ + return count_ones( ~tt ); +} + +/*! \cond PRIVATE */ +inline int64_t find_first_bit_in_word( uint64_t word ) +{ + int64_t n = 0; + if ( word == 0 ) + { + return -1; + } + + if ( ( word & UINT64_C( 0x00000000FFFFFFFF ) ) == 0 ) + { + n += 32; + word >>= 32; + } + if ( ( word & UINT64_C( 0x000000000000FFFF ) ) == 0 ) + { + n += 16; + word >>= 16; + } + if ( ( word & UINT64_C( 0x00000000000000FF ) ) == 0 ) + { + n += 8; + word >>= 8; + } + if ( ( word & UINT64_C( 0x000000000000000F ) ) == 0 ) + { + n += 4; + word >>= 4; + } + if ( ( word & UINT64_C( 0x0000000000000003 ) ) == 0 ) + { + n += 2; + word >>= 2; + } + if ( ( word & UINT64_C( 0x0000000000000001 ) ) == 0 ) + { + n++; + } + + return n; +} + +inline int64_t find_last_bit_in_word( uint64_t word ) +{ + int64_t n = 0; + if ( word == 0 ) + { + return -1; + } + + if ( ( word & UINT64_C( 0xFFFFFFFF00000000 ) ) == 0 ) + { + n += 32; + word <<= 32; + } + if ( ( word & UINT64_C( 0xFFFF000000000000 ) ) == 0 ) + { + n += 16; + word <<= 16; + } + if ( ( word & UINT64_C( 0xFF00000000000000 ) ) == 0 ) + { + n += 8; + word <<= 8; + } + if ( ( word & UINT64_C( 0xF000000000000000 ) ) == 0 ) + { + n += 4; + word <<= 4; + } + if ( ( word & UINT64_C( 0xC000000000000000 ) ) == 0 ) + { + n += 2; + word <<= 2; + } + if ( ( word & UINT64_C( 0x8000000000000000 ) ) == 0 ) + { + n++; + } + + return 63 - n; +} +/*! \endcond */ + +/*! \brief Finds least-significant one-bit + + Returns -1, if truth table is constant 0. + + \param tt Truth table + \param start Bit to start from (default is 0) +*/ +template +int64_t find_first_one_bit( const TT& tt, int64_t start = 0 ) +{ + uint64_t mask = ~( ( UINT64_C( 1 ) << uint64_t( start % 64 ) ) - 1u ); + auto it = tt.cbegin() + ( start >> 6 ); + if ( it != tt.cend() && ( *it & mask ) != 0 ) + { + return 64 * std::distance( tt.cbegin(), it ) + find_first_bit_in_word( *it & mask ); + } + else if ( it == tt.cend() ) + { + return -1; + } + + it = std::find_if( it + 1, tt.cend(), []( auto word ) { return word != 0; } ); + + if ( it == tt.cend() ) + { + return -1; + } + + return 64 * std::distance( tt.cbegin(), it ) + find_first_bit_in_word( *it ); +} + +/*! \brief Finds most-significant one-bit + + Returns -1, if truth table is constant 0. + + \param tt Truth table +*/ +template +int64_t find_last_one_bit( const TT& tt ) +{ + const auto it = std::find_if( tt.crbegin(), tt.crend(), []( auto word ) { return word != 0; } ); + + if ( it == tt.crend() ) + { + return -1; + } + + return 64 * ( std::distance( it, tt.crend() ) - 1 ) + find_last_bit_in_word( *it ); +} + +/*! \brief Finds least-significant bit difference + + Returns -1, if truth tables are the same + + \param first First truth table + \param second Second truth table +*/ +template +int64_t find_first_bit_difference( const TT& first, const TT& second ) +{ + if constexpr ( std::is_same::value ) + { + assert( first.num_bits() == second.num_bits() ); + } + else + { + assert( first.num_vars() == second.num_vars() ); + } + + auto it = first.cbegin(); + auto it2 = second.cbegin(); + auto w = 0; + + while ( it != first.cend() ) + { + if ( *it ^ *it2 ) + { + return 64 * w + find_first_bit_in_word( *it ^ *it2 ); + } + ++it; + ++it2; + ++w; + } + return -1; +} + +/*! \brief Finds most-significant bit difference + + Returns -1, if truth tables are the same + + \param first First truth table + \param second Second truth table +*/ +template +int64_t find_last_bit_difference( const TT& first, const TT& second ) +{ + if constexpr ( std::is_same::value ) + { + assert( first.num_bits() == second.num_bits() ); + } + else + { + assert( first.num_vars() == second.num_vars() ); + } + + auto it = first.crbegin(); + auto it2 = second.crbegin(); + auto w = first.num_blocks() - 1; + + while ( it != first.crend() ) + { + if ( *it ^ *it2 ) + { + return 64 * w + find_last_bit_in_word( *it ^ *it2 ); + } + ++it; + ++it2; + --w; + } + return -1; +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/cnf.hpp b/third-party/mockturtle/lib/kitty/kitty/cnf.hpp new file mode 100644 index 00000000000..8a51ee7a37d --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/cnf.hpp @@ -0,0 +1,79 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cnf.hpp + \brief Implements methods to compute conjunctive normal forms (CNF) + + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "cube.hpp" +#include "isop.hpp" +#include "operators.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \brief Create CNF of the characteristic function + + Creates a CNF representation for the characteritic function of the input + function, also known as Tseytin transformation. To obtain small CNF, + an ISOP is computed. + + \param tt Truth table +*/ +template::value>> +std::vector cnf_characteristic( const TT& tt ) +{ + std::vector cubes; + detail::isop_rec( tt, tt, tt.num_vars(), cubes ); + + for ( auto& cube : cubes ) + { + cube._bits = ~cube._bits & cube._mask; + cube.add_literal( tt.num_vars(), true ); + } + + const auto end = cubes.size(); + + detail::isop_rec( ~tt, ~tt, tt.num_vars(), cubes ); + + for ( auto i = end; i < cubes.size(); ++i ) + { + auto& cube = cubes[i]; + cube._bits = ~cube._bits & cube._mask; + cube.add_literal( tt.num_vars(), false ); + } + + return cubes; +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/constructors.hpp b/third-party/mockturtle/lib/kitty/kitty/constructors.hpp new file mode 100644 index 00000000000..a7cb04919be --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/constructors.hpp @@ -0,0 +1,1587 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file constructors.hpp + \brief Implements operations to construct truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "cube.hpp" +#include "detail/constants.hpp" +#include "detail/mscfix.hpp" +#include "detail/utils.hpp" +#include "dynamic_truth_table.hpp" +#include "operations.hpp" +#include "operators.hpp" +#include "partial_truth_table.hpp" +#include "static_truth_table.hpp" +#include "ternary_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Creates truth table with number of variables + + If some truth table instance is given, one can create a truth table with the + same type by calling the `construct()` method on it. This function helps if + only the number of variables is known and the base type and uniforms the + creation of static and dynamic truth tables. Note, however, that for static + truth tables `num_vars` must be consistent to the number of variables in the + truth table type. + + \param num_vars Number of variables +*/ +template +inline TT create( unsigned num_vars ) +{ + (void)num_vars; + TT tt; + assert( tt.num_vars() == num_vars ); + return tt; +} + +/*! \cond PRIVATE */ +template<> +inline dynamic_truth_table create( unsigned num_vars ) +{ + return dynamic_truth_table( num_vars ); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template<> +inline partial_truth_table create( unsigned num_vars ) +{ + return partial_truth_table( 1 << num_vars ); +} +/*! \endcond */ + +/*! \brief Constructs projections (single-variable functions) + + \param tt Truth table + \param var_index Index of the variable, must be smaller than the truth table's number of variables + \param complement If true, realize inverse projection +*/ +template +void create_nth_var( TT& tt, uint8_t var_index, bool complement = false ) +{ + if constexpr ( std::is_same::value ) + { + assert( tt.num_bits() >= ( UINT64_C( 1 ) << var_index ) ); + if ( tt.num_bits() <= 64 ) + { + /* assign from precomputed table */ + tt._bits[0] = complement ? ~detail::projections[var_index] : detail::projections[var_index]; + + /* mask if truth table does not require all bits */ + tt.mask_bits(); + return; + } + } + else + { + if ( tt.num_vars() <= 6 ) + { + /* assign from precomputed table */ + tt._bits[0] = complement ? ~detail::projections[var_index] : detail::projections[var_index]; + + /* mask if truth table does not require all bits */ + tt.mask_bits(); + return; + } + } + + if ( var_index < 6 ) + { + std::fill( std::begin( tt._bits ), std::end( tt._bits ), complement ? ~detail::projections[var_index] : detail::projections[var_index] ); + } + else + { + const auto c = 1 << ( var_index - 6 ); + const auto zero = uint64_t( 0 ); + const auto one = ~zero; + auto block = uint64_t( 0u ); + + while ( block < tt.num_blocks() ) + { + for ( auto i = 0; i < c; ++i ) + { + tt._bits[block++] = complement ? one : zero; + } + for ( auto i = 0; i < c; ++i ) + { + tt._bits[block++] = complement ? zero : one; + } + } + } +} + +/*! \cond PRIVATE */ +template +void create_nth_var( static_truth_table& tt, uint8_t var_index, bool complement = false ) +{ + /* assign from precomputed table */ + tt._bits = complement ? ~detail::projections[var_index] : detail::projections[var_index]; + + /* mask if truth table does not require all bits */ + tt.mask_bits(); +} + +template +void create_nth_var( ternary_truth_table& tt, uint8_t var_index, bool complement = false ) +{ + create_nth_var( tt._bits, var_index, complement ); + tt._care = ~( tt._bits.construct() ); + /* mask if truth table does not require all bits */ + tt.mask_bits(); +} +/*! \endcond */ + +/*! \brief Constructs projections (single-variable functions) out-of-place + + \param tt Truth table + \param var_index Index of the variable, must be smaller than the truth table's number of variables + \param complement If true, realize inverse projection +*/ +template +TT nth_var( uint8_t num_vars, uint8_t var_index, bool complement = false ) +{ + TT tt = create( num_vars ); + create_nth_var( tt, var_index, complement ); + return tt; +} + +/*! \brief Constructs truth table from binary string + + Note that the first character in the string represents the most + significant bit in the truth table. For example, the 2-input AND + function is represented by the binary string "1000". The number of + characters in `binary` must match the number of bits in `tt`. + + \param tt Truth table + \param binary Binary string with as many characters as bits in the truth table +*/ +template +void create_from_binary_string( TT& tt, const std::string& binary ) +{ + assert( binary.size() == tt.num_bits() ); + + clear( tt ); + + size_t i = 0u, j = binary.size(); + do + { + --j; + if ( binary[i++] == '1' ) + { + set_bit( tt, j ); + } + } while ( j ); +} + +/*! \brief Constructs truth table from hexadecimal string + + Note that the first character in the string represents the four most + significant bit in the truth table. For example, the 3-input + majority function is represented by the binary string "E8" or "e8". + The number of characters in `hex` must be one fourth the number of + bits in `tt`. + + \param tt Truth table + \param hex Hexadecimal string +*/ +template +void create_from_hex_string( TT& tt, const std::string& hex ) +{ + clear( tt ); + + /* special case for small truth tables */ + if ( tt.num_vars() < 2 ) + { + assert( hex.size() == 1 ); + const auto i = detail::hex_to_int[static_cast( hex[0] )]; + if ( i & 1 ) + { + set_bit( tt, 0 ); + } + if ( tt.num_vars() == 1u && ( i & 2 ) ) + { + set_bit( tt, 1 ); + } + return; + } + + assert( ( hex.size() << 2 ) == tt.num_bits() ); + + auto j = tt.num_bits() - 1; + + for ( unsigned char c : hex ) + { + const auto i = detail::hex_to_int[c]; + if ( i & 8 ) + { + set_bit( tt, j ); + } + if ( i & 4 ) + { + set_bit( tt, j - 1 ); + } + if ( i & 2 ) + { + set_bit( tt, j - 2 ); + } + if ( i & 1 ) + { + set_bit( tt, j - 3 ); + } + j -= 4; + } +} + +/*! \cond PRIVATE */ +template> +void create_from_hex_string( partial_truth_table& tt, const std::string& hex ) +{ + clear( tt ); + + const auto len = hex.size() << 2; + assert( len >= tt.num_bits() && "truth table length too long" ); + assert( ( len - 4 ) < tt.num_bits() && "truth table length too short" ); + + auto j = tt.num_bits() - 1; + + /* the first char; may not use all 4 bits */ + auto i = detail::hex_to_int[static_cast( hex[0] )]; + + auto s = len - tt.num_bits(); /* number of leading bits not used. 0 <= s < 4 */ + + if ( ( s <= 0u ) && ( i & 8 ) ) + { + set_bit( tt, j ); + } + if ( ( s <= 1u ) && ( i & 4 ) ) + { + set_bit( tt, j + s - 1 ); + } + if ( ( s <= 2u ) && ( i & 2 ) ) + { + set_bit( tt, j + s - 2 ); + } + if ( ( s <= 3u ) && ( i & 1 ) ) + { + set_bit( tt, j + s - 3 ); + } + j -= static_cast( 4u - s ); + + for ( auto c = 1u; c < hex.size(); ++c ) + { + i = detail::hex_to_int[static_cast( hex[c] )]; + if ( i & 8 ) + { + set_bit( tt, j ); + } + if ( i & 4 ) + { + set_bit( tt, j - 1 ); + } + if ( i & 2 ) + { + set_bit( tt, j - 2 ); + } + if ( i & 1 ) + { + set_bit( tt, j - 3 ); + } + j -= 4; + } +} +/*! \endcond */ + +/*! \brief Creates string from raw character data + + Can create a truth table from the data that is produced by + `print_raw`, e.g., from binary files or `std::stringstream`. + + \param tt Truth table + \param in Input stream +*/ +template +void create_from_raw( TT& tt, std::istream& in ) +{ + std::for_each( tt.begin(), tt.end(), [&in]( auto& word ) + { in.read( reinterpret_cast( &word ), sizeof( word ) ); } ); +} + +/*! \brief Constructs a truth table from random value + + Computes random words and assigns them to the truth table. The + number of variables is determined from the truth table. + + \param tt Truth table + \param seed Random seed +*/ +template +void create_random( TT& tt, std::default_random_engine::result_type seed ) +{ + std::default_random_engine gen( seed ); + std::uniform_int_distribution dist( 0ul, std::numeric_limits::max() ); + + assign_operation( tt, [&dist, &gen]() + { return dist( gen ); } ); +} + +/*! \brief Constructs a truth table from random value + + Computes random words and assigns them to the truth table. The + number of variables is determined from the truth table. Seed is + taken from current time. + + \param tt Truth table +*/ +template +void create_random( TT& tt ) +{ + create_random( tt, static_cast( std::chrono::system_clock::now().time_since_epoch().count() ) ); +} + +/*! \brief Constructs a truth table from a range of words + + The range of words is given in terms of a begin and end iterator. + Hence, it's possible to copy words from a C++ container or a C + array. + + \param tt Truth table + \param begin Begin iterator + \param end End iterator +*/ +template +void create_from_words( TT& tt, InputIt begin, InputIt end ) +{ + assert( std::distance( begin, end ) == static_cast( tt.num_blocks() ) ); + std::copy( begin, end, tt.begin() ); +} + +/*! \brief Creates truth table from cubes representation + + A sum-of-product is represented as a vector of products (called + cubes). + + An empty truth table is given as first argument to determine type + and number of variables. Literals in products that do not fit the + number of variables of the truth table are ignored. + + The cube representation only allows truth table sizes up to 32 + variables. + + \param tt Truth table + \param cubes Vector of cubes + \param esop Use ESOP instead of SOP +*/ +template::value>> +void create_from_cubes( TT& tt, const std::vector& cubes, bool esop = false ) +{ + /* we collect product terms for an (E)SOP, start with const0 */ + clear( tt ); + + for ( auto cube : cubes ) + { + auto product = ~tt.construct(); /* const1 of same size */ + + auto bits = cube._bits; + auto mask = cube._mask; + + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( mask & 1 ) + { + auto var = tt.construct(); + create_nth_var( var, i, !( bits & 1 ) ); + product &= var; + } + bits >>= 1; + mask >>= 1; + } + + if ( esop ) + { + tt ^= product; + } + else + { + tt |= product; + } + } +} + +/*! \brief Creates truth table from clause representation + + A product-of-sum is represented as a vector of sums (called clauses). + + An empty truth table is given as first argument to determine type + and number of variables. Literals in sums that do not fit the + number of variables of the truth table are ignored. + + The clause representation only allows truth table sizes up to 32 + variables. + + \param tt Truth table + \param clauses Vector of clauses + \param esop Use product of exclusive sums instead of POS +*/ +template::value>> +void create_from_clauses( TT& tt, const std::vector& clauses, bool esop = false ) +{ + /* we collect product terms for an (E)SOP, start with const0 */ + clear( tt ); + tt = ~tt; + + for ( auto clause : clauses ) + { + auto sum = tt.construct(); /* const1 of same size */ + + auto bits = clause._bits; + auto mask = clause._mask; + + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( mask & 1 ) + { + auto var = tt.construct(); + create_nth_var( var, i, !( bits & 1 ) ); + + if ( esop ) + { + sum ^= var; + } + else + { + sum |= var; + } + } + bits >>= 1; + mask >>= 1; + } + + tt &= sum; + } +} + +/*! \brief Constructs majority-n function + + The number of variables is determined from the truth table. + + \param tt Truth table +*/ +template::value>> +inline void create_majority( TT& tt ) +{ + create_threshold( tt, tt.num_vars() >> 1 ); +} + +/*! \brief Constructs threshold function + + The resulting function is true, if strictly more than `threshold` inputs are + 1. The number of variables is determined from the truth table. + + \param tt Truth table + \param threshold threshold value +*/ +template +void create_threshold( TT& tt, uint8_t threshold ) +{ + clear( tt ); + + for ( uint64_t x = 0; x < tt.num_bits(); ++x ) + { + if ( __builtin_popcount( static_cast( x ) ) > threshold ) + { + set_bit( tt, x ); + } + } +} + +/*! \brief Constructs equals-k function + + The resulting function is true, if exactly `bitcount` bits are 1. The number + of variables is determiend from the truth table. + + \param tt Truth table + \param bitcount equals-k value +*/ +template::value>> +void create_equals( TT& tt, uint8_t bitcount ) +{ + clear( tt ); + if ( bitcount > tt.num_vars() ) + return; + + if ( tt.num_vars() <= 6 ) + { + const auto word = detail::onehots[tt.num_vars()][bitcount]; + create_from_words( tt, &word, &word + 1 ); + } + else + { + for ( uint64_t x = 0; x < tt.num_bits(); ++x ) + { + if ( __builtin_popcount( static_cast( x ) ) == bitcount ) + { + set_bit( tt, x ); + } + } + } +} + +/*! \brief Constructs symmetric function + + Bits in `counts` are numbered from 0 to 63. If bit `i` is set in `counts`, + the created truth table will evaluate to true, if `i` bits are set in the + input assignment. + + \param tt Truth table + \param counts Bitcount mask +*/ +template +void create_symmetric( TT& tt, uint64_t counts ) +{ + clear( tt ); + + for ( uint64_t x = 0; x < tt.num_bits(); ++x ) + { + if ( ( counts >> __builtin_popcount( static_cast( x ) ) ) & 1 ) + { + set_bit( tt, x ); + } + } +} + +/*! \brief Constructs parity function over n variables + + The number of variables is determined from the truth table. + + \param tt Truth table +*/ +template::value>> +void create_parity( TT& tt ) +{ + clear( tt ); + + *tt.begin() = UINT64_C( 0x6996966996696996 ); + + if ( tt.num_vars() < 6 ) + { + tt.mask_bits(); + } + else if ( tt.num_vars() > 6 ) + { + for ( auto i = 1u; i < tt.num_blocks(); i <<= 1 ) + { + std::transform( tt.begin(), tt.begin() + i, tt.begin() + i, []( auto const& block ) + { return ~block; } ); + } + } +} + +/*! \cond PRIVATE */ +template::value>> +bool create_from_chain( TT& tt, Fn&& next_line, std::vector& steps, std::string* error ) +{ + /* in case of error (makes code more readable) */ + auto fail_with = [&error]( const std::string& line, const std::string& message ) + { + if ( error ) + { + *error = "error in \"" + line + "\": " + message; + } + return false; + }; + + /* initialize variable steps */ + steps.clear(); + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + auto var = tt.construct(); + create_nth_var( var, i ); + steps.push_back( var ); + } + + auto next_step = tt.num_vars() + 1; + + std::string line; + while ( !( line = next_line() ).empty() ) + { + detail::trim( line ); + + /* first character must be an x */ + if ( line[0] != 'x' ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + /* find equals sign */ + const auto eq = line.find( '=' ); + if ( eq == std::string::npos ) + { + return fail_with( line, "no equal sign found" ); + } + + /* next step id */ + const auto step = static_cast( std::stoul( line.substr( 1, eq - 1 ) ) ); + if ( step != next_step ) + { + return fail_with( line, "steps are not in order" ); + } + + line = detail::trim_copy( line.substr( eq + 1 ) ); + + if ( line.empty() ) + { + return fail_with( line, "line uncompleted" ); + } + + /* first character must be an x */ + if ( line[0] != 'x' ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + std::size_t op_pos = 0; + const auto op1 = static_cast( std::stoul( line.substr( 1 ), &op_pos ) ); + + if ( op1 < 1 || op1 >= step ) + { + return fail_with( line, "invalid operand index" ); + } + + line = detail::trim_copy( line.substr( op_pos + 1 ) ); + + if ( line.empty() ) + { + return fail_with( line, "line uncompleted" ); + } + + const auto next_x = line.find( 'x' ); + + if ( next_x == std::string::npos ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + const auto op_code = detail::trim_copy( line.substr( 0, next_x ) ); + + line = detail::trim_copy( line.substr( next_x ) ); + + if ( line.empty() ) + { + return fail_with( line, "line uncompleted" ); + } + + /* first character must be an x */ + if ( line[0] != 'x' ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + const auto op2 = static_cast( std::stoul( line.substr( 1 ) ) ); + + if ( op2 < 1 || op2 >= step ) + { + return fail_with( line, "invalid operand index" ); + } + + /* now process arguments */ + auto tt_step = tt.construct(); + + if ( op_code == "!|" ) + { + tt_step = ~binary_or( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == ">" ) + { + tt_step = binary_and( steps[op1 - 1], ~steps[op2 - 1] ); + } + else if ( op_code == "<" ) + { + tt_step = binary_and( ~steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "^" ) + { + tt_step = binary_xor( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "!&" ) + { + tt_step = ~binary_and( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "&" ) + { + tt_step = binary_and( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "=" ) + { + tt_step = ~binary_xor( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "<=" ) + { + tt_step = binary_or( ~steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == ">=" ) + { + tt_step = binary_or( steps[op1 - 1], ~steps[op2 - 1] ); + } + else if ( op_code == "|" ) + { + tt_step = binary_or( steps[op1 - 1], steps[op2 - 1] ); + } + else + { + return fail_with( op_code, "invalid operator" ); + } + steps.push_back( tt_step ); + ++next_step; + } + + return true; +} +/*! \endcond */ + +/*! \brief Constructs truth table from Boolean chain + + If ``tt`` has \f$n\f$ variables, then each string in ``steps`` is of the form + + \verbatim embed:rst + :: + + x = x x + \endverbatim + + where ```` is an increasing number starting from \f$n + 1\f$, and ```` + and ```` refer to previous steps or primary inputs where \f$j < i\f$ and + \f$k < i\f$. Primary inputs are indexed from \f$1\f$ to \f$n\f$. The last + computed step will be assigned to ``tt``. The following operators are + supported: + + \verbatim embed:rst + +----------+-------------------------+-------------+ + | ```` | Operation | Truth table | + +==========+=========================+=============+ + | ``"!|"`` | Nondisjunction | 0001 | + +----------+-------------------------+-------------+ + | ``">"`` | Nonimplication | 0010 | + +----------+-------------------------+-------------+ + | ``"<"`` | Converse nonimplication | 0100 | + +----------+-------------------------+-------------+ + | ``"^"`` | Exclusive disjunction | 0110 | + +----------+-------------------------+-------------+ + | ``"!&"`` | Nonconjunction | 0111 | + +----------+-------------------------+-------------+ + | ``"&"`` | Conjunction | 1000 | + +----------+-------------------------+-------------+ + | ``"="`` | Equivalence | 1001 | + +----------+-------------------------+-------------+ + | ``">="`` | Nonimplication | 1011 | + +----------+-------------------------+-------------+ + | ``"<="`` | Implication | 1101 | + +----------+-------------------------+-------------+ + | ``"|"`` | Disjunction | 1110 | + +----------+-------------------------+-------------+ + \endverbatim + + The following example will generate the majority function: + + \verbatim embed:rst + .. code-block:: cpp + + kitty::static_truth_table<3> tt; + kitty::create_from_chain( tt, {"x4 = x1 & x2", + "x5 = x1 & x3", + "x6 = x2 & x3", + "x7 = x4 | x5", + "x8 = x6 | x7"} ); + \endverbatim + + If parsing fails, the function returns ``false``, and if ``error`` is not + ``nullptr``, it contains a descriptive reason, why parsing failed. Otherwise, + the function returns ``true``. + + \param tt Truth table + \param steps Vector of steps + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_from_chain( TT& tt, const std::vector& steps, std::string* error = nullptr ) +{ + std::vector vec_steps; + auto it = steps.begin(); + if ( !create_from_chain( + tt, [&it, &steps]() + { return ( it != steps.end() ) ? *it++ : std::string(); }, + vec_steps, error ) ) + { + return false; + } + + tt = vec_steps.back(); + return true; +} + +/*! \brief Constructs truth tables from Boolean chain + + Like ``create_from_chain``, but also returns all internally computed steps. + + \param num_vars Number of input variables + \param tts Truth table for all steps, tt[i] corresponds to step x\f$(i + 1)\f$ + \param steps Vector of steps + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_multiple_from_chain( unsigned num_vars, std::vector& tts, const std::vector& steps, std::string* error = nullptr ) +{ + auto tt = create( num_vars ); + tts.clear(); + auto it = steps.begin(); + if ( !create_from_chain( + tt, [&it, &steps]() + { return ( it != steps.end() ) ? *it++ : std::string(); }, + tts, error ) ) + { + return false; + } + + tt = tts.back(); + return true; +} + +/*! \brief Constructs truth table from Boolean chain + + Like the other ``create_from_chain`` function, but reads chain from an input + stream instead of a vector of strings. Lines are separated by a new line. + Empty lines are skipped over. + + \param tt Truth table + \param in Input stream to read chain + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_from_chain( TT& tt, std::istream& in, std::string* error = nullptr ) +{ + std::vector vec_steps; + if ( !create_from_chain( + tt, [&in]() + { + std::string line; + while ( true ) + { + if ( std::getline( in, line ) ) + { + detail::trim( line ); + if ( !line.empty() ) + { + return line; + } + } + else + { + return std::string(); + } + } }, + vec_steps, error ) ) + { + return false; + } + + tt = vec_steps.back(); + return true; +} + +/*! \brief Constructs truth tables from Boolean chain + + Like ``create_from_chain``, but also returns all internally computed steps. + + \param num_vars Number of input variables + \param tts Truth table for all steps, tt[i] corresponds to step x\f$(i + 1)\f$ + \param in Input stream to read chain + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_multiple_from_chain( unsigned num_vars, std::vector& tts, std::istream& in, std::string* error = nullptr ) +{ + auto tt = create( num_vars ); + tts.clear(); + if ( !create_from_chain( + tt, [&in]() + { + std::string line; + while ( true ) + { + if ( std::getline( in, line ) ) + { + detail::trim( line ); + if ( !line.empty() ) + { + return line; + } + } + else + { + return std::string(); + } + } }, + tts, error ) ) + { + return false; + } + + tt = tts.back(); + return true; +} + +/*! \brief Creates characteristic function + + Creates the truth table of the characteristic function, which contains one + additional variable. The new output variable will be the most-significant + variable of the new function. + + \param tt Truth table for characteristic function + \param from Input truth table +*/ +template::value>> +inline void create_characteristic( TT& tt, const TTFrom& from ) +{ + assert( tt.num_vars() == from.num_vars() + 1 ); + + auto var = tt.construct(); + create_nth_var( var, from.num_vars() ); + + auto ext = tt.construct(); + extend_to_inplace( ext, from ); + + tt = ~var ^ ext; +} + +/*! \brief Creates truth table from textual expression + + An expression `E` is a constant `0` or `1`, or a truth table `a`, + `b`, ..., `p` from the vector `input_tts`, the negation of an + expression `!E`, the conjunction of multiple expressions `(E...E)`, + the disjunction of multiple expressions `{E...E}`, the exclusive OR + of multiple expressions `[E...E]`, or the majority of three + expressions ``. + + \param tt Truth table + \param from Expression as string + \param input_tts Input truth tables assigned to a, b, ... +*/ +template::value>> +bool create_from_expression( TT& tt, const std::string& expression, std::vector const& input_tts ) +{ + enum stack_symbols + { + FUNC, + AND, + OR, + XOR, + MAJ, + NEG + }; + std::stack symbols; + std::stack truth_tables; + + const auto push_tt = [&]( TT& func ) + { + while ( !symbols.empty() && symbols.top() == NEG ) + { + func = ~func; + symbols.pop(); + } + symbols.push( FUNC ); + truth_tables.push( func ); + }; + + for ( auto const& c : expression ) + { + switch ( c ) + { + default: + if ( c >= 'a' && c <= 'p' ) + { + assert( input_tts.size() > uint64_t( c - 'a' ) ); + auto var = input_tts[c - 'a']; + push_tt( var ); + } + else + { + std::cerr << "[e] unexpected symbol in expression: " << c << "\n"; + return false; + } + break; + case '0': + { + auto func = tt.construct(); + push_tt( func ); + } + break; + case '1': + { + auto func = ~tt.construct(); + push_tt( func ); + } + break; + case '!': + symbols.push( NEG ); + break; + case '(': + symbols.push( AND ); + break; + case '{': + symbols.push( OR ); + break; + case '[': + symbols.push( XOR ); + break; + case '<': + symbols.push( MAJ ); + break; + case ')': + { + auto func = ~tt.construct(); + while ( !symbols.empty() && symbols.top() == FUNC ) + { + func &= truth_tables.top(); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != AND ) + { + std::cerr << "[e] could not parse AND expression\n"; + return false; + } + symbols.pop(); + push_tt( func ); + } + break; + case '}': + { + auto func = tt.construct(); + while ( !symbols.empty() && symbols.top() == FUNC ) + { + func |= truth_tables.top(); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != OR ) + { + std::cerr << "[e] could not parse OR expression\n"; + return false; + } + symbols.pop(); + push_tt( func ); + } + break; + case ']': + { + auto func = tt.construct(); + while ( !symbols.empty() && symbols.top() == FUNC ) + { + func ^= truth_tables.top(); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != XOR ) + { + std::cerr << "[e] could not parse XOR expression\n"; + return false; + } + symbols.pop(); + push_tt( func ); + } + break; + case '>': + { + std::vector children; + while ( !symbols.empty() && symbols.top() == FUNC ) + { + children.push_back( truth_tables.top() ); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != MAJ ) + { + std::cerr << "[e] could not parse MAJ expression\n"; + return false; + } + if ( children.size() != 3u ) + { + std::cerr << "[e] MAJ expression must have three children\n"; + return false; + } + symbols.pop(); + auto func = ternary_majority( children[0], children[1], children[2] ); + push_tt( func ); + } + break; + } + } + + if ( symbols.size() != 1 || truth_tables.size() != 1 ) + { + std::cerr << "[e] expression parsing incomplete\n"; + return false; + } + + tt = truth_tables.top(); + return true; +} + +/*! \brief Creates truth table from textual expression + + An expression `E` is a constant `0` or `1`, or a variable `a`, `b`, ..., `p`, + the negation of an expression `!E`, the conjunction of multiple expressions + `(E...E)`, the disjunction of multiple expressions `{E...E}`, the exclusive + OR of multiple expressions `[E...E]`, or the majority of three expressions + ``. Examples are `[(ab)(!ac)]` to describe if-then-else, or `!{!a!b}` + to describe the application of De Morgan's law to `(ab)`. The size of the + truth table must fit the largest variable in the expression, e.g., if `c` is + the largest variable, then the truth table have at least three variables. + + \param tt Truth table + \param from Expression as string +*/ +template::value>> +bool create_from_expression( TT& tt, const std::string& expression ) +{ + std::vector inputs_tts( tt.num_vars() ); + for ( uint8_t i = 0u; i < tt.num_vars(); ++i ) + { + auto var = tt.construct(); + create_nth_var( var, i ); + inputs_tts[i] = var; + } + return create_from_expression( tt, expression, inputs_tts ); +} + +namespace detail +{ +template::value>> +bool formula_execute_operation( std::stack& truth_tables, unsigned const op ) +{ + auto fn1 = truth_tables.top(); + truth_tables.pop(); + auto fn2 = truth_tables.top(); + truth_tables.pop(); + + if ( op == 3 ) /* AND */ + { + truth_tables.push( fn1 & fn2 ); + } + else if ( op == 2 ) /* XOR */ + { + truth_tables.push( fn1 ^ fn2 ); + } + else if ( op == 1 ) /* OR */ + { + truth_tables.push( fn1 | fn2 ); + } + else + { + return false; + } + return true; +} +} /* namespace detail */ + +/*! \brief Creates a truth table from a Boolean formula + + Translates a Boolean expression to a truth table with + the variable names and ordering defined by `var_names`. + The supported Boolean operations are the negation `!a` + or `a'`, the conjunction `a*b` or `a&b` or `a b`, + the disjunction `a+b`, or `a|b`, and the exclusive OR + `a^b`. Brackets `()` can be used for the operation + order. + + \param tt Truth table + \param from Expression as string + \param input_tts Variable names +*/ +template::value>> +bool create_from_formula( TT& tt, const std::string& expression, const std::vector& var_names ) +{ + enum stack_symbols + { + MARK = 0, + OR = 1, + XOR = 2, + AND = 3, + NEG = 4 + }; + + enum stack_op + { + START, + VAR, + OPER, + F_ERROR + }; + + /* create input truth tables */ + std::vector inputs_tts( tt.num_vars() ); + assert( tt.num_vars() < 256 ); + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + auto var = tt.construct(); + create_nth_var( var, static_cast( i ) ); + inputs_tts[i] = var; + } + + std::stack symbols; + std::stack truth_tables; + + /* check brackets */ + unsigned nbrackets = 0; + for ( char const& c : expression ) + { + if ( c == '(' ) + { + ++nbrackets; + } + else if ( c == ')' ) + { + --nbrackets; + } + } + + if ( nbrackets != 0 ) + { + std::cerr << "[e] different number of opening and closing brackets.\n"; + return false; + } + + stack_op flag = START; + auto temp_tt = tt.construct(); + for ( auto i = 0u; i < expression.length(); ++i ) + { + char const c = expression.at( i ); + switch ( c ) + { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + + case '0': + if ( flag == VAR ) + { + std::cerr << "[e] symbol before constant.\n"; + flag = F_ERROR; + break; + } + truth_tables.push( tt.construct() ); + flag = VAR; + break; + + case '1': + if ( flag == VAR ) + { + std::cerr << "[e] symbol before constant.\n"; + flag = F_ERROR; + break; + } + truth_tables.push( ~tt.construct() ); + flag = VAR; + break; + + case '!': + if ( flag == VAR ) + { + /* assuming an AND op */ + symbols.push( AND ); + flag = OPER; + } + symbols.push( NEG ); + break; + + case '\'': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before negation.\n"; + flag = F_ERROR; + break; + } + temp_tt = truth_tables.top(); + truth_tables.pop(); + truth_tables.push( ~temp_tt ); + break; + + case '*': + case '&': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before binary operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( AND ); + flag = OPER; + break; + + case '+': + case '|': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before binary operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( OR ); + flag = OPER; + break; + + case '^': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before binary operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( XOR ); + flag = OPER; + break; + + case '(': + if ( flag == VAR ) + { + /* assuming an AND op */ + symbols.push( AND ); + } + symbols.push( MARK ); + flag = START; + break; + + case ')': + if ( symbols.size() != 0 ) + { + while ( 1 ) + { + stack_symbols oper = symbols.top(); + symbols.pop(); + if ( oper == MARK ) + break; + + if ( !detail::formula_execute_operation( truth_tables, oper ) ) + { + std::cerr << "[e] unknown operation.\n"; + flag = F_ERROR; + break; + } + } + } + if ( flag != F_ERROR ) + { + flag = VAR; + } + break; + + default: + /* read variable name */ + auto j = 1u; + while ( i + j < expression.length() && + expression[i + j] != ' ' && expression[i + j] != '\t' && expression[i + j] != '\r' && + expression[i + j] != '\n' && expression[i + j] != '*' && expression[i + j] != '&' && + expression[i + j] != '+' && expression[i + j] != '|' && expression[i + j] != '^' && + expression[i + j] != '\'' && expression[i + j] != ')' ) + { + if ( expression[i + j] == '!' || expression[i + j] == '(' ) + { + std::cerr << "[e] negation sign or open bracket inside variable name.\n"; + flag = F_ERROR; + break; + } + ++j; + } + + uint32_t var_index = 0; + bool match = false; + for ( auto const& v : var_names ) + { + if ( expression.compare( i, j, v ) == 0 ) + { + match = true; + break; + } + ++var_index; + } + + if ( !match ) + { + std::cerr << "[e] cannot find variable " << expression.substr( i, j ) << " in variables list.\n"; + flag = F_ERROR; + break; + } + + if ( flag == VAR ) + { + symbols.push( AND ); + } + + /* increase pointer index */ + i += j - 1; + + truth_tables.push( inputs_tts[var_index] ); + + flag = VAR; + break; + } + + if ( flag == F_ERROR ) + { + break; + } + else if ( flag == START ) + { + continue; + } + else if ( flag == VAR ) + { + /* check if there are negations */ + while ( 1 ) + { + if ( symbols.size() == 0 ) + { + break; + } + auto oper = symbols.top(); + if ( oper == NEG ) + { + temp_tt = truth_tables.top(); + truth_tables.pop(); + truth_tables.push( ~temp_tt ); + symbols.pop(); + } + else + { + break; + } + } + } + else if ( flag == OPER ) + { + while ( 1 ) + { + /* execute the ops with the a higher priority than the last op */ + if ( symbols.size() == 1 ) + { + break; + } + auto op1 = symbols.top(); + symbols.pop(); + auto op2 = symbols.top(); + if ( op2 >= op1 ) + { + symbols.pop(); + /* execute previous op */ + if ( !detail::formula_execute_operation( truth_tables, op2 ) ) + { + std::cerr << "[e] unknown operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( op1 ); + } + else + { + /* push operations back */ + symbols.push( op1 ); + break; + } + } + } + } + + if ( flag != F_ERROR ) + { + /* last operation if present */ + while ( symbols.size() > 0 ) + { + if ( !detail::formula_execute_operation( truth_tables, symbols.top() ) ) + { + std::cerr << "[e] unknown operation.\n"; + flag = F_ERROR; + break; + } + symbols.pop(); + } + + /* assign truth table */ + tt = truth_tables.top(); + } + + return true; +} + +/*! \brief Creates function where on-set corresponds to prime numbers + + This creates a function in which \f$f(x) = 1\f$, if and only if \f$x\f$ is + a prime number in its integer representation. The function only works for + truth tables with at most 10 variables. The number of variables is determined + from the truth table. + + \param tt Truth table +*/ +template::value>> +void create_prime( TT& tt ) +{ + if ( tt.num_vars() > 10 ) + return; + + clear( tt ); + auto p = detail::primes; + + while ( *p < tt.num_bits() ) + { + set_bit( tt, *p++ ); + } +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/cube.hpp b/third-party/mockturtle/lib/kitty/kitty/cube.hpp new file mode 100644 index 00000000000..d634ea50e54 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/cube.hpp @@ -0,0 +1,333 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cube.hpp + \brief A cube data structure for up to 32 variables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "hash.hpp" +#include "detail/mscfix.hpp" + +namespace kitty +{ +class cube +{ +public: + /*! \brief Constructs the empty cube + + Represents the one-cube + */ + cube() : _value( 0 ) {} /* NOLINT */ + + /*! \brief Constructs a cube from bits and mask + + For a valid cube and to be consistent in the ternary values, we + assume that whenever a bit in the care bitmask is set to 0, also + the polarity bitmask must be 0. + + \param bits Polarity bitmask of variables (0: negative, 1: positive) + \param mask Care bitmask of variables (1: part of cube, 0: not part of cube) + */ + cube( uint32_t bits, uint32_t mask ) : _bits( bits ), _mask( mask ) {} /* NOLINT */ + + /*! \brief Constructs a cube from a string + + Each character corresponds to one literal in the cube. Only up to first 32 + characters of the string will be considered, since this data structure + cannot represent cubes with more than 32 literals. A '1' in the string + corresponds to a postive literal, a '0' corresponds to a negative literal. + All other characters represent don't care, but it is customary to use '-'. + + \param str String representing a cube + */ + // cppcheck-suppress noExplicitConstructor + cube( const std::string& str ) /* NOLINT */ + { + _bits = _mask = 0u; + + auto p = str.begin(); + if ( p == str.end() ) + { + return; + } + + for ( uint64_t i = 1; i <= ( uint64_t( 1u ) << 32u ); i <<= 1 ) + { + switch ( *p ) + { + default: /* don't care */ + break; + case '1': + _bits |= i; + /* no break on purpose, jump to 0 and set mask */ + case '0': + _mask |= i; + break; + } + + if ( ++p == str.end() ) + { + return; + } + } + } + + /*! \brief Returns number of literals */ + inline int num_literals() const + { + return __builtin_popcount( _mask ); + } + + /*! \brief Returns the difference to another cube */ + inline int difference( const cube& that ) const + { + return ( _bits ^ that._bits ) | ( _mask ^ that._mask ); + } + + /*! \brief Returns the distance to another cube */ + inline int distance( const cube& that ) const + { + return __builtin_popcount( difference( that ) ); + } + + /*! \brief Checks whether two cubes are equivalent */ + inline bool operator==( const cube& that ) const + { + return _value == that._value; + } + + /*! \brief Checks whether two cubes are not equivalent */ + inline bool operator!=( const cube& that ) const + { + return _value != that._value; + } + + /*! \brief Default comparison operator */ + inline bool operator<( const cube& that ) const + { + return _value < that._value; + } + + /*! \brief Returns the negated cube */ + inline cube operator~() const + { + return { ~_bits, _mask }; + } + + /*! \brief Merges two cubes of distance-1 */ + inline cube merge( const cube& that ) const + { + const auto d = difference( that ); + return { _bits ^ ( ~that._bits & d ), _mask ^ ( that._mask & d ) }; + } + + /*! \brief Adds literal to cube */ + inline void add_literal( uint8_t var_index, bool polarity = true ) + { + set_mask( var_index ); + + if ( polarity ) + { + set_bit( var_index ); + } + else + { + clear_bit( var_index ); + } + } + + /*! \brief Removes literal from cube */ + inline void remove_literal( uint8_t var_index ) + { + clear_mask( var_index ); + clear_bit( var_index ); + } + + /*! \brief Constructs the elementary cube representing a single variable */ + static cube nth_var_cube( uint8_t var_index ) + { + const auto _bits = uint32_t( 1 ) << var_index; + return { _bits, _bits }; + } + + /*! \brief Constructs the elementary cube containing the first k positive literals */ + static cube pos_cube( uint8_t k ) + { + const uint32_t _bits = ( uint64_t( 1 ) << k ) - 1; + return { _bits, _bits }; + } + + /*! \brief Constructs the elementary cube containing the first k negative literals */ + static cube neg_cube( uint8_t k ) + { + const uint32_t _bits = ( uint64_t( 1 ) << k ) - 1; + return { 0u, _bits }; + } + + /*! \brief Prints a cube */ + inline void print( unsigned length = 32u, std::ostream& os = std::cout ) const + { + for ( auto i = 0u; i < length; ++i ) + { + os << ( get_mask( i ) ? ( get_bit( i ) ? '1' : '0' ) : '-' ); + } + } + + /*! \brief Gets bit at index */ + inline bool get_bit( uint8_t index ) const + { + return ( ( _bits >> index ) & 1 ) != 0; + } + + /*! \brief Gets mask at index */ + inline bool get_mask( uint8_t index ) const + { + return ( ( _mask >> index ) & 1 ) != 0; + } + + /*! \brief Sets bit at index */ + inline void set_bit( uint8_t index ) + { + _bits |= ( 1 << index ); + } + + /*! \brief Sets mask at index */ + inline void set_mask( uint8_t index ) + { + _mask |= ( 1 << index ); + } + + /*! \brief Clears bit at index */ + inline void clear_bit( uint8_t index ) + { + _bits &= ~( 1 << index ); + } + + /*! \brief Clears mask at index */ + inline void clear_mask( uint8_t index ) + { + _mask &= ~( 1 << index ); + } + + /*! \brief Flips bit at index */ + inline void flip_bit( uint8_t index ) + { + _bits ^= ( 1 << index ); + } + + /*! \brief Flips mask at index */ + inline void flip_mask( uint8_t index ) + { + _mask ^= ( 1 << index ); + } + + /*! \brief Iterates over all minterms in the cube + * + * The callback function takes a cube as input, which is actually + * a minterm (i.e., all variables are set), and returns a boolean. + * The loop terminates when the callback returns false. + * + * \param length Number of variables in the cube + * \param fn Callback function on each minterm + */ + template + void foreach_minterm( uint8_t length, Fn&& fn ) const + { + foreach_minterm_rec( *this, length, fn ); + } + + template + bool foreach_minterm_rec( cube const& c, uint8_t prev_index, Fn&& fn ) const + { + if ( prev_index == 0 ) + { + return fn( c ); + } + + uint8_t index = prev_index - 1; + if ( !get_mask( index ) ) + { + cube c0 = c; + c0.set_mask( index ); + if ( !foreach_minterm_rec( c0, index, fn ) ) + { + return false; + } + c0.set_bit( index ); + return foreach_minterm_rec( c0, index, fn ); + } + else + { + return foreach_minterm_rec( c, index, fn ); + } + } + + /* cube data */ + union + { + struct + { + uint32_t _bits; + uint32_t _mask; + }; + uint64_t _value; + }; +}; + +/*! \brief Prints all cubes in a vector + + \param cubes Vector of cubes + \param length Number of variables in each cube + \param os Output stream +*/ +inline void print_cubes( const std::vector& cubes, unsigned length = 32u, std::ostream& os = std::cout ) +{ + for ( const auto& cube : cubes ) + { + cube.print( length, os ); + os << '\n'; + } + + os << std::flush; +} + +template<> +struct hash +{ + std::size_t operator()( const cube& c ) const + { + return std::hash{}( c._value ); + } +}; +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/decomposition.hpp b/third-party/mockturtle/lib/kitty/kitty/decomposition.hpp new file mode 100644 index 00000000000..fa368d98fdd --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/decomposition.hpp @@ -0,0 +1,956 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file decomposition.hpp + \brief Check decomposition properties (and perform decomposition) of a function + + \author Mahyar Emami (mahyar.emami@epfl.ch) + \author Mathias Soeken + \author Eleonora Testa +*/ + +#pragma once + +#include +#include + +#include "constructors.hpp" +#include "operations.hpp" +#include "implicant.hpp" +#include "traits.hpp" + +namespace kitty +{ + +enum class top_decomposition +{ + none, + and_, + or_, + lt_, + le_, + xor_ +}; + +enum class bottom_decomposition +{ + none, + and_, + or_, + lt_, + le_, + xor_ +}; + +enum class bi_decomposition +{ + none, + and_, + or_, + xor_, + weak_and_, + weak_or_ +}; + +/*! \brief Checks, whether function is top disjoint decomposable + + \verbatim embed:rst + Checks whether the input function ``tt`` can be represented by the function + :math:`f = g(h(X_1), a)`, where :math:`a \notin X_1`. The return value + is :math:`g`: + + * ``top_decomposition::and_``: :math:`g = a \land h(X_1)` + * ``top_decomposition::or_``: :math:`g = a \lor h(X_1)` + * ``top_decomposition::lt_``: :math:`g = \bar a \land h(X_1)` + * ``top_decomposition::le_``: :math:`g = \bar a \lor h(X_1)` + * ``top_decomposition::xor_``: :math:`g = a \oplus h(X_1)` + * ``top_decomposition::none``: decomposition does not exist + + The function can return the remainder function :math:`h`, whic will not depend + on :math:`a`. + \endverbatim + + \param tt Input function \f$f\f$ + \param var_index Variable \f$a\f$ + \param func If not ``null`` and decomposition exists, its value is assigned the remainder \f$h\f$ + \param allow_xor Set to false to disable XOR decomposition +*/ +template +top_decomposition is_top_decomposable( const TT& tt, uint32_t var_index, TT* func = nullptr, bool allow_xor = true ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + auto var = tt.construct(); + kitty::create_nth_var( var, var_index ); + + if ( implies( tt, var ) ) + { + if ( func ) + { + *func = cofactor1( tt, var_index ); + } + return top_decomposition::and_; + } + else if ( implies( var, tt ) ) + { + if ( func ) + { + *func = cofactor0( tt, var_index ); + } + return top_decomposition::or_; + } + else if ( implies( tt, ~var ) ) + { + if ( func ) + { + *func = cofactor0( tt, var_index ); + } + return top_decomposition::lt_; + } + else if ( implies( ~var, tt ) ) + { + if ( func ) + { + *func = cofactor1( tt, var_index ); + } + return top_decomposition::le_; + } + + if ( allow_xor ) + { + /* try XOR */ + const auto co0 = cofactor0( tt, var_index ); + const auto co1 = cofactor1( tt, var_index ); + + if ( equal( co0, ~co1 ) ) + { + if ( func ) + { + *func = co0; + } + return top_decomposition::xor_; + } + } + + return top_decomposition::none; +} + +/*! \brief Checks, whether function is bottom disjoint decomposable + + \verbatim embed:rst + Checks whether the input function ``tt`` can be represented by the function + :math:`f = h(X_1, g(a, b))`, where :math:`a, b \notin X_1`. The return value + is :math:`g`: + + * ``bottom_decomposition::and_``: :math:`g = a \land b` + * ``bottom_decomposition::or_``: :math:`g = a \lor b` + * ``bottom_decomposition::lt_``: :math:`g = \bar a \land b` + * ``bottom_decomposition::le_``: :math:`g = \bar a \lor b` + * ``bottom_decomposition::xor_``: :math:`g = a \oplus b` + * ``bottom_decomposition::none``: decomposition does not exist + + The function can return the remainder function :math:`h` in where :math:`g` + is substituted by :math:`a`. The remainder function will not depend on + :math:`b`. + \endverbatim + + \param tt Input function \f$f\f$ + \param var_index1 Variable \f$a\f$ + \param var_index2 Variable \f$b\f$ + \param func If not ``null`` and decomposition exists, its value is assigned the remainder \f$h\f$ + \param allow_xor Set to false to disable XOR decomposition +*/ +template +bottom_decomposition is_bottom_decomposable( const TT& tt, uint32_t var_index1, uint32_t var_index2, TT* func = nullptr, bool allow_xor = true ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = cofactor0( tt, var_index1 ); + const auto tt1 = cofactor1( tt, var_index1 ); + + const auto tt00 = cofactor0( tt0, var_index2 ); + const auto tt01 = cofactor1( tt0, var_index2 ); + const auto tt10 = cofactor0( tt1, var_index2 ); + const auto tt11 = cofactor1( tt1, var_index2 ); + + const auto eq01 = equal( tt00, tt01 ); + const auto eq02 = equal( tt00, tt10 ); + const auto eq03 = equal( tt00, tt11 ); + const auto eq12 = equal( tt01, tt10 ); + const auto eq13 = equal( tt01, tt11 ); + const auto eq23 = equal( tt10, tt11 ); + + const auto num_pairs = + static_cast( eq01 ) + + static_cast( eq02 ) + + static_cast( eq03 ) + + static_cast( eq12 ) + + static_cast( eq13 ) + + static_cast( eq23 ); + + if ( num_pairs != 2u && num_pairs != 3 ) + { + return bottom_decomposition::none; + } + + if ( !eq01 && !eq02 && !eq03 ) // 00 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt11, tt00 ); + } + return bottom_decomposition::or_; + } + else if ( !eq01 && !eq12 && !eq13 ) // 01 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt01, tt10 ); + } + return bottom_decomposition::lt_; + } + else if ( !eq02 && !eq12 && !eq23 ) // 10 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt01, tt10 ); + } + return bottom_decomposition::le_; + } + else if ( !eq03 && !eq13 && !eq23 ) // 11 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt11, tt00 ); + } + return bottom_decomposition::and_; + } + else if ( allow_xor ) // XOR + { + if ( func ) + { + *func = mux_var( var_index1, tt01, tt00 ); + } + return bottom_decomposition::xor_; + } + + return bottom_decomposition::none; +} + +/*! \cond PRIVATE */ +namespace detail +{ + +template +TT exist_set( const TT& tt, const std::vector& set ) +{ + auto exist = tt; + for ( auto x = 0u; x < set.size(); x++ ) + { + auto ra_0 = cofactor0( exist, set[x] ); + auto ra_1 = cofactor1( exist, set[x] ); + exist = binary_or( ra_0, ra_1 ); + } + return exist; +} + +template +TT select_one_cube( const TT& q ) +{ + auto m = q.construct(); + auto minterms = kitty::get_minterms( q ); + const uint64_t min = minterms[0]; + set_bit( m, min ); + for ( auto i = 0u; i < q.num_vars(); i++ ) + { + std::vector h( 1, i ); + auto m_p = exist_set( m, h ); + if ( binary_and( m_p, q ) == m_p ) + { + m = m_p; + } + } + return m; +} + +template +bool check_or_decomp( const TT& tt, const TT& dc, const std::vector& i, const std::vector& j ) +{ + auto q = binary_and( tt, dc ); + auto r = binary_and( ~tt, dc ); + auto p = binary_and( q, binary_and( exist_set( r, i ), exist_set( r, j ) ) ); + + return is_const0( p ); +} + +template +std::pair> check_xor_decomp( const TT& tt, const TT& dc, const std::vector& i, const std::vector& j ) +{ + auto q = binary_and( tt, dc ); + auto r = binary_and( ~tt, dc ); + + auto qa = tt.construct(); + auto ra = tt.construct(); + auto qb = tt.construct(); + auto rb = tt.construct(); + + std::vector q_and_rs{ qa, ra, qb, rb }; + while ( !is_const0( q ) ) + { + auto cube = select_one_cube( q ); + qa = binary_or( qa, exist_set( cube, j ) ); + while ( !is_const0( binary_or( qa, ra ) ) ) + { + qb = exist_set( binary_or( binary_and( q, ra ), binary_and( r, qa ) ), i ); + rb = exist_set( binary_or( binary_and( q, qa ), binary_and( r, ra ) ), i ); + if ( !is_const0( binary_and( qb, rb ) ) ) + { + return { false, q_and_rs }; + } + q = binary_and( q, ~binary_or( qa, ra ) ); + r = binary_and( r, ~binary_or( qa, ra ) ); + + q_and_rs[0] = binary_or( q_and_rs[0], qa ); + q_and_rs[1] = binary_or( q_and_rs[1], ra ); + + qa = exist_set( binary_or( binary_and( q, rb ), binary_and( r, qb ) ), j ); + ra = exist_set( binary_or( binary_and( q, qb ), binary_and( r, rb ) ), j ); + if ( !is_const0( binary_and( qa, ra ) ) ) + { + return { false, q_and_rs }; + } + q = binary_and( q, ~binary_or( qb, rb ) ); + r = binary_and( r, ~binary_or( qb, rb ) ); + + q_and_rs[2] = binary_or( q_and_rs[2], qb ); + q_and_rs[3] = binary_or( q_and_rs[3], rb ); + } + } + if ( !is_const0( r ) ) + { + q_and_rs[1] = binary_or( q_and_rs[1], exist_set( r, j ) ); + q_and_rs[3] = binary_or( q_and_rs[3], exist_set( r, i ) ); + } + return { true, q_and_rs }; +} + +template +bool check_weak_decomp( const TT& tt, const TT& dc, const std::vector& i ) +{ + auto p = exist_set( binary_and( ~tt, dc ), i ); + return !is_const0( binary_and( binary_and( tt, dc ), ~p ) ); +} + +template +std::pair, std::vector> find_initial_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector var_a_vect, var_b_vect; + for ( auto& i : s ) + { + for ( auto& j : s ) + { + if ( j == i ) + { + continue; + } + std::vector var_a_p( 1, i ); + std::vector var_b_p( 1, j ); + if ( check_or_decomp( tt, dc, var_a_p, var_b_p ) ) + { + var_a_vect.push_back( i ); + var_b_vect.push_back( j ); + return { var_a_vect, var_b_vect }; + } + } + } + return { var_a_vect, var_b_vect }; +} + +template +std::tuple, std::vector, std::vector> find_initial_xor( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector var_a_vect, var_b_vect; + std::vector q_and_r; + + for ( auto& i : s ) + { + for ( auto& j : s ) + { + if ( j == i ) + { + continue; + } + std::vector var_a_p( 1, i ); + std::vector var_b_p( 1, j ); + auto f = check_xor_decomp( tt, dc, var_a_p, var_b_p ); + if ( f.first ) + { + var_a_vect.push_back( i ); + var_b_vect.push_back( j ); + q_and_r = f.second; + return std::make_tuple( var_a_vect, var_b_vect, q_and_r ); + } + } + } + return std::make_tuple( var_a_vect, var_b_vect, q_and_r ); +} + +template +std::pair, std::vector> find_initial_weak_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector var_a_vect, var_b_vect; + for ( auto& i : s ) + { + std::vector var_a_p( 1, i ); + var_a_p.push_back( i ); + if ( check_weak_decomp( tt, dc, var_a_p ) ) + { + var_a_vect.push_back( i ); + var_b_vect.push_back( i ); + return { var_a_vect, var_b_vect }; + } + } + return { var_a_vect, var_b_vect }; +} + +template +std::pair, std::vector> group_variables_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector xa, xb; + auto var_x = find_initial_or( tt, dc, s ); + xa = var_x.first; + xb = var_x.second; + if ( ( xa.size() == 0 ) && ( xb.size() == 0 ) ) + { + return var_x; + } + for ( auto h : s ) + { + auto it = std::find( xa.begin(), xa.end(), h ); + if ( it != xa.end() ) + { + continue; + } + it = std::find( xb.begin(), xb.end(), h ); + if ( it != xb.end() ) + { + continue; + } + + if ( xa.size() <= xb.size() ) + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + if ( check_or_decomp( tt, dc, xa_p, xb ) ) + { + xa.push_back( h ); + } + else if ( check_or_decomp( tt, dc, xa, xb_p ) ) + { + xb.push_back( h ); + } + } + else + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + if ( check_or_decomp( tt, dc, xa, xb_p ) ) + { + xb.push_back( h ); + } + else if ( check_or_decomp( tt, dc, xa_p, xb ) ) + { + xa.push_back( h ); + } + } + } + return { xa, xb }; +} + +template +std::pair, std::vector> group_variables_weak_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector xa, xb; + auto var_x = find_initial_weak_or( tt, dc, s ); + xa = var_x.first; + xb = var_x.second; + if ( ( xa.size() == 0 ) && ( xb.size() == 0 ) ) + { + return var_x; + } + for ( auto h : s ) + { + auto it = std::find( xa.begin(), xa.end(), h ); + if ( it != xa.end() ) + { + continue; + } + auto xa_p = xa; + xa_p.push_back( h ); + if ( check_weak_decomp( tt, dc, xa_p ) ) + { + xa.push_back( h ); + } + } + return { xa, xb }; +} + +template +std::tuple, std::vector, std::vector> group_variables_xor( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector xa, xb; + std::vector q_and_r; + auto var_x = find_initial_xor( tt, dc, s ); + xa = std::get<0>( var_x ); + xb = std::get<1>( var_x ); + q_and_r = std::get<2>( var_x ); + if ( ( xa.size() == 0 ) && ( xb.size() == 0 ) ) + { + return var_x; + } + for ( auto h : s ) + { + auto it = std::find( xa.begin(), xa.end(), h ); + if ( it != xa.end() ) + { + continue; + } + it = std::find( xb.begin(), xb.end(), h ); + if ( it != xb.end() ) + { + continue; + } + + if ( xa.size() <= xb.size() ) + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + auto f = check_xor_decomp( tt, dc, xa_p, xb ); + auto f2 = check_xor_decomp( tt, dc, xa, xb_p ); + if ( f.first ) + { + xa.push_back( h ); + q_and_r = f.second; + } + else if ( f2.first ) + { + xb.push_back( h ); + q_and_r = f2.second; + } + } + else + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + auto f = check_xor_decomp( tt, dc, xa_p, xb ); + auto f2 = check_xor_decomp( tt, dc, xa, xb_p ); + if ( f2.first ) + { + xb.push_back( h ); + q_and_r = f2.second; + } + else if ( f.first ) + { + xa.push_back( h ); + q_and_r = f.second; + } + } + } + return std::make_tuple( xa, xb, q_and_r ); +} + +inline std::tuple, std::vector, bi_decomposition> best_variable_grouping( const std::pair, std::vector>& x_or, const std::pair, std::vector>& x_and, const std::pair, std::vector>& x_xor, bool xor_cost ) +{ + if ( xor_cost ) + { + if ( ( x_xor.first.size() != 0 ) && ( x_xor.second.size() != 0 ) ) + { + return std::make_tuple( x_xor.first, x_xor.second, bi_decomposition::xor_ ); + } + } + + int diff_or = static_cast( x_or.first.size() ) - static_cast( x_or.second.size() ); + if ( ( x_or.first.size() == 0 ) || ( x_or.second.size() == 0 ) ) + { + diff_or = 100; + } + if ( diff_or < 0 ) + { + diff_or = -diff_or; + } + + int diff_and = static_cast( x_and.first.size() ) - static_cast( x_and.second.size() ); + if ( ( x_and.first.size() == 0 ) || ( x_and.second.size() == 0 ) ) + { + diff_and = 100; + } + if ( diff_and < 0 ) + { + diff_and = -diff_and; + } + + int diff_xor = static_cast( x_xor.first.size() ) - static_cast( x_xor.second.size() ); + if ( ( x_xor.first.size() == 0 ) || ( x_xor.second.size() == 0 ) ) + { + diff_xor = 100; + } + if ( diff_xor < 0 ) + { + diff_xor = -diff_xor; + } + if ( ( diff_or == 100 ) && ( diff_and == 100 ) && ( diff_xor == 100 ) ) + { + return std::make_tuple( x_or.first, x_or.second, bi_decomposition::none ); + } + if ( ( diff_xor <= diff_and ) && ( diff_xor <= diff_or ) ) + { + return std::make_tuple( x_xor.first, x_xor.second, bi_decomposition::xor_ ); + } + else if ( ( diff_and <= diff_xor ) && ( diff_and <= diff_or ) ) + { + return std::make_tuple( x_and.first, x_and.second, bi_decomposition::and_ ); + } + else // if ( ( diff_xor <= diff_or ) && ( diff_xor <= diff_and ) ) + { + return std::make_tuple( x_or.first, x_or.second, bi_decomposition::or_ ); + } +} + +template +std::vector derive_b( const TT& tt, const TT& dc, const std::tuple, std::vector, bi_decomposition>& x_best, const TT& fa ) +{ + std::vector qb; + if ( ( std::get<2>( x_best ) == bi_decomposition::or_ ) || ( std::get<2>( x_best ) == bi_decomposition::weak_or_ ) ) + { + auto rb = exist_set( binary_and( ~tt, dc ), std::get<0>( x_best ) ); + qb.push_back( exist_set( binary_and( binary_and( tt, dc ), ~fa ), std::get<0>( x_best ) ) ); + assert( !is_const0( rb ) ); + qb.push_back( binary_or( qb[0], rb ) ); + return qb; + } + else // (( std::get<2>( x_best ) == bi_decomposition::and_) || ( std::get<2>( x_best ) == bi_decomposition::weak_and_) ) + { + qb.push_back( exist_set( binary_and( tt, dc ), std::get<0>( x_best ) ) ); + auto rb = exist_set( binary_and( binary_and( ~tt, dc ), ~fa ), std::get<0>( x_best ) ); + qb.push_back( binary_or( qb[0], rb ) ); + assert( !is_const0( qb[0] ) ); + return qb; + } +} + +template +std::vector derive_a( const TT& tt, const TT& dc, const std::tuple, std::vector, bi_decomposition>& x_best ) +{ + std::vector qa; + if ( std::get<2>( x_best ) == bi_decomposition::or_ ) + { + qa.push_back( exist_set( binary_and( binary_and( tt, dc ), exist_set( binary_and( ~tt, dc ), std::get<0>( x_best ) ) ), std::get<1>( x_best ) ) ); + auto ra = exist_set( binary_and( ~tt, dc ), std::get<1>( x_best ) ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } + else if ( std::get<2>( x_best ) == bi_decomposition::and_ ) + { + auto ra = exist_set( binary_and( binary_and( ~tt, dc ), exist_set( binary_and( tt, dc ), std::get<0>( x_best ) ) ), std::get<1>( x_best ) ); + qa.push_back( exist_set( binary_and( tt, dc ), std::get<1>( x_best ) ) ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } + else if ( std::get<2>( x_best ) == bi_decomposition::weak_or_ ) + { + qa.push_back( binary_and( binary_and( tt, dc ), exist_set( binary_and( ~tt, dc ), std::get<0>( x_best ) ) ) ); + auto ra = binary_and( ~tt, dc ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } + else // if ( std::get<2>( x_best ) == bi_decomposition::weak_and_ ) + { + qa.push_back( binary_and( tt, dc ) ); + auto ra = binary_and( binary_and( ~tt, dc ), exist_set( binary_and( tt, dc ), std::get<0>( x_best ) ) ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } +} + +template +std::tuple> is_bi_decomposable( const TT& tt, const TT& dc, bool cost ) +{ + + auto fa = tt.construct(); + auto fb = tt.construct(); + std::vector qa, qb; + + if ( is_const0( dc ) ) + { + return std::make_tuple( dc, bi_decomposition::none, qa ); + } + + std::vector support; + for ( auto x = 0u; x < tt.num_vars(); x++ ) + { + if ( has_var( binary_and( tt, dc ), x ) ) + { + support.push_back( x ); + } + } + + if ( support.size() <= 1 ) + { + return std::make_tuple( tt, bi_decomposition::none, qa ); + } + + auto x_or = detail::group_variables_or( tt, dc, support ); + auto x_and = detail::group_variables_or( ~tt, dc, support ); + auto x_xor = detail::group_variables_xor( tt, dc, support ); + auto x_best = detail::best_variable_grouping( x_or, x_and, std::make_pair( std::get<0>( x_xor ), std::get<1>( x_xor ) ), cost ); + + if ( std::get<2>( x_best ) == bi_decomposition::none ) + { + x_or = detail::group_variables_weak_or( tt, dc, support ); + if ( x_or.first.size() == 0 ) + { + x_and = detail::group_variables_weak_or( ~tt, dc, support ); + x_best = make_tuple( x_and.first, x_and.second, bi_decomposition::weak_and_ ); + } + else + x_best = make_tuple( x_or.first, x_or.second, bi_decomposition::weak_or_ ); + } + else if ( std::get<2>( x_best ) == bi_decomposition::xor_ ) + { + fa = std::get<2>( x_xor )[0]; + fb = std::get<2>( x_xor )[2]; + + qa = std::get<2>( x_xor ); + qa[1] = binary_or( qa[1], qa[0] ); + qa[3] = binary_or( qa[3], qa[2] ); + return std::make_tuple( binary_xor( fa, fb ), bi_decomposition::xor_, qa ); + } + + qa = detail::derive_a( tt, dc, x_best ); + if ( ( std::get<2>( x_best ) == bi_decomposition::or_ ) || ( std::get<2>( x_best ) == bi_decomposition::weak_or_ ) ) + { + qb = detail::derive_b( tt, dc, x_best, qa[0] ); + } + else + { + qb = detail::derive_b( tt, dc, x_best, binary_and( ~qa[0], qa[1] ) ); + } + + fa = qa[0]; + fb = qb[0]; + + qa.push_back( qb[0] ); + qa.push_back( qb[1] ); + + if ( ( std::get<2>( x_best ) == bi_decomposition::and_ ) || ( std::get<2>( x_best ) == bi_decomposition::weak_and_ ) ) + { + return std::make_tuple( binary_and( fa, fb ), std::get<2>( x_best ), qa ); + } + else + { + return std::make_tuple( binary_or( fa, fb ), std::get<2>( x_best ), qa ); + } +} + +} /* namespace detail */ +/* \endcond */ + +/*! \brief Checks whether a function is bi-decomposable. + + \verbatim embed:rst + Checks whether an incompletely specified function (ISF) ``tt`` can be represented by the function + :math:`f = h(l(X_1, X_3), g(X_2, X_3))`. + It returns a tuple of: + 1. :math:'f', which is a completely specified Boolean function compatible with the input ISF + 2. :math:'h', which is the type of decomposition (and, or, xor, weak and, weak or) + 3. :math:'l' and :math:'g', given as ISF (ON-set and DC-set) + + The algorithm is inspired by "An Algorithm for Bi-Decomposition of Logic Functions" by A. Mishchenko et al. + presented in DAC 2001. + + \endverbatim + + \param tt ON-set of the input function \f$f\f$ + \param dc DC-set of the input function \f$f\f$ +*/ + +template +std::tuple> is_bi_decomposable( const TT& tt, const TT& dc ) +{ + return detail::is_bi_decomposable( tt, dc, false ); +} + +/*! \brief Checks whether a function is bi-decomposable using XOR as preferred operation. + + \verbatim embed:rst + Checks whether an incompletely specified function (ISF) ``tt`` can be represented by the function + :math:`f = h(l(X_1, X_3), g(X_2, X_3))`. + It returns a tuple of: + 1. :math:'f', which is a completely specified Boolean function compatible with the input ISF + 2. :math:'h', which is the type of decomposition (and, or, xor, weak and, weak or) + 3. :math:'l' and :math:'g', given as ISF (ON-set and DC-set) + + The algorithm is inspired by "An Algorithm for Bi-Decomposition of Logic Functions" by A. Mishchenko et al. + presented in DAC 2001. + + The cost is changed to add more XOR gates compared to AND/OR gates. This cost function is motivated by + minimizing the number of AND gates in XAGs for cryptography and security applications. For these applications + XOR gates are "free". + + \endverbatim + + \param tt ON-set of the input function \f$f\f$ + \param dc DC-set of the input function \f$f\f$ +*/ + +template +std::tuple> is_bi_decomposable_mc( const TT& tt, const TT& dc ) +{ + return detail::is_bi_decomposable( tt, dc, true ); +} + +namespace detail +{ + +/*! \brief A helper function to enumerate missing indices. + + \param ys_index A list of already selected indices + \param max_index The maximum value for an index + \return Remaining indices +*/ +inline std::vector enumerate_zs_index( const std::vector& ys_index, uint32_t max_index ) +{ + std::vector zs_index; + for ( uint32_t i = 0u; i <= max_index; ++i ) + { + if ( std::find( ys_index.begin(), ys_index.end(), i ) == ys_index.end() ) + { + zs_index.push_back( i ); + } + } + + return zs_index; +} + +} // namespace detail + +/*! \brief Checks, whether a function is Ashenhurst decomposable. + + Given functions f(.), g(.), and h(.) and a partition + on arguments into z and y. This function determines whether + f(x) is decomposable into g(z, h(y)) where x = union(z,y) and + intersect(z, y) = null. + This function does not check for permutation of variables given by + zs_index and ys_index. The elements in these vectors are treated as ordered + values. + + \param tt The function to the check the decomposition on (function f) + \param zs_index The ordered set of indices of vector x (input to f) that + are the inputs to outer_func (g). + \param ys_index The ordered set of indices of vector x (input to f) that are + input to the inner_func (h). + \param outer_func The outer decomposition function (function g). + \param inner_func The inner decomposition function (function h). + \return true if the given decomposition is a valid one, false otherwise. +*/ +template +bool is_ashenhurst_decomposable( const TTf& tt, + const std::vector& zs_index, + const std::vector& ys_index, + const TTg& outer_func, + const TTh& inner_func ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector y_vars; + std::vector z_vars; + + for ( const auto idx : ys_index ) + { + auto var = tt.construct(); + create_nth_var( var, idx ); + y_vars.push_back( var ); + } + for ( const auto idx : zs_index ) + { + auto var = tt.construct(); + create_nth_var( var, idx ); + z_vars.push_back( var ); + } + auto h = compose_truth_table( inner_func, y_vars ); + z_vars.push_back( h ); + auto f = compose_truth_table( outer_func, z_vars ); + return equal( f, tt ); +} + +/*! \brief Finds all of the possible Ashenhurst decompositions of a function + given an input partitioning. + + \param tt The function to find all of its decompositions + \param ys_index Indices indicating the partitioning of inputs + \param decomposition A vector of decomposition pairs. This serves as a return + return container. + \return Returns the number of possible decompositions. +*/ +template +uint32_t ashenhurst_decomposition( const TTf& tt, const std::vector& ys_index, std::vector>& decomposition ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector zs_index = detail::enumerate_zs_index( ys_index, tt.num_vars() - 1 ); + decomposition.clear(); + + // TODO: this does not work for dynamic_truth_table (number of variables not known) + TTg g; + do + { + TTh h; + do + { + if ( is_ashenhurst_decomposable( tt, zs_index, ys_index, g, h ) ) + { + decomposition.emplace_back( g, h ); + } + next_inplace( h ); + } while ( !is_const0( h ) ); + next_inplace( g ); + } while ( !is_const0( g ) ); + return static_cast( decomposition.size() ); +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/constants.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/constants.hpp new file mode 100644 index 00000000000..8f57d04f45c --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/constants.hpp @@ -0,0 +1,196 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file constants.hpp + \brief Collects several important constants + + \author Mathias Soeken +*/ + +/*! \cond PRIVATE */ +#pragma once + +#include +#include + +namespace kitty +{ + +namespace detail +{ + +static constexpr uint64_t projections[] = { + UINT64_C( 0xaaaaaaaaaaaaaaaa ), + UINT64_C( 0xcccccccccccccccc ), + UINT64_C( 0xf0f0f0f0f0f0f0f0 ), + UINT64_C( 0xff00ff00ff00ff00 ), + UINT64_C( 0xffff0000ffff0000 ), + UINT64_C( 0xffffffff00000000 ) }; + +static constexpr uint64_t projections_neg[] = { + UINT64_C( 0x5555555555555555 ), + UINT64_C( 0x3333333333333333 ), + UINT64_C( 0x0f0f0f0f0f0f0f0f ), + UINT64_C( 0x00ff00ff00ff00ff ), + UINT64_C( 0x0000ffff0000ffff ), + UINT64_C( 0x00000000ffffffff ) }; + +static constexpr uint64_t masks[] = { + UINT64_C( 0x0000000000000001 ), + UINT64_C( 0x0000000000000003 ), + UINT64_C( 0x000000000000000f ), + UINT64_C( 0x00000000000000ff ), + UINT64_C( 0x000000000000ffff ), + UINT64_C( 0x00000000ffffffff ), + UINT64_C( 0xffffffffffffffff ) }; + +static constexpr uint64_t permutation_masks[][3] = { + { UINT64_C( 0x9999999999999999 ), UINT64_C( 0x2222222222222222 ), UINT64_C( 0x4444444444444444 ) }, + { UINT64_C( 0xc3c3c3c3c3c3c3c3 ), UINT64_C( 0x0c0c0c0c0c0c0c0c ), UINT64_C( 0x3030303030303030 ) }, + { UINT64_C( 0xf00ff00ff00ff00f ), UINT64_C( 0x00f000f000f000f0 ), UINT64_C( 0x0f000f000f000f00 ) }, + { UINT64_C( 0xff0000ffff0000ff ), UINT64_C( 0x0000ff000000ff00 ), UINT64_C( 0x00ff000000ff0000 ) }, + { UINT64_C( 0xffff00000000ffff ), UINT64_C( 0x00000000ffff0000 ), UINT64_C( 0x0000ffff00000000 ) } }; + +static constexpr uint64_t ppermutation_masks[][6][3] = { + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x9999999999999999 ), UINT64_C( 0x2222222222222222 ), UINT64_C( 0x4444444444444444 ) }, + { UINT64_C( 0xa5a5a5a5a5a5a5a5 ), UINT64_C( 0x0a0a0a0a0a0a0a0a ), UINT64_C( 0x5050505050505050 ) }, + { UINT64_C( 0xaa55aa55aa55aa55 ), UINT64_C( 0x00aa00aa00aa00aa ), UINT64_C( 0x5500550055005500 ) }, + { UINT64_C( 0xaaaa5555aaaa5555 ), UINT64_C( 0x0000aaaa0000aaaa ), UINT64_C( 0x5555000055550000 ) }, + { UINT64_C( 0xaaaaaaaa55555555 ), UINT64_C( 0x00000000aaaaaaaa ), UINT64_C( 0x5555555500000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xc3c3c3c3c3c3c3c3 ), UINT64_C( 0x0c0c0c0c0c0c0c0c ), UINT64_C( 0x3030303030303030 ) }, + { UINT64_C( 0xcc33cc33cc33cc33 ), UINT64_C( 0x00cc00cc00cc00cc ), UINT64_C( 0x3300330033003300 ) }, + { UINT64_C( 0xcccc3333cccc3333 ), UINT64_C( 0x0000cccc0000cccc ), UINT64_C( 0x3333000033330000 ) }, + { UINT64_C( 0xcccccccc33333333 ), UINT64_C( 0x00000000cccccccc ), UINT64_C( 0x3333333300000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xf00ff00ff00ff00f ), UINT64_C( 0x00f000f000f000f0 ), UINT64_C( 0x0f000f000f000f00 ) }, + { UINT64_C( 0xf0f00f0ff0f00f0f ), UINT64_C( 0x0000f0f00000f0f0 ), UINT64_C( 0x0f0f00000f0f0000 ) }, + { UINT64_C( 0xf0f0f0f00f0f0f0f ), UINT64_C( 0x00000000f0f0f0f0 ), UINT64_C( 0x0f0f0f0f00000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xff0000ffff0000ff ), UINT64_C( 0x0000ff000000ff00 ), UINT64_C( 0x00ff000000ff0000 ) }, + { UINT64_C( 0xff00ff0000ff00ff ), UINT64_C( 0x00000000ff00ff00 ), UINT64_C( 0x00ff00ff00000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xffff00000000ffff ), UINT64_C( 0x00000000ffff0000 ), UINT64_C( 0x0000ffff00000000 ) } } }; + +static std::vector onehots[] = { + { UINT64_C( 0x1 ) }, + { UINT64_C( 0x1 ), UINT64_C( 0x2 ) }, + { UINT64_C( 0x1 ), UINT64_C( 0x6 ), UINT64_C( 0x8 ) }, + { UINT64_C( 0x01 ), UINT64_C( 0x16 ), UINT64_C( 0x68 ), UINT64_C( 0x80 ) }, + { UINT64_C( 0x0001 ), UINT64_C( 0x0116 ), UINT64_C( 0x1668 ), UINT64_C( 0x6880 ), UINT64_C( 0x8000 ) }, + { UINT64_C( 0x00000001 ), UINT64_C( 0x00010116 ), UINT64_C( 0x01161668 ), UINT64_C( 0x16686880 ), UINT64_C( 0x68808000 ), UINT64_C( 0x80000000 ) }, + { UINT64_C( 0x0000000000000001 ), + UINT64_C( 0x0000000100010116 ), + UINT64_C( 0x0001011601161668 ), + UINT64_C( 0x0116166816686880 ), + UINT64_C( 0x1668688068808000 ), + UINT64_C( 0x6880800080000000 ), + UINT64_C( 0x8000000000000000 ) } }; + +static constexpr int32_t hex_to_int[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +/*! adjacent swap sequences (from 2 to 7 variables) */ +static std::vector swaps[] = { { 0 }, + { 1, 0, 1, 0, 1 }, + { 2, 1, 0, 2, 0, 1, 2, 0, 2, 1, 0, 2, 0, 1, 2, 0, 2, 1, 0, 2, 0, 1, 2 }, + { 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3, 0, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3, 0, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3 }, + { 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4 }, + { 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, + 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, + 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, + 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, + 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, + 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, + 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, + 0, 1, 2, 3, 4, 5 } }; + +/*! flip sequences (from 2 to 6 variables) */ +static std::vector flips[] = { { 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 } }; + +static constexpr uint8_t de_bruijn64[] = { 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5 }; + +static constexpr uint16_t primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, + 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, + 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, + 167, 173, 179, 181, 191, 193, 197, 199, + 211, 223, 227, 229, 233, 239, 241, 251, + 257, 263, 269, 271, 277, 281, 283, 293, + 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, + 401, 409, 419, 421, 431, 433, 439, 443, + 449, 457, 461, 463, 467, 479, 487, 491, + 499, 503, 509, 521, 523, 541, 547, 557, + 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, + 653, 659, 661, 673, 677, 683, 691, 701, + 709, 719, 727, 733, 739, 743, 751, 757, + 761, 769, 773, 787, 797, 809, 811, 821, + 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, + 937, 941, 947, 953, 967, 971, 977, 983, + 991, 997, 1009, 1013, 1019, 1021, 1031 }; + +static constexpr int8_t log2[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, + -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 6 }; + +} // namespace detail +} // namespace kitty + /*! \endcond */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/linear_constants.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/linear_constants.hpp new file mode 100644 index 00000000000..481492c5b81 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/linear_constants.hpp @@ -0,0 +1,51 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file linear_constants.hpp + \brief Collects several constants for permutation and linear classification + + \author Mathias Soeken +*/ + +/*! \cond PRIVATE */ +#pragma once + +#include + +namespace kitty +{ + +namespace detail +{ +const uint64_t linear_masks[] = { 0x4, 0x2, 0x4, 0x0, 0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x2, 0x0, 0x4, 0x2, 0x0, 0x50, 0x0, 0xa, 0x0, 0x50, 0x0, 0x0, 0xa, 0x0, 0x50, 0x50, 0x0, 0x6, 0x0, 0x14, 0x0, 0x0, 0x6, 0x0, 0x14, 0x50, 0x0, 0xa, 0x12, 0x50, 0x0, 0x0, 0xa, 0x12, 0x50, 0x50, 0x30, 0x6, 0x0, 0x14, 0x0, 0x30, 0x6, 0x0, 0x14, 0x50, 0x30, 0xc, 0x12, 0x44, 0x0, 0x30, 0xc, 0x12, 0x44, 0x50, 0x30, 0xc, 0x22, 0x14, 0x0, 0x30, 0xc, 0x22, 0x14, 0x50, 0x0, 0xc, 0x12, 0x44, 0x0, 0x0, 0xc, 0x12, 0x44, 0x50, 0x0, 0xc, 0x22, 0x14, 0x0, 0x0, 0xc, 0x22, 0x14, 0x50, 0x0, 0x6, 0x30, 0x44, 0x0, 0x0, 0x6, 0x30, 0x44, 0x50, 0x0, 0xa, 0x30, 0x50, 0x0, 0x0, 0xa, 0x30, 0x50, 0x50, 0x30, 0x6, 0x30, 0x44, 0x0, 0x30, 0x6, 0x30, 0x44, 0x50, 0x0, 0xa, 0x22, 0x50, 0x0, 0x0, 0xa, 0x22, 0x50, 0x44, 0x30, 0x6, 0x12, 0x50, 0x0, 0x30, 0x6, 0x12, 0x50, 0x44, 0x0, 0x6, 0x12, 0x14, 0x0, 0x0, 0x6, 0x12, 0x14, 0x44, 0x30, 0x6, 0x12, 0x14, 0x0, 0x0, 0x6, 0x12, 0x50, 0x44, 0x0, 0x6, 0x12, 0x50, 0x0, 0x30, 0x6, 0x12, 0x14, 0x0, 0x30, 0xc, 0x30, 0x0, 0x0, 0x30, 0xc, 0x30, 0x44, 0x0, 0x30, 0xc, 0x30, 0x50, 0x0, 0x30, 0xc, 0x30, 0x14, 0x0, 0x30, 0x6, 0x12, 0x0, 0x0, 0x0, 0x6, 0x12, 0x44, 0x44, 0x30, 0x6, 0x12, 0x0, 0x44, 0x0, 0x6, 0x12, 0x44, 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x0, 0xc, 0x30, 0x44, 0x0, 0x0, 0xc, 0x30, 0x50, 0x0, 0x0, 0xc, 0x30, 0x14, 0x0, 0x0, 0x6, 0x12, 0x0, 0x0, 0x30, 0x6, 0x12, 0x44, 0x44, 0x30, 0x6, 0x12, 0x44, 0x44, 0x0, 0x6, 0x12, 0x0, 0x50, 0x0, 0xa, 0x12, 0x14, 0x0, 0x30, 0x6, 0x0, 0x50, 0x50, 0x30, 0x6, 0x0, 0x50, 0x0, 0x0, 0xa, 0x12, 0x14, 0x50, 0x0, 0xa, 0x0, 0x14, 0x0, 0x0, 0x6, 0x0, 0x50, 0x50, 0x0, 0x6, 0x0, 0x50, 0x0, 0x0, 0xa, 0x0, 0x14, 0x0, 0x30, 0xc, 0x12, 0x0, 0x50, 0x30, 0xc, 0x12, 0x0, 0x0, 0x30, 0xc, 0x22, 0x50, 0x50, 0x30, 0xc, 0x22, 0x50, 0x0, 0x30, 0x6, 0x30, 0x0, 0x50, 0x30, 0x6, 0x30, 0x0, 0x50, 0x0, 0xa, 0x22, 0x14, 0x0, 0x0, 0xa, 0x22, 0x14, 0x0, 0x0, 0x6, 0x30, 0x0, 0x50, 0x0, 0x6, 0x30, 0x0, 0x50, 0x0, 0xa, 0x30, 0x14, 0x0, 0x0, 0xa, 0x30, 0x14, 0x0, 0x0, 0xc, 0x12, 0x0, 0x50, 0x0, 0xc, 0x12, 0x0, 0x0, 0x0, 0xc, 0x22, 0x50, 0x50, 0x0, 0xc, 0x22, 0x50, 0x44, 0x0, 0x0, 0x22, 0x44, 0x0, 0x0, 0x0, 0x22, 0x44, 0x44, 0x0, 0x0, 0x12, 0x14, 0x0, 0x0, 0x0, 0x12, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x22, 0x0, 0x44, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x12, 0x50, 0x44, 0x0, 0x0, 0x12, 0x50, 0x44, 0x0, 0x0, 0x22, 0x14, 0x0, 0x0, 0x0, 0x12, 0x44, 0x44, 0x0, 0x0, 0x12, 0x44, 0x0, 0x0, 0x0, 0x22, 0x14, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x30, 0x44, 0x0, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x0, 0x30, 0x14, 0x0, 0x0, 0x0, 0x12, 0x0, 0x44, 0x0, 0x0, 0x22, 0x50, 0x0, 0x0, 0x0, 0x22, 0x50, 0x44, 0x0, 0x0, 0x12, 0x0, 0x50, 0x0, 0xc, 0x12, 0x14, 0x0, 0x0, 0xc, 0x22, 0x44, 0x50, 0x0, 0xc, 0x22, 0x44, 0x0, 0x0, 0xc, 0x12, 0x14, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x44, 0x50, 0x0, 0xa, 0x0, 0x0, 0x50, 0x0, 0x6, 0x0, 0x44, 0x0, 0x0, 0xa, 0x22, 0x0, 0x50, 0x30, 0x6, 0x30, 0x14, 0x50, 0x0, 0xa, 0x22, 0x0, 0x0, 0x30, 0x6, 0x30, 0x14, 0x50, 0x30, 0xc, 0x12, 0x14, 0x0, 0x30, 0xc, 0x22, 0x44, 0x50, 0x30, 0xc, 0x22, 0x44, 0x0, 0x30, 0xc, 0x12, 0x14, 0x0, 0x0, 0xa, 0x30, 0x0, 0x50, 0x0, 0x6, 0x30, 0x14, 0x50, 0x0, 0xa, 0x30, 0x0, 0x0, 0x0, 0x6, 0x30, 0x14, 0x0, 0x0, 0xa, 0x12, 0x0, 0x0, 0x30, 0x6, 0x0, 0x44, 0x50, 0x0, 0xa, 0x12, 0x0, 0x50, 0x30, 0x6, 0x0, 0x44, 0x44, 0x0, 0x6, 0x22, 0x44, 0x0, 0x0, 0x6, 0x22, 0x44, 0x44, 0x30, 0x6, 0x22, 0x50, 0x0, 0x30, 0x6, 0x22, 0x50, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x44, 0x0, 0x0, 0xc, 0x0, 0x50, 0x0, 0x0, 0xc, 0x0, 0x14, 0x0, 0x0, 0x6, 0x22, 0x0, 0x44, 0x0, 0x6, 0x22, 0x0, 0x44, 0x30, 0x6, 0x22, 0x14, 0x0, 0x30, 0x6, 0x22, 0x14, 0x0, 0x30, 0xc, 0x0, 0x0, 0x0, 0x30, 0xc, 0x0, 0x44, 0x0, 0x30, 0xc, 0x0, 0x50, 0x0, 0x30, 0xc, 0x0, 0x14, 0x0, 0x30, 0x6, 0x22, 0x0, 0x44, 0x0, 0x6, 0x22, 0x14, 0x44, 0x30, 0x6, 0x22, 0x0, 0x0, 0x0, 0x6, 0x22, 0x14, 0x44, 0x0, 0x6, 0x22, 0x50, 0x0, 0x30, 0x6, 0x22, 0x44, 0x44, 0x30, 0x6, 0x22, 0x44, 0x0, 0x0, 0x6, 0x22, 0x50, 0x50, 0x30, 0x6, 0x30, 0x50, 0x0, 0x0, 0xa, 0x22, 0x44, 0x50, 0x0, 0xa, 0x22, 0x44, 0x0, 0x30, 0x6, 0x30, 0x50, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x44, 0x50, 0x0, 0xa, 0x0, 0x44, 0x50, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0xc, 0x22, 0x0, 0x50, 0x0, 0xc, 0x12, 0x50, 0x0, 0x0, 0xc, 0x12, 0x50, 0x50, 0x0, 0xc, 0x22, 0x0, 0x0, 0x30, 0xc, 0x22, 0x0, 0x50, 0x30, 0xc, 0x12, 0x50, 0x0, 0x30, 0xc, 0x12, 0x50, 0x50, 0x30, 0xc, 0x22, 0x0, 0x0, 0x30, 0x6, 0x0, 0x0, 0x0, 0x0, 0xa, 0x12, 0x44, 0x50, 0x0, 0xa, 0x12, 0x44, 0x50, 0x30, 0x6, 0x0, 0x0, 0x50, 0x0, 0x6, 0x30, 0x50, 0x0, 0x0, 0xa, 0x30, 0x44, 0x50, 0x0, 0xa, 0x30, 0x44, 0x0, 0x0, 0x6, 0x30, 0x50, + 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x1144, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x1144, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x1144, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x1144, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x0, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1122, 0x5050, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2112, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x4444, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x0, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x1414, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1122, 0x5050, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2112, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x4444, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x1144, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2112, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x5500, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x550, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x550, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x1414, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x5050, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x550, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x550, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x4114, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3030, 0x5050, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3030, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x1144, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1212, 0x4444, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3030, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1212, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2112, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1122, 0x0, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x330, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x550, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x0, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1122, 0x5050, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x550, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x330, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x0, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x1414, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1122, 0x5050, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1122, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0xf00, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x0, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x0, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x3030, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x1212, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x2222, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x1414, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x1414, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0xf00, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x0, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x1212, 0x5500, 0x4444, 0x3030, 0x0, 0x96, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1212, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0x30c, 0x2222, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3030, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x2112, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x30c, 0x1122, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x30c, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x2112, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x30c, 0x1122, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0x30c, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x2112, 0x1414, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x1122, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x906, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x30c, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0x30c, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x1122, 0x1414, 0x5050, 0x3030, 0xf00, 0x96, 0x906, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x906, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x906, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x1144, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x1144, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x1144, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x4114, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x550, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x0, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2112, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1122, 0x5050, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x0, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x0, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x550, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x0, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1122, 0x5050, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2112, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x1414, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x4114, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x0, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x4114, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x0, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x4114, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2222, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x0, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x1144, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x550, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x0, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x0, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x550, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x1414, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x0, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x5500, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x5500, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x0, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2222, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2112, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1122, 0x1414, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x550, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3030, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x5500, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x0, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x5500, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x5500, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x0, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x550, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x5500, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x3030, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x2112, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x0, 0xf00, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x4114, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x1414, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x4444, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x0, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x5500, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x5500, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x0, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x4114, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x1414, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x0, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x0, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x0, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x0, 0x30c, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x2222, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0xc0c, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0xc0c, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3030, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xc0c, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0x0, 0xc0c, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x0, 0x30c, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x0, 0x30c, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x1144, 0x5050, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0x0, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x0, 0x906, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x0, 0x606, 0x330, 0x5050, 0x5050, 0x0, 0x0, 0x0, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x0, 0xa0a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x0, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x0, 0x906, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x550, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x4114, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x550, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x4114, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x4114, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x550, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x5500, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x2112, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x1212, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x1414, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x0, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x1414, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x550, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x1144, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x2112, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x0, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x550, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x5050, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x1144, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x1212, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x2222, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x330, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x2112, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x2112, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x550, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x1144, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x2112, 0x4444, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x0, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x5500, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x0, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x5500, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x0, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x550, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x550, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x4114, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x4114, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3030, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x0, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x550, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x4114, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x4444, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x1414, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x1144, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x0, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x0, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x1144, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x0, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x0, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x1414, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x1144, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x1414, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x550, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x4114, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x4114, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x4114, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x4444, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x1414, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x1144, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x0, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x0, 0x2112, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x0, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x1414, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x1144, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x0, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x1414, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x5050, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x550, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x2112, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x4114, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x4114, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x4444, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x4114, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x4444, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x1414, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4444, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1144, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x4114, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x4114, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4444, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1414, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x1144, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x4114, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x0, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x4114, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x550, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x4114, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x1144, 0x4444, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x5500, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x1414, 0x4444, 0x3030, 0xf00, 0x96, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x5050, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1212, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x330, 0x5500, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x606, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x0, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x5050, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x2222, 0x5500, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0xc0c, 0x1212, 0x550, 0x5050, 0x3030, 0x0, 0x3c, 0xc0c, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x0, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3300, 0x550, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xc0c, 0x1122, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0xc0c, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x0, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2112, 0x5500, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1122, 0x550, 0x5050, 0x3030, 0xf00, 0x3c, 0xc0c, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x0, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x3030, 0x550, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x5050, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xc0c, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0x3c, 0xc0c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x1144, 0x0, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x5050, 0x5050, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x1122, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x606, 0x0, 0x5500, 0x5050, 0x3030, 0xf00, 0x96, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x606, 0x330, 0x5050, 0x5050, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x606, 0x330, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x606, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xa0a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x1414, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x4114, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x4114, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x4114, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1414, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x1414, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x4114, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x5050, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x4114, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x1414, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x4114, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x4114, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x4114, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x4114, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x4114, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x4114, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x1414, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x4114, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x4114, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x4114, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x1144, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x4444, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x1144, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x4444, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x4444, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x1144, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x550, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x4444, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x550, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x4444, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x4114, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x1212, 0x5500, 0x4444, 0x0, 0xf00, 0x96, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x0, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x1144, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x0, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x1212, 0x5500, 0x4444, 0x3300, 0x0, 0x96, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x2222, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x550, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x0, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x550, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4114, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x0, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x5500, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x5500, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x0, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x4114, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x4114, 0x4444, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x4444, 0x4444, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0xf0, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0xf0, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x2222, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x5050, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x0, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4114, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x0, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x4114, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x550, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x5500, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x0, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x4114, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x5500, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x1414, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x4114, 0x4444, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x4444, 0x4444, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x5050, 0x4444, 0x0, 0xf00, 0xf0, 0x906, 0x1122, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x1122, 0x1144, 0x4444, 0x3300, 0xf00, 0xf0, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x2222, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x0, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x4114, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x550, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x4114, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x4114, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x550, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x0, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x5050, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x0, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x1144, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x4114, 0x4444, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x1212, 0x5500, 0x4444, 0x3300, 0xf00, 0x96, 0x0, 0x2222, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x2222, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x550, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x4114, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x0, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x4114, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x4114, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x550, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x550, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x4114, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x4114, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x550, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x4444, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x4114, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x0, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x4114, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x550, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x0, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x550, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x4444, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x0, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x4114, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x550, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x4114, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x0, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x4444, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x4114, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x550, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x4444, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x4114, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x1144, 0x4444, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x5050, 0x4444, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x5500, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x1414, 0x4444, 0x3300, 0x0, 0x66, 0xf00, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x1212, 0x5500, 0x4444, 0x0, 0x0, 0x96, 0xf00, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x1122, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x0, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x1144, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x1144, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x0, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x5500, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1414, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x4114, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x0, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x4114, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x5500, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x1414, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x4114, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x0, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x0, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x4114, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x1414, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x5500, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x1414, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x4114, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x0, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x4114, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x4114, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x0, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x4114, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x0, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x4114, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x5500, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x1414, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x4114, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x1144, 0x5050, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x606, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x0, 0x5500, 0x5050, 0x0, 0xf00, 0x96, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x0, 0x1144, 0x5050, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x5050, 0x5050, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x30c, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x1122, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x30c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x1212, 0x1144, 0x5050, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x906, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x3300, 0x5500, 0x5050, 0x3300, 0x0, 0x96, 0x50a, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x3300, 0x550, 0x5050, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x4444, 0x5050, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x2222, 0x5500, 0x5050, 0x0, 0x0, 0x66, 0x50a, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x1212, 0x550, 0x5050, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x4444, 0x5050, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x5050, 0x5050, 0x0, 0x0, 0xcc, 0x906, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x3030, 0x1144, 0x5050, 0x0, 0x0, 0xcc, 0x50a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x3030, 0x550, 0x5050, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x4444, 0x5050, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5050, 0x5050, 0x0, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x2222, 0x1144, 0x5050, 0x3300, 0x0, 0x3c, 0x50a, 0x1122, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x1122, 0x550, 0x5050, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x4444, 0x5050, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x5050, 0x5050, 0x3300, 0x0, 0xcc, 0x906, 0x3030, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x3030, 0x1144, 0x5050, 0x3300, 0x0, 0xcc, 0x50a, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x3030, 0x550, 0x5050, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x4444, 0x5050, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5050, 0x5050, 0x3300, 0xf00, 0x3c, 0xa0a, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x2222, 0x1144, 0x5050, 0x0, 0x0, 0x3c, 0x50a, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x1122, 0x550, 0x5050, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x3300, 0x5500, 0x5050, 0x0, 0x0, 0x96, 0x50a, 0x3300, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x3300, 0x550, 0x5050, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x4444, 0x5050, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0xc0c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x2222, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x50a, 0x1212, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x1212, 0x550, 0x5050, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x1414, 0x5050, 0x3300, 0xf00, 0x5a, 0x606, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x0, 0x5500, 0x5050, 0x3300, 0xf00, 0x96, 0xa0a, 0x0, 0x5050, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x0, 0x1144, 0x5050, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x5050, 0x5050, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x1414, 0x5050, 0x0, 0xf00, 0x5a, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x1122, 0x5500, 0x5050, 0x3300, 0x0, 0x66, 0x30c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x0, 0x1144, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3030, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1212, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3030, 0x1414, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3030, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2112, 0x5050, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x0, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0xf00, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xc0c, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xc0c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xc0c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x606, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0xf00, 0x1212, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0xf00, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x5050, 0x5500, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x1122, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0xf00, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x606, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0x606, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xa0a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x606, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x5a, 0xf00, 0x330, 0x1144, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x0, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x0, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x0, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x2112, 0x1414, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x0, 0x3300, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0x0, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0x0, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0x0, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x0, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x96, 0x0, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x0, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x0, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x0, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x5a, 0x0, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x0, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3030, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3030, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3030, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2112, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2222, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x0, 0x1414, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x0, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2222, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x330, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1122, 0x550, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1122, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2112, 0x5050, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3300, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x1122, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0xc0c, 0x2112, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2112, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x1122, 0x4444, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x30c, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0xf0, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x550, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0x0, 0xf0, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0x0, 0xf0, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x1144, 0x5500, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x3300, 0x0, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3300, 0x0, 0xf0, 0x606, 0x0, 0x0, 0x5500, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x550, 0x0, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0xf0, 0x906, 0x330, 0x5050, 0x0, 0x0, 0x0, 0xf0, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0xf0, 0xa0a, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xf0, 0x606, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0xc0c, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x2222, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x2112, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0x30c, 0x1212, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3300, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3030, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x2222, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x3300, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x2222, 0x5500, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x2112, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x30c, 0x1212, 0x550, 0x5500, 0x0, 0xf00, 0xf0, 0x30c, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0xf0, 0xc0c, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x0, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0xf0, 0x906, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x1212, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0xf0, 0x606, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x550, 0x0, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x4444, 0x5500, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x5050, 0x5500, 0x0, 0xf00, 0xf0, 0x906, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0xf0, 0x50a, 0x330, 0x1144, 0x5500, 0x0, 0xf00, 0xf0, 0xa0a, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0xf0, 0x606, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x3300, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x3300, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x330, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x1122, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x2112, 0x5050, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x1122, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x2222, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x330, 0x550, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x2222, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x2112, 0x1414, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x330, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x330, 0x4444, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x0, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x2112, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x1212, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x2222, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x1212, 0x550, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x3030, 0x1414, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x0, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x0, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x330, 0x4444, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x330, 0x550, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2222, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x0, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x0, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x1212, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x0, 0x2112, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2222, 0x1414, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0x30c, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0x30c, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x30c, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x4444, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x3300, 0x1414, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0x906, 0x3300, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x0, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x550, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x5500, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x1212, 0x5050, 0x0, 0x3300, 0xf00, 0x3c, 0x0, 0x2112, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x0, 0x2222, 0x0, 0x0, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x1122, 0x1414, 0x5500, 0x3300, 0x0, 0x66, 0x906, 0x0, 0x5050, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x2112, 0x4444, 0x5500, 0x3300, 0xf00, 0x96, 0x0, 0x0, 0x0, 0x5500, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x550, 0x0, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x906, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x50a, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0x906, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x0, 0x330, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x0, 0x330, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x330, 0x5500, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1122, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x0, 0x1144, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x0, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x550, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x5050, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x2222, 0x5500, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x2112, 0x5050, 0x0, 0x3300, 0x0, 0x3c, 0xf00, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x3c, 0xf00, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x0, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2112, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3300, 0x550, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x0, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3030, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3030, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x0, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x550, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x2112, 0x5500, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x2222, 0x5050, 0x0, 0x0, 0x0, 0xcc, 0xf00, 0x1122, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x0, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x550, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x2112, 0x5500, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x2222, 0x5050, 0x0, 0x3300, 0x0, 0xcc, 0xf00, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0xf00, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x0, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x2112, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x2222, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x3030, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x0, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x3300, 0x1144, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x3300, 0x550, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x550, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x5050, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x2222, 0x5500, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x2112, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0xf00, 0x1212, 0x550, 0x5500, 0x0, 0x0, 0x3c, 0xf00, 0x1122, 0x0, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xf00, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0xf00, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x1212, 0x1144, 0x5500, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x1144, 0x0, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xf00, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xf00, 0x330, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xf00, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x5a, 0xf00, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x5050, 0x5500, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x66, 0xa0a, 0x330, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x1122, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x96, 0xa0a, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x330, 0x1144, 0x0, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x0, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x5a, 0x906, 0x2112, 0x5050, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x96, 0x50a, 0x1122, 0x0, 0x0, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x3300, 0x0, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x66, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x0, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x5500, 0x0, 0x0, 0x0, 0xcc, 0xa0a, 0x2112, 0x5500, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0xcc, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x0, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x5500, 0x0, 0x3300, 0x0, 0xcc, 0xa0a, 0x2112, 0x5500, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x2112, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0xcc, 0x50a, 0x1212, 0x0, 0x0, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x2112, 0x1414, 0x5500, 0x3300, 0xf00, 0x5a, 0xc0c, 0x3030, 0x5050, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x1122, 0x4444, 0x5500, 0x3300, 0x0, 0x66, 0x50a, 0x3030, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0xa0a, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x3300, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x50a, 0x3300, 0x0, 0x0, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x0, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x5050, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x3030, 0x1414, 0x5500, 0x0, 0xf00, 0x5a, 0x906, 0x2112, 0x5050, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x3030, 0x4444, 0x5500, 0x0, 0x0, 0x96, 0x50a, 0x1122, 0x0, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x1144, 0x0, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x1212, 0x1414, 0x0, 0x3300, 0x0, 0x96, 0xa0a, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x5a, 0x606, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x1122, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x1144, 0x0, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x5050, 0x5500, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x330, 0x1414, 0x0, 0x0, 0x0, 0x66, 0xa0a, 0x330, 0x5500, 0x5500, 0x0, 0xf00, 0x5a, 0x30c, 0x330, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x330, 0x1144, 0x5500, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x1144, 0x0, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x30c, 0x0, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x30c, 0x0, 0x5500, 0x5500, 0x0, 0x0, 0x66, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0x0, 0x96, 0x30c, 0x0, 0x1144, 0x5500, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x1144, 0x0, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x5050, 0x5500, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x606, 0x1122, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x606, 0x1122, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xa0a, 0x1212, 0x5050, 0x0, 0x3030, 0xf00, 0x96, 0x30c, 0x1212, 0x1144, 0x5500, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x550, 0x0, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0xc0c, 0x3300, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x906, 0x2222, 0x5500, 0x5500, 0x3030, 0x0, 0x96, 0xc0c, 0x3300, 0x4444, 0x0, 0x3300, 0xf00, 0x96, 0x50a, 0x1212, 0x550, 0x5500, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x4444, 0x5500, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x906, 0x2222, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0xf00, 0x96, 0xc0c, 0x1212, 0x4444, 0x0, 0x3300, 0x0, 0x66, 0xc0c, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x4444, 0x5500, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x1144, 0x0, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5050, 0x5500, 0x0, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0xf00, 0x3c, 0x906, 0x3030, 0x1144, 0x5500, 0x0, 0xf00, 0x3c, 0x606, 0x3030, 0x4444, 0x0, 0x0, 0xf00, 0x3c, 0x50a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x4444, 0x5500, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x1144, 0x0, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x5050, 0x5500, 0x0, 0x0, 0xcc, 0x906, 0x2222, 0x5050, 0x0, 0x3030, 0x0, 0x3c, 0x906, 0x2222, 0x1144, 0x5500, 0x3030, 0x0, 0x3c, 0x606, 0x1122, 0x4444, 0x0, 0x0, 0x0, 0xcc, 0x606, 0x1122, 0x550, 0x5500, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x550, 0x0, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x4444, 0x5500, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x1144, 0x0, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x5050, 0x5500, 0x3300, 0x0, 0xcc, 0x906, 0x2222, 0x5050, 0x0, 0x3030, 0xf00, 0x3c, 0x906, 0x3030, 0x1144, 0x5500, 0x3030, 0xf00, 0x3c, 0x606, 0x3030, 0x4444, 0x0, 0x3300, 0x0, 0xcc, 0x606, 0x1122, 0x550, 0x5500, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x550, 0x0, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x4444, 0x5500, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x1144, 0x0, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5050, 0x5500, 0x3300, 0xf00, 0x3c, 0xa0a, 0x3030, 0x5050, 0x0, 0x0, 0x0, 0x3c, 0x906, 0x2222, 0x1144, 0x5500, 0x0, 0x0, 0x3c, 0x606, 0x1122, 0x4444, 0x0, 0x3300, 0xf00, 0x3c, 0x50a, 0x3030, 0x550, 0x5500, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x550, 0x0, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0xc0c, 0x3300, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0xc0c, 0x3300, 0x5500, 0x5500, 0x0, 0x0, 0x96, 0xc0c, 0x3300, 0x4444, 0x0, 0x0, 0x0, 0x66, 0xc0c, 0x3300, 0x550, 0x5500, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x550, 0x0, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x4444, 0x5500, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x906, 0x2222, 0x1414, 0x0, 0x0, 0x0, 0x5a, 0x906, 0x2222, 0x5500, 0x5500, 0x3030, 0xf00, 0x96, 0xc0c, 0x1212, 0x4444, 0x0, 0x0, 0xf00, 0x96, 0x50a, 0x1212, 0x550, 0x5500, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x1144, 0x0, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x5500, 0x0, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x1414, 0x5500, 0x3030, 0x0, 0xaa, 0x30c, 0x0, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x606, 0x1122, 0x5500, 0x5500, 0x3300, 0xf00, 0x96, 0xa0a, 0x1212, 0x5050, 0x0, 0x3030, 0x0, 0x96, 0x30c, 0x0, 0x1144, 0x5500, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x1144, 0x0, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x5050, 0x5500, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x5500, 0x0, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x1414, 0x5500, 0x0, 0x0, 0xaa, 0x606, 0x1122, 0x1414, 0x0, 0x3300, 0x0, 0x5a, 0x30c, 0x0, 0x5500, 0x5500, 0x3300, 0x0, 0x66, 0x30c, 0x0, 0x5050, 0x0, 0x0, 0xf00, 0x96, 0x30c, 0x1212, 0x1144 }; + +const unsigned masks_start[] = { 0u, 18u, 858u }; + +const unsigned masks_end[] = { 18u, 858u, 141978u }; +} // namespace detail +} // namespace kitty + /*! \endcond */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/mscfix.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/mscfix.hpp new file mode 100644 index 00000000000..9453fa6d39c --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/mscfix.hpp @@ -0,0 +1,40 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mscfix.hpp + \brief Fixes some compatibility issues with MS VC compilers + + \author Mathias Soeken +*/ + +#pragma once + +// Use Windows popcount version where appropriate +#ifdef _MSC_VER +#include +#define __builtin_popcount __popcnt +#define __builtin_popcountll __popcnt64 +#endif \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/shift.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/shift.hpp new file mode 100644 index 00000000000..18e5ae37761 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/shift.hpp @@ -0,0 +1,1002 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file shift.hpp + \brief Shift helper functions + + \author Mathias Soeken +*/ + +/*! \cond PRIVATE */ +#pragma once + +#include +#include + +namespace kitty +{ +namespace detail +{ + +// This code has been auto-generated by Nikolaj Bjorner +// https://github.com/NikolajBjorner/z3/blob/17a256eca1a375f023bb98c3e4e728845d92dfb2/src/ast/expr_network.cpp#L22 +inline uint64_t compute_shift( uint64_t x, unsigned code ) +{ + switch ( code ) + { +#define v_x0 ( x & 1 ) +#define v_x1 v_x0 + case 1: + return v_x1; +#define v_x2 ( v_x1 | ( v_x1 << 1 ) ) + case 2: + return v_x2; +#define v_x3 ( x & 3 ) +#define v_x4 v_x3 + case 3: + return v_x4; +#define v_x5 ( v_x2 | ( v_x2 << 2 ) ) + case 4: + return v_x5; +#define v_x6 ( v_x4 | ( v_x4 << 2 ) ) + case 5: + return v_x6; +#define v_x7 ( x & 2 ) +#define v_x8 ( v_x7 << 1 ) +#define v_x9 ( v_x8 | ( v_x8 << 1 ) ) +#define v_x10 ( v_x2 | v_x9 ) + case 6: + return v_x10; +#define v_x11 ( x & 15 ) +#define v_x12 v_x11 + case 7: + return v_x12; +#define v_x13 ( v_x5 | ( v_x5 << 4 ) ) + case 8: + return v_x13; +#define v_x14 ( v_x6 | ( v_x6 << 4 ) ) + case 9: + return v_x14; +#define v_x15 ( v_x10 | ( v_x10 << 4 ) ) + case 10: + return v_x15; +#define v_x16 ( v_x12 | ( v_x12 << 4 ) ) + case 11: + return v_x16; +#define v_x17 ( v_x7 << 3 ) +#define v_x18 ( v_x17 | ( v_x17 << 1 ) ) +#define v_x19 ( v_x18 | ( v_x18 << 2 ) ) +#define v_x20 ( v_x5 | v_x19 ) + case 12: + return v_x20; +#define v_x21 ( x & 12 ) +#define v_x22 ( v_x21 << 2 ) +#define v_x23 ( v_x22 | ( v_x22 << 2 ) ) +#define v_x24 ( v_x6 | v_x23 ) + case 13: + return v_x24; +#define v_x25 ( x & 4 ) +#define v_x26 ( v_x25 << 2 ) +#define v_x27 ( v_x26 | ( v_x26 << 1 ) ) +#define v_x28 ( x & 8 ) +#define v_x29 ( v_x28 << 3 ) +#define v_x30 ( v_x29 | ( v_x29 << 1 ) ) +#define v_x31 ( v_x27 | v_x30 ) +#define v_x32 ( v_x10 | v_x31 ) + case 14: + return v_x32; +#define v_x33 ( x & 255 ) +#define v_x34 v_x33 + case 15: + return v_x34; +#define v_x35 ( v_x13 | ( v_x13 << 8 ) ) + case 16: + return v_x35; +#define v_x36 ( v_x14 | ( v_x14 << 8 ) ) + case 17: + return v_x36; +#define v_x37 ( v_x15 | ( v_x15 << 8 ) ) + case 18: + return v_x37; +#define v_x38 ( v_x16 | ( v_x16 << 8 ) ) + case 19: + return v_x38; +#define v_x39 ( v_x20 | ( v_x20 << 8 ) ) + case 20: + return v_x39; +#define v_x40 ( v_x24 | ( v_x24 << 8 ) ) + case 21: + return v_x40; +#define v_x41 ( v_x32 | ( v_x32 << 8 ) ) + case 22: + return v_x41; +#define v_x42 ( v_x34 | ( v_x34 << 8 ) ) + case 23: + return v_x42; +#define v_x43 ( v_x7 << 7 ) +#define v_x44 ( v_x43 | ( v_x43 << 1 ) ) +#define v_x45 ( v_x44 | ( v_x44 << 2 ) ) +#define v_x46 ( v_x45 | ( v_x45 << 4 ) ) +#define v_x47 ( v_x13 | v_x46 ) + case 24: + return v_x47; +#define v_x48 ( v_x21 << 6 ) +#define v_x49 ( v_x48 | ( v_x48 << 2 ) ) +#define v_x50 ( v_x49 | ( v_x49 << 4 ) ) +#define v_x51 ( v_x14 | v_x50 ) + case 25: + return v_x51; +#define v_x52 ( v_x25 << 6 ) +#define v_x53 ( v_x52 | ( v_x52 << 1 ) ) +#define v_x54 ( v_x28 << 7 ) +#define v_x55 ( v_x54 | ( v_x54 << 1 ) ) +#define v_x56 ( v_x53 | v_x55 ) +#define v_x57 ( v_x56 | ( v_x56 << 4 ) ) +#define v_x58 ( v_x15 | v_x57 ) + case 26: + return v_x58; +#define v_x59 ( x & 240 ) +#define v_x60 ( v_x59 << 4 ) +#define v_x61 ( v_x60 | ( v_x60 << 4 ) ) +#define v_x62 ( v_x16 | v_x61 ) + case 27: + return v_x62; +#define v_x63 ( v_x53 | ( v_x53 << 2 ) ) +#define v_x64 ( v_x28 << 9 ) +#define v_x65 ( v_x64 | ( v_x64 << 1 ) ) +#define v_x66 ( v_x65 | ( v_x65 << 2 ) ) +#define v_x67 ( v_x63 | v_x66 ) +#define v_x68 ( v_x20 | v_x67 ) + case 28: + return v_x68; +#define v_x69 ( x & 48 ) +#define v_x70 ( v_x69 << 4 ) +#define v_x71 ( v_x70 | ( v_x70 << 2 ) ) +#define v_x72 ( x & 192 ) +#define v_x73 ( v_x72 << 6 ) +#define v_x74 ( v_x73 | ( v_x73 << 2 ) ) +#define v_x75 ( v_x71 | v_x74 ) +#define v_x76 ( v_x24 | v_x75 ) + case 29: + return v_x76; +#define v_x77 ( x & 16 ) +#define v_x78 ( v_x77 << 4 ) +#define v_x79 ( v_x78 | ( v_x78 << 1 ) ) +#define v_x80 ( x & 32 ) +#define v_x81 ( v_x80 << 5 ) +#define v_x82 ( v_x81 | ( v_x81 << 1 ) ) +#define v_x83 ( v_x79 | v_x82 ) +#define v_x84 ( x & 64 ) +#define v_x85 ( v_x84 << 6 ) +#define v_x86 ( v_x85 | ( v_x85 << 1 ) ) +#define v_x87 ( x & 128 ) +#define v_x88 ( v_x87 << 7 ) +#define v_x89 ( v_x88 | ( v_x88 << 1 ) ) +#define v_x90 ( v_x86 | v_x89 ) +#define v_x91 ( v_x83 | v_x90 ) +#define v_x92 ( v_x32 | v_x91 ) + case 30: + return v_x92; +#define v_x93 ( x & 65535 ) +#define v_x94 v_x93 + case 31: + return v_x94; +#define v_x95 ( v_x35 | ( v_x35 << 16 ) ) + case 32: + return v_x95; +#define v_x96 ( v_x36 | ( v_x36 << 16 ) ) + case 33: + return v_x96; +#define v_x97 ( v_x37 | ( v_x37 << 16 ) ) + case 34: + return v_x97; +#define v_x98 ( v_x38 | ( v_x38 << 16 ) ) + case 35: + return v_x98; +#define v_x99 ( v_x39 | ( v_x39 << 16 ) ) + case 36: + return v_x99; +#define v_x100 ( v_x40 | ( v_x40 << 16 ) ) + case 37: + return v_x100; +#define v_x101 ( v_x41 | ( v_x41 << 16 ) ) + case 38: + return v_x101; +#define v_x102 ( v_x42 | ( v_x42 << 16 ) ) + case 39: + return v_x102; +#define v_x103 ( v_x47 | ( v_x47 << 16 ) ) + case 40: + return v_x103; +#define v_x104 ( v_x51 | ( v_x51 << 16 ) ) + case 41: + return v_x104; +#define v_x105 ( v_x58 | ( v_x58 << 16 ) ) + case 42: + return v_x105; +#define v_x106 ( v_x62 | ( v_x62 << 16 ) ) + case 43: + return v_x106; +#define v_x107 ( v_x68 | ( v_x68 << 16 ) ) + case 44: + return v_x107; +#define v_x108 ( v_x76 | ( v_x76 << 16 ) ) + case 45: + return v_x108; +#define v_x109 ( v_x92 | ( v_x92 << 16 ) ) + case 46: + return v_x109; +#define v_x110 ( v_x94 | ( v_x94 << 16 ) ) + case 47: + return v_x110; +#define v_x111 ( v_x7 << 15 ) +#define v_x112 ( v_x111 | ( v_x111 << 1 ) ) +#define v_x113 ( v_x112 | ( v_x112 << 2 ) ) +#define v_x114 ( v_x113 | ( v_x113 << 4 ) ) +#define v_x115 ( v_x114 | ( v_x114 << 8 ) ) +#define v_x116 ( v_x35 | v_x115 ) + case 48: + return v_x116; +#define v_x117 ( v_x21 << 14 ) +#define v_x118 ( v_x117 | ( v_x117 << 2 ) ) +#define v_x119 ( v_x118 | ( v_x118 << 4 ) ) +#define v_x120 ( v_x119 | ( v_x119 << 8 ) ) +#define v_x121 ( v_x36 | v_x120 ) + case 49: + return v_x121; +#define v_x122 ( v_x25 << 14 ) +#define v_x123 ( v_x122 | ( v_x122 << 1 ) ) +#define v_x124 ( v_x28 << 15 ) +#define v_x125 ( v_x124 | ( v_x124 << 1 ) ) +#define v_x126 ( v_x123 | v_x125 ) +#define v_x127 ( v_x126 | ( v_x126 << 4 ) ) +#define v_x128 ( v_x127 | ( v_x127 << 8 ) ) +#define v_x129 ( v_x37 | v_x128 ) + case 50: + return v_x129; +#define v_x130 ( v_x59 << 12 ) +#define v_x131 ( v_x130 | ( v_x130 << 4 ) ) +#define v_x132 ( v_x131 | ( v_x131 << 8 ) ) +#define v_x133 ( v_x38 | v_x132 ) + case 51: + return v_x133; +#define v_x134 ( v_x123 | ( v_x123 << 2 ) ) +#define v_x135 ( v_x28 << 17 ) +#define v_x136 ( v_x135 | ( v_x135 << 1 ) ) +#define v_x137 ( v_x136 | ( v_x136 << 2 ) ) +#define v_x138 ( v_x134 | v_x137 ) +#define v_x139 ( v_x138 | ( v_x138 << 8 ) ) +#define v_x140 ( v_x39 | v_x139 ) + case 52: + return v_x140; +#define v_x141 ( v_x69 << 12 ) +#define v_x142 ( v_x141 | ( v_x141 << 2 ) ) +#define v_x143 ( v_x72 << 14 ) +#define v_x144 ( v_x143 | ( v_x143 << 2 ) ) +#define v_x145 ( v_x142 | v_x144 ) +#define v_x146 ( v_x145 | ( v_x145 << 8 ) ) +#define v_x147 ( v_x40 | v_x146 ) + case 53: + return v_x147; +#define v_x148 ( v_x77 << 12 ) +#define v_x149 ( v_x148 | ( v_x148 << 1 ) ) +#define v_x150 ( v_x80 << 13 ) +#define v_x151 ( v_x150 | ( v_x150 << 1 ) ) +#define v_x152 ( v_x149 | v_x151 ) +#define v_x153 ( v_x84 << 14 ) +#define v_x154 ( v_x153 | ( v_x153 << 1 ) ) +#define v_x155 ( v_x87 << 15 ) +#define v_x156 ( v_x155 | ( v_x155 << 1 ) ) +#define v_x157 ( v_x154 | v_x156 ) +#define v_x158 ( v_x152 | v_x157 ) +#define v_x159 ( v_x158 | ( v_x158 << 8 ) ) +#define v_x160 ( v_x41 | v_x159 ) + case 54: + return v_x160; +#define v_x161 ( x & 65280 ) +#define v_x162 ( v_x161 << 8 ) +#define v_x163 ( v_x162 | ( v_x162 << 8 ) ) +#define v_x164 ( v_x42 | v_x163 ) + case 55: + return v_x164; +#define v_x165 ( v_x134 | ( v_x134 << 4 ) ) +#define v_x166 ( v_x28 << 21 ) +#define v_x167 ( v_x166 | ( v_x166 << 1 ) ) +#define v_x168 ( v_x167 | ( v_x167 << 2 ) ) +#define v_x169 ( v_x168 | ( v_x168 << 4 ) ) +#define v_x170 ( v_x165 | v_x169 ) +#define v_x171 ( v_x47 | v_x170 ) + case 56: + return v_x171; +#define v_x172 ( v_x142 | ( v_x142 << 4 ) ) +#define v_x173 ( v_x72 << 18 ) +#define v_x174 ( v_x173 | ( v_x173 << 2 ) ) +#define v_x175 ( v_x174 | ( v_x174 << 4 ) ) +#define v_x176 ( v_x172 | v_x175 ) +#define v_x177 ( v_x51 | v_x176 ) + case 57: + return v_x177; +#define v_x178 ( v_x152 | ( v_x152 << 4 ) ) +#define v_x179 ( v_x84 << 18 ) +#define v_x180 ( v_x179 | ( v_x179 << 1 ) ) +#define v_x181 ( v_x87 << 19 ) +#define v_x182 ( v_x181 | ( v_x181 << 1 ) ) +#define v_x183 ( v_x180 | v_x182 ) +#define v_x184 ( v_x183 | ( v_x183 << 4 ) ) +#define v_x185 ( v_x178 | v_x184 ) +#define v_x186 ( v_x58 | v_x185 ) + case 58: + return v_x186; +#define v_x187 ( x & 3840 ) +#define v_x188 ( v_x187 << 8 ) +#define v_x189 ( v_x188 | ( v_x188 << 4 ) ) +#define v_x190 ( x & 61440 ) +#define v_x191 ( v_x190 << 12 ) +#define v_x192 ( v_x191 | ( v_x191 << 4 ) ) +#define v_x193 ( v_x189 | v_x192 ) +#define v_x194 ( v_x62 | v_x193 ) + case 59: + return v_x194; +#define v_x195 ( v_x149 | ( v_x149 << 2 ) ) +#define v_x196 ( v_x80 << 15 ) +#define v_x197 ( v_x196 | ( v_x196 << 1 ) ) +#define v_x198 ( v_x197 | ( v_x197 << 2 ) ) +#define v_x199 ( v_x195 | v_x198 ) +#define v_x200 ( v_x180 | ( v_x180 << 2 ) ) +#define v_x201 ( v_x87 << 21 ) +#define v_x202 ( v_x201 | ( v_x201 << 1 ) ) +#define v_x203 ( v_x202 | ( v_x202 << 2 ) ) +#define v_x204 ( v_x200 | v_x203 ) +#define v_x205 ( v_x199 | v_x204 ) +#define v_x206 ( v_x68 | v_x205 ) + case 60: + return v_x206; +#define v_x207 ( x & 768 ) +#define v_x208 ( v_x207 << 8 ) +#define v_x209 ( v_x208 | ( v_x208 << 2 ) ) +#define v_x210 ( x & 3072 ) +#define v_x211 ( v_x210 << 10 ) +#define v_x212 ( v_x211 | ( v_x211 << 2 ) ) +#define v_x213 ( v_x209 | v_x212 ) +#define v_x214 ( x & 12288 ) +#define v_x215 ( v_x214 << 12 ) +#define v_x216 ( v_x215 | ( v_x215 << 2 ) ) +#define v_x217 ( x & 49152 ) +#define v_x218 ( v_x217 << 14 ) +#define v_x219 ( v_x218 | ( v_x218 << 2 ) ) +#define v_x220 ( v_x216 | v_x219 ) +#define v_x221 ( v_x213 | v_x220 ) +#define v_x222 ( v_x76 | v_x221 ) + case 61: + return v_x222; +#define v_x223 ( x & 256 ) +#define v_x224 ( v_x223 << 8 ) +#define v_x225 ( v_x224 | ( v_x224 << 1 ) ) +#define v_x226 ( x & 512 ) +#define v_x227 ( v_x226 << 9 ) +#define v_x228 ( v_x227 | ( v_x227 << 1 ) ) +#define v_x229 ( v_x225 | v_x228 ) +#define v_x230 ( x & 1024 ) +#define v_x231 ( v_x230 << 10 ) +#define v_x232 ( v_x231 | ( v_x231 << 1 ) ) +#define v_x233 ( x & 2048 ) +#define v_x234 ( v_x233 << 11 ) +#define v_x235 ( v_x234 | ( v_x234 << 1 ) ) +#define v_x236 ( v_x232 | v_x235 ) +#define v_x237 ( v_x229 | v_x236 ) +#define v_x238 ( x & 4096 ) +#define v_x239 ( v_x238 << 12 ) +#define v_x240 ( v_x239 | ( v_x239 << 1 ) ) +#define v_x241 ( x & 8192 ) +#define v_x242 ( v_x241 << 13 ) +#define v_x243 ( v_x242 | ( v_x242 << 1 ) ) +#define v_x244 ( v_x240 | v_x243 ) +#define v_x245 ( x & 16384 ) +#define v_x246 ( v_x245 << 14 ) +#define v_x247 ( v_x246 | ( v_x246 << 1 ) ) +#define v_x248 ( x & 32768 ) +#define v_x249 ( v_x248 << 15 ) +#define v_x250 ( v_x249 | ( v_x249 << 1 ) ) +#define v_x251 ( v_x247 | v_x250 ) +#define v_x252 ( v_x244 | v_x251 ) +#define v_x253 ( v_x237 | v_x252 ) +#define v_x254 ( v_x92 | v_x253 ) + case 62: + return v_x254; +#define v_x255 ( x & 4294967295 ) +#define v_x256 v_x255 + case 63: + return v_x256; +#define v_x257 ( v_x95 | ( v_x95 << 32 ) ) + case 64: + return v_x257; +#define v_x258 ( v_x96 | ( v_x96 << 32 ) ) + case 65: + return v_x258; +#define v_x259 ( v_x97 | ( v_x97 << 32 ) ) + case 66: + return v_x259; +#define v_x260 ( v_x98 | ( v_x98 << 32 ) ) + case 67: + return v_x260; +#define v_x261 ( v_x99 | ( v_x99 << 32 ) ) + case 68: + return v_x261; +#define v_x262 ( v_x100 | ( v_x100 << 32 ) ) + case 69: + return v_x262; +#define v_x263 ( v_x101 | ( v_x101 << 32 ) ) + case 70: + return v_x263; +#define v_x264 ( v_x102 | ( v_x102 << 32 ) ) + case 71: + return v_x264; +#define v_x265 ( v_x103 | ( v_x103 << 32 ) ) + case 72: + return v_x265; +#define v_x266 ( v_x104 | ( v_x104 << 32 ) ) + case 73: + return v_x266; +#define v_x267 ( v_x105 | ( v_x105 << 32 ) ) + case 74: + return v_x267; +#define v_x268 ( v_x106 | ( v_x106 << 32 ) ) + case 75: + return v_x268; +#define v_x269 ( v_x107 | ( v_x107 << 32 ) ) + case 76: + return v_x269; +#define v_x270 ( v_x108 | ( v_x108 << 32 ) ) + case 77: + return v_x270; +#define v_x271 ( v_x109 | ( v_x109 << 32 ) ) + case 78: + return v_x271; +#define v_x272 ( v_x110 | ( v_x110 << 32 ) ) + case 79: + return v_x272; +#define v_x273 ( v_x116 | ( v_x116 << 32 ) ) + case 80: + return v_x273; +#define v_x274 ( v_x121 | ( v_x121 << 32 ) ) + case 81: + return v_x274; +#define v_x275 ( v_x129 | ( v_x129 << 32 ) ) + case 82: + return v_x275; +#define v_x276 ( v_x133 | ( v_x133 << 32 ) ) + case 83: + return v_x276; +#define v_x277 ( v_x140 | ( v_x140 << 32 ) ) + case 84: + return v_x277; +#define v_x278 ( v_x147 | ( v_x147 << 32 ) ) + case 85: + return v_x278; +#define v_x279 ( v_x160 | ( v_x160 << 32 ) ) + case 86: + return v_x279; +#define v_x280 ( v_x164 | ( v_x164 << 32 ) ) + case 87: + return v_x280; +#define v_x281 ( v_x171 | ( v_x171 << 32 ) ) + case 88: + return v_x281; +#define v_x282 ( v_x177 | ( v_x177 << 32 ) ) + case 89: + return v_x282; +#define v_x283 ( v_x186 | ( v_x186 << 32 ) ) + case 90: + return v_x283; +#define v_x284 ( v_x194 | ( v_x194 << 32 ) ) + case 91: + return v_x284; +#define v_x285 ( v_x206 | ( v_x206 << 32 ) ) + case 92: + return v_x285; +#define v_x286 ( v_x222 | ( v_x222 << 32 ) ) + case 93: + return v_x286; +#define v_x287 ( v_x254 | ( v_x254 << 32 ) ) + case 94: + return v_x287; +#define v_x288 ( v_x256 | ( v_x256 << 32 ) ) + case 95: + return v_x288; +#define v_x289 ( v_x7 << 31 ) +#define v_x290 ( v_x289 | ( v_x289 << 1 ) ) +#define v_x291 ( v_x290 | ( v_x290 << 2 ) ) +#define v_x292 ( v_x291 | ( v_x291 << 4 ) ) +#define v_x293 ( v_x292 | ( v_x292 << 8 ) ) +#define v_x294 ( v_x293 | ( v_x293 << 16 ) ) +#define v_x295 ( v_x95 | v_x294 ) + case 96: + return v_x295; +#define v_x296 ( v_x21 << 30 ) +#define v_x297 ( v_x296 | ( v_x296 << 2 ) ) +#define v_x298 ( v_x297 | ( v_x297 << 4 ) ) +#define v_x299 ( v_x298 | ( v_x298 << 8 ) ) +#define v_x300 ( v_x299 | ( v_x299 << 16 ) ) +#define v_x301 ( v_x96 | v_x300 ) + case 97: + return v_x301; +#define v_x302 ( v_x25 << 30 ) +#define v_x303 ( v_x302 | ( v_x302 << 1 ) ) +#define v_x304 ( v_x28 << 31 ) +#define v_x305 ( v_x304 | ( v_x304 << 1 ) ) +#define v_x306 ( v_x303 | v_x305 ) +#define v_x307 ( v_x306 | ( v_x306 << 4 ) ) +#define v_x308 ( v_x307 | ( v_x307 << 8 ) ) +#define v_x309 ( v_x308 | ( v_x308 << 16 ) ) +#define v_x310 ( v_x97 | v_x309 ) + case 98: + return v_x310; +#define v_x311 ( v_x59 << 28 ) +#define v_x312 ( v_x311 | ( v_x311 << 4 ) ) +#define v_x313 ( v_x312 | ( v_x312 << 8 ) ) +#define v_x314 ( v_x313 | ( v_x313 << 16 ) ) +#define v_x315 ( v_x98 | v_x314 ) + case 99: + return v_x315; +#define v_x316 ( v_x303 | ( v_x303 << 2 ) ) +#define v_x317 ( v_x28 << 33 ) +#define v_x318 ( v_x317 | ( v_x317 << 1 ) ) +#define v_x319 ( v_x318 | ( v_x318 << 2 ) ) +#define v_x320 ( v_x316 | v_x319 ) +#define v_x321 ( v_x320 | ( v_x320 << 8 ) ) +#define v_x322 ( v_x321 | ( v_x321 << 16 ) ) +#define v_x323 ( v_x99 | v_x322 ) + case 100: + return v_x323; +#define v_x324 ( v_x69 << 28 ) +#define v_x325 ( v_x324 | ( v_x324 << 2 ) ) +#define v_x326 ( v_x72 << 30 ) +#define v_x327 ( v_x326 | ( v_x326 << 2 ) ) +#define v_x328 ( v_x325 | v_x327 ) +#define v_x329 ( v_x328 | ( v_x328 << 8 ) ) +#define v_x330 ( v_x329 | ( v_x329 << 16 ) ) +#define v_x331 ( v_x100 | v_x330 ) + case 101: + return v_x331; +#define v_x332 ( v_x77 << 28 ) +#define v_x333 ( v_x332 | ( v_x332 << 1 ) ) +#define v_x334 ( v_x80 << 29 ) +#define v_x335 ( v_x334 | ( v_x334 << 1 ) ) +#define v_x336 ( v_x333 | v_x335 ) +#define v_x337 ( v_x84 << 30 ) +#define v_x338 ( v_x337 | ( v_x337 << 1 ) ) +#define v_x339 ( v_x87 << 31 ) +#define v_x340 ( v_x339 | ( v_x339 << 1 ) ) +#define v_x341 ( v_x338 | v_x340 ) +#define v_x342 ( v_x336 | v_x341 ) +#define v_x343 ( v_x342 | ( v_x342 << 8 ) ) +#define v_x344 ( v_x343 | ( v_x343 << 16 ) ) +#define v_x345 ( v_x101 | v_x344 ) + case 102: + return v_x345; +#define v_x346 ( v_x161 << 24 ) +#define v_x347 ( v_x346 | ( v_x346 << 8 ) ) +#define v_x348 ( v_x347 | ( v_x347 << 16 ) ) +#define v_x349 ( v_x102 | v_x348 ) + case 103: + return v_x349; +#define v_x350 ( v_x316 | ( v_x316 << 4 ) ) +#define v_x351 ( v_x28 << 37 ) +#define v_x352 ( v_x351 | ( v_x351 << 1 ) ) +#define v_x353 ( v_x352 | ( v_x352 << 2 ) ) +#define v_x354 ( v_x353 | ( v_x353 << 4 ) ) +#define v_x355 ( v_x350 | v_x354 ) +#define v_x356 ( v_x355 | ( v_x355 << 16 ) ) +#define v_x357 ( v_x103 | v_x356 ) + case 104: + return v_x357; +#define v_x358 ( v_x325 | ( v_x325 << 4 ) ) +#define v_x359 ( v_x72 << 34 ) +#define v_x360 ( v_x359 | ( v_x359 << 2 ) ) +#define v_x361 ( v_x360 | ( v_x360 << 4 ) ) +#define v_x362 ( v_x358 | v_x361 ) +#define v_x363 ( v_x362 | ( v_x362 << 16 ) ) +#define v_x364 ( v_x104 | v_x363 ) + case 105: + return v_x364; +#define v_x365 ( v_x336 | ( v_x336 << 4 ) ) +#define v_x366 ( v_x84 << 34 ) +#define v_x367 ( v_x366 | ( v_x366 << 1 ) ) +#define v_x368 ( v_x87 << 35 ) +#define v_x369 ( v_x368 | ( v_x368 << 1 ) ) +#define v_x370 ( v_x367 | v_x369 ) +#define v_x371 ( v_x370 | ( v_x370 << 4 ) ) +#define v_x372 ( v_x365 | v_x371 ) +#define v_x373 ( v_x372 | ( v_x372 << 16 ) ) +#define v_x374 ( v_x105 | v_x373 ) + case 106: + return v_x374; +#define v_x375 ( v_x187 << 24 ) +#define v_x376 ( v_x375 | ( v_x375 << 4 ) ) +#define v_x377 ( v_x190 << 28 ) +#define v_x378 ( v_x377 | ( v_x377 << 4 ) ) +#define v_x379 ( v_x376 | v_x378 ) +#define v_x380 ( v_x379 | ( v_x379 << 16 ) ) +#define v_x381 ( v_x106 | v_x380 ) + case 107: + return v_x381; +#define v_x382 ( v_x333 | ( v_x333 << 2 ) ) +#define v_x383 ( v_x80 << 31 ) +#define v_x384 ( v_x383 | ( v_x383 << 1 ) ) +#define v_x385 ( v_x384 | ( v_x384 << 2 ) ) +#define v_x386 ( v_x382 | v_x385 ) +#define v_x387 ( v_x367 | ( v_x367 << 2 ) ) +#define v_x388 ( v_x87 << 37 ) +#define v_x389 ( v_x388 | ( v_x388 << 1 ) ) +#define v_x390 ( v_x389 | ( v_x389 << 2 ) ) +#define v_x391 ( v_x387 | v_x390 ) +#define v_x392 ( v_x386 | v_x391 ) +#define v_x393 ( v_x392 | ( v_x392 << 16 ) ) +#define v_x394 ( v_x107 | v_x393 ) + case 108: + return v_x394; +#define v_x395 ( v_x207 << 24 ) +#define v_x396 ( v_x395 | ( v_x395 << 2 ) ) +#define v_x397 ( v_x210 << 26 ) +#define v_x398 ( v_x397 | ( v_x397 << 2 ) ) +#define v_x399 ( v_x396 | v_x398 ) +#define v_x400 ( v_x214 << 28 ) +#define v_x401 ( v_x400 | ( v_x400 << 2 ) ) +#define v_x402 ( v_x217 << 30 ) +#define v_x403 ( v_x402 | ( v_x402 << 2 ) ) +#define v_x404 ( v_x401 | v_x403 ) +#define v_x405 ( v_x399 | v_x404 ) +#define v_x406 ( v_x405 | ( v_x405 << 16 ) ) +#define v_x407 ( v_x108 | v_x406 ) + case 109: + return v_x407; +#define v_x408 ( v_x223 << 24 ) +#define v_x409 ( v_x408 | ( v_x408 << 1 ) ) +#define v_x410 ( v_x226 << 25 ) +#define v_x411 ( v_x410 | ( v_x410 << 1 ) ) +#define v_x412 ( v_x409 | v_x411 ) +#define v_x413 ( v_x230 << 26 ) +#define v_x414 ( v_x413 | ( v_x413 << 1 ) ) +#define v_x415 ( v_x233 << 27 ) +#define v_x416 ( v_x415 | ( v_x415 << 1 ) ) +#define v_x417 ( v_x414 | v_x416 ) +#define v_x418 ( v_x412 | v_x417 ) +#define v_x419 ( v_x238 << 28 ) +#define v_x420 ( v_x419 | ( v_x419 << 1 ) ) +#define v_x421 ( v_x241 << 29 ) +#define v_x422 ( v_x421 | ( v_x421 << 1 ) ) +#define v_x423 ( v_x420 | v_x422 ) +#define v_x424 ( v_x245 << 30 ) +#define v_x425 ( v_x424 | ( v_x424 << 1 ) ) +#define v_x426 ( v_x248 << 31 ) +#define v_x427 ( v_x426 | ( v_x426 << 1 ) ) +#define v_x428 ( v_x425 | v_x427 ) +#define v_x429 ( v_x423 | v_x428 ) +#define v_x430 ( v_x418 | v_x429 ) +#define v_x431 ( v_x430 | ( v_x430 << 16 ) ) +#define v_x432 ( v_x109 | v_x431 ) + case 110: + return v_x432; +#define v_x433 ( x & 4294901760 ) +#define v_x434 ( v_x433 << 16 ) +#define v_x435 ( v_x434 | ( v_x434 << 16 ) ) +#define v_x436 ( v_x110 | v_x435 ) + case 111: + return v_x436; +#define v_x437 ( v_x350 | ( v_x350 << 8 ) ) +#define v_x438 ( v_x28 << 45 ) +#define v_x439 ( v_x438 | ( v_x438 << 1 ) ) +#define v_x440 ( v_x439 | ( v_x439 << 2 ) ) +#define v_x441 ( v_x440 | ( v_x440 << 4 ) ) +#define v_x442 ( v_x441 | ( v_x441 << 8 ) ) +#define v_x443 ( v_x437 | v_x442 ) +#define v_x444 ( v_x116 | v_x443 ) + case 112: + return v_x444; +#define v_x445 ( v_x358 | ( v_x358 << 8 ) ) +#define v_x446 ( v_x72 << 42 ) +#define v_x447 ( v_x446 | ( v_x446 << 2 ) ) +#define v_x448 ( v_x447 | ( v_x447 << 4 ) ) +#define v_x449 ( v_x448 | ( v_x448 << 8 ) ) +#define v_x450 ( v_x445 | v_x449 ) +#define v_x451 ( v_x121 | v_x450 ) + case 113: + return v_x451; +#define v_x452 ( v_x365 | ( v_x365 << 8 ) ) +#define v_x453 ( v_x84 << 42 ) +#define v_x454 ( v_x453 | ( v_x453 << 1 ) ) +#define v_x455 ( v_x87 << 43 ) +#define v_x456 ( v_x455 | ( v_x455 << 1 ) ) +#define v_x457 ( v_x454 | v_x456 ) +#define v_x458 ( v_x457 | ( v_x457 << 4 ) ) +#define v_x459 ( v_x458 | ( v_x458 << 8 ) ) +#define v_x460 ( v_x452 | v_x459 ) +#define v_x461 ( v_x129 | v_x460 ) + case 114: + return v_x461; +#define v_x462 ( v_x376 | ( v_x376 << 8 ) ) +#define v_x463 ( v_x190 << 36 ) +#define v_x464 ( v_x463 | ( v_x463 << 4 ) ) +#define v_x465 ( v_x464 | ( v_x464 << 8 ) ) +#define v_x466 ( v_x462 | v_x465 ) +#define v_x467 ( v_x133 | v_x466 ) + case 115: + return v_x467; +#define v_x468 ( v_x386 | ( v_x386 << 8 ) ) +#define v_x469 ( v_x454 | ( v_x454 << 2 ) ) +#define v_x470 ( v_x87 << 45 ) +#define v_x471 ( v_x470 | ( v_x470 << 1 ) ) +#define v_x472 ( v_x471 | ( v_x471 << 2 ) ) +#define v_x473 ( v_x469 | v_x472 ) +#define v_x474 ( v_x473 | ( v_x473 << 8 ) ) +#define v_x475 ( v_x468 | v_x474 ) +#define v_x476 ( v_x140 | v_x475 ) + case 116: + return v_x476; +#define v_x477 ( v_x399 | ( v_x399 << 8 ) ) +#define v_x478 ( v_x214 << 36 ) +#define v_x479 ( v_x478 | ( v_x478 << 2 ) ) +#define v_x480 ( v_x217 << 38 ) +#define v_x481 ( v_x480 | ( v_x480 << 2 ) ) +#define v_x482 ( v_x479 | v_x481 ) +#define v_x483 ( v_x482 | ( v_x482 << 8 ) ) +#define v_x484 ( v_x477 | v_x483 ) +#define v_x485 ( v_x147 | v_x484 ) + case 117: + return v_x485; +#define v_x486 ( v_x418 | ( v_x418 << 8 ) ) +#define v_x487 ( v_x238 << 36 ) +#define v_x488 ( v_x487 | ( v_x487 << 1 ) ) +#define v_x489 ( v_x241 << 37 ) +#define v_x490 ( v_x489 | ( v_x489 << 1 ) ) +#define v_x491 ( v_x488 | v_x490 ) +#define v_x492 ( v_x245 << 38 ) +#define v_x493 ( v_x492 | ( v_x492 << 1 ) ) +#define v_x494 ( v_x248 << 39 ) +#define v_x495 ( v_x494 | ( v_x494 << 1 ) ) +#define v_x496 ( v_x493 | v_x495 ) +#define v_x497 ( v_x491 | v_x496 ) +#define v_x498 ( v_x497 | ( v_x497 << 8 ) ) +#define v_x499 ( v_x486 | v_x498 ) +#define v_x500 ( v_x160 | v_x499 ) + case 118: + return v_x500; +#define v_x501 ( x & 16711680 ) +#define v_x502 ( v_x501 << 16 ) +#define v_x503 ( v_x502 | ( v_x502 << 8 ) ) +#define v_x504 ( x & 4278190080 ) +#define v_x505 ( v_x504 << 24 ) +#define v_x506 ( v_x505 | ( v_x505 << 8 ) ) +#define v_x507 ( v_x503 | v_x506 ) +#define v_x508 ( v_x164 | v_x507 ) + case 119: + return v_x508; +#define v_x509 ( v_x382 | ( v_x382 << 4 ) ) +#define v_x510 ( v_x80 << 35 ) +#define v_x511 ( v_x510 | ( v_x510 << 1 ) ) +#define v_x512 ( v_x511 | ( v_x511 << 2 ) ) +#define v_x513 ( v_x512 | ( v_x512 << 4 ) ) +#define v_x514 ( v_x509 | v_x513 ) +#define v_x515 ( v_x469 | ( v_x469 << 4 ) ) +#define v_x516 ( v_x87 << 49 ) +#define v_x517 ( v_x516 | ( v_x516 << 1 ) ) +#define v_x518 ( v_x517 | ( v_x517 << 2 ) ) +#define v_x519 ( v_x518 | ( v_x518 << 4 ) ) +#define v_x520 ( v_x515 | v_x519 ) +#define v_x521 ( v_x514 | v_x520 ) +#define v_x522 ( v_x171 | v_x521 ) + case 120: + return v_x522; +#define v_x523 ( v_x396 | ( v_x396 << 4 ) ) +#define v_x524 ( v_x210 << 30 ) +#define v_x525 ( v_x524 | ( v_x524 << 2 ) ) +#define v_x526 ( v_x525 | ( v_x525 << 4 ) ) +#define v_x527 ( v_x523 | v_x526 ) +#define v_x528 ( v_x479 | ( v_x479 << 4 ) ) +#define v_x529 ( v_x217 << 42 ) +#define v_x530 ( v_x529 | ( v_x529 << 2 ) ) +#define v_x531 ( v_x530 | ( v_x530 << 4 ) ) +#define v_x532 ( v_x528 | v_x531 ) +#define v_x533 ( v_x527 | v_x532 ) +#define v_x534 ( v_x177 | v_x533 ) + case 121: + return v_x534; +#define v_x535 ( v_x412 | ( v_x412 << 4 ) ) +#define v_x536 ( v_x230 << 30 ) +#define v_x537 ( v_x536 | ( v_x536 << 1 ) ) +#define v_x538 ( v_x233 << 31 ) +#define v_x539 ( v_x538 | ( v_x538 << 1 ) ) +#define v_x540 ( v_x537 | v_x539 ) +#define v_x541 ( v_x540 | ( v_x540 << 4 ) ) +#define v_x542 ( v_x535 | v_x541 ) +#define v_x543 ( v_x491 | ( v_x491 << 4 ) ) +#define v_x544 ( v_x245 << 42 ) +#define v_x545 ( v_x544 | ( v_x544 << 1 ) ) +#define v_x546 ( v_x248 << 43 ) +#define v_x547 ( v_x546 | ( v_x546 << 1 ) ) +#define v_x548 ( v_x545 | v_x547 ) +#define v_x549 ( v_x548 | ( v_x548 << 4 ) ) +#define v_x550 ( v_x543 | v_x549 ) +#define v_x551 ( v_x542 | v_x550 ) +#define v_x552 ( v_x186 | v_x551 ) + case 122: + return v_x552; +#define v_x553 ( x & 983040 ) +#define v_x554 ( v_x553 << 16 ) +#define v_x555 ( v_x554 | ( v_x554 << 4 ) ) +#define v_x556 ( x & 15728640 ) +#define v_x557 ( v_x556 << 20 ) +#define v_x558 ( v_x557 | ( v_x557 << 4 ) ) +#define v_x559 ( v_x555 | v_x558 ) +#define v_x560 ( x & 251658240 ) +#define v_x561 ( v_x560 << 24 ) +#define v_x562 ( v_x561 | ( v_x561 << 4 ) ) +#define v_x563 ( x & 4026531840 ) +#define v_x564 ( v_x563 << 28 ) +#define v_x565 ( v_x564 | ( v_x564 << 4 ) ) +#define v_x566 ( v_x562 | v_x565 ) +#define v_x567 ( v_x559 | v_x566 ) +#define v_x568 ( v_x194 | v_x567 ) + case 123: + return v_x568; +#define v_x569 ( v_x409 | ( v_x409 << 2 ) ) +#define v_x570 ( v_x226 << 27 ) +#define v_x571 ( v_x570 | ( v_x570 << 1 ) ) +#define v_x572 ( v_x571 | ( v_x571 << 2 ) ) +#define v_x573 ( v_x569 | v_x572 ) +#define v_x574 ( v_x537 | ( v_x537 << 2 ) ) +#define v_x575 ( v_x233 << 33 ) +#define v_x576 ( v_x575 | ( v_x575 << 1 ) ) +#define v_x577 ( v_x576 | ( v_x576 << 2 ) ) +#define v_x578 ( v_x574 | v_x577 ) +#define v_x579 ( v_x573 | v_x578 ) +#define v_x580 ( v_x488 | ( v_x488 << 2 ) ) +#define v_x581 ( v_x241 << 39 ) +#define v_x582 ( v_x581 | ( v_x581 << 1 ) ) +#define v_x583 ( v_x582 | ( v_x582 << 2 ) ) +#define v_x584 ( v_x580 | v_x583 ) +#define v_x585 ( v_x545 | ( v_x545 << 2 ) ) +#define v_x586 ( v_x248 << 45 ) +#define v_x587 ( v_x586 | ( v_x586 << 1 ) ) +#define v_x588 ( v_x587 | ( v_x587 << 2 ) ) +#define v_x589 ( v_x585 | v_x588 ) +#define v_x590 ( v_x584 | v_x589 ) +#define v_x591 ( v_x579 | v_x590 ) +#define v_x592 ( v_x206 | v_x591 ) + case 124: + return v_x592; +#define v_x593 ( x & 196608 ) +#define v_x594 ( v_x593 << 16 ) +#define v_x595 ( v_x594 | ( v_x594 << 2 ) ) +#define v_x596 ( x & 786432 ) +#define v_x597 ( v_x596 << 18 ) +#define v_x598 ( v_x597 | ( v_x597 << 2 ) ) +#define v_x599 ( v_x595 | v_x598 ) +#define v_x600 ( x & 3145728 ) +#define v_x601 ( v_x600 << 20 ) +#define v_x602 ( v_x601 | ( v_x601 << 2 ) ) +#define v_x603 ( x & 12582912 ) +#define v_x604 ( v_x603 << 22 ) +#define v_x605 ( v_x604 | ( v_x604 << 2 ) ) +#define v_x606 ( v_x602 | v_x605 ) +#define v_x607 ( v_x599 | v_x606 ) +#define v_x608 ( x & 50331648 ) +#define v_x609 ( v_x608 << 24 ) +#define v_x610 ( v_x609 | ( v_x609 << 2 ) ) +#define v_x611 ( x & 201326592 ) +#define v_x612 ( v_x611 << 26 ) +#define v_x613 ( v_x612 | ( v_x612 << 2 ) ) +#define v_x614 ( v_x610 | v_x613 ) +#define v_x615 ( x & 805306368 ) +#define v_x616 ( v_x615 << 28 ) +#define v_x617 ( v_x616 | ( v_x616 << 2 ) ) +#define v_x618 ( x & 3221225472 ) +#define v_x619 ( v_x618 << 30 ) +#define v_x620 ( v_x619 | ( v_x619 << 2 ) ) +#define v_x621 ( v_x617 | v_x620 ) +#define v_x622 ( v_x614 | v_x621 ) +#define v_x623 ( v_x607 | v_x622 ) +#define v_x624 ( v_x222 | v_x623 ) + case 125: + return v_x624; +#define v_x625 ( x & 65536 ) +#define v_x626 ( v_x625 << 16 ) +#define v_x627 ( v_x626 | ( v_x626 << 1 ) ) +#define v_x628 ( x & 131072 ) +#define v_x629 ( v_x628 << 17 ) +#define v_x630 ( v_x629 | ( v_x629 << 1 ) ) +#define v_x631 ( v_x627 | v_x630 ) +#define v_x632 ( x & 262144 ) +#define v_x633 ( v_x632 << 18 ) +#define v_x634 ( v_x633 | ( v_x633 << 1 ) ) +#define v_x635 ( x & 524288 ) +#define v_x636 ( v_x635 << 19 ) +#define v_x637 ( v_x636 | ( v_x636 << 1 ) ) +#define v_x638 ( v_x634 | v_x637 ) +#define v_x639 ( v_x631 | v_x638 ) +#define v_x640 ( x & 1048576 ) +#define v_x641 ( v_x640 << 20 ) +#define v_x642 ( v_x641 | ( v_x641 << 1 ) ) +#define v_x643 ( x & 2097152 ) +#define v_x644 ( v_x643 << 21 ) +#define v_x645 ( v_x644 | ( v_x644 << 1 ) ) +#define v_x646 ( v_x642 | v_x645 ) +#define v_x647 ( x & 4194304 ) +#define v_x648 ( v_x647 << 22 ) +#define v_x649 ( v_x648 | ( v_x648 << 1 ) ) +#define v_x650 ( x & 8388608 ) +#define v_x651 ( v_x650 << 23 ) +#define v_x652 ( v_x651 | ( v_x651 << 1 ) ) +#define v_x653 ( v_x649 | v_x652 ) +#define v_x654 ( v_x646 | v_x653 ) +#define v_x655 ( v_x639 | v_x654 ) +#define v_x656 ( x & 16777216 ) +#define v_x657 ( v_x656 << 24 ) +#define v_x658 ( v_x657 | ( v_x657 << 1 ) ) +#define v_x659 ( x & 33554432 ) +#define v_x660 ( v_x659 << 25 ) +#define v_x661 ( v_x660 | ( v_x660 << 1 ) ) +#define v_x662 ( v_x658 | v_x661 ) +#define v_x663 ( x & 67108864 ) +#define v_x664 ( v_x663 << 26 ) +#define v_x665 ( v_x664 | ( v_x664 << 1 ) ) +#define v_x666 ( x & 134217728 ) +#define v_x667 ( v_x666 << 27 ) +#define v_x668 ( v_x667 | ( v_x667 << 1 ) ) +#define v_x669 ( v_x665 | v_x668 ) +#define v_x670 ( v_x662 | v_x669 ) +#define v_x671 ( x & 268435456 ) +#define v_x672 ( v_x671 << 28 ) +#define v_x673 ( v_x672 | ( v_x672 << 1 ) ) +#define v_x674 ( x & 536870912 ) +#define v_x675 ( v_x674 << 29 ) +#define v_x676 ( v_x675 | ( v_x675 << 1 ) ) +#define v_x677 ( v_x673 | v_x676 ) +#define v_x678 ( x & 1073741824 ) +#define v_x679 ( v_x678 << 30 ) +#define v_x680 ( v_x679 | ( v_x679 << 1 ) ) +#define v_x681 ( x & 2147483648 ) +#define v_x682 ( v_x681 << 31 ) +#define v_x683 ( v_x682 | ( v_x682 << 1 ) ) +#define v_x684 ( v_x680 | v_x683 ) +#define v_x685 ( v_x677 | v_x684 ) +#define v_x686 ( v_x670 | v_x685 ) +#define v_x687 ( v_x655 | v_x686 ) +#define v_x688 ( v_x254 | v_x687 ) + case 126: + return v_x688; + case 127: + return x; + default: + assert( false ); + return 0; + } +} + +} // namespace detail +} // namespace kitty + +/*! \endcond */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/utils.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/utils.hpp new file mode 100644 index 00000000000..907ab583755 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/utils.hpp @@ -0,0 +1,85 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file utils.hpp + \brief Helper functions + + \author Mathias Soeken +*/ + +/*! \cond PRIVATE */ +#pragma once + +#include +#include + +namespace kitty +{ + +namespace detail +{ + +/* string utils are from https://stackoverflow.com/a/217605 */ +inline void ltrim( std::string& s ) +{ + s.erase( s.begin(), std::find_if( s.begin(), s.end(), []( int ch ) + { return std::isspace( ch ) == 0; } ) ); +} + +inline void rtrim( std::string& s ) +{ + s.erase( std::find_if( s.rbegin(), s.rend(), []( int ch ) + { return std::isspace( ch ) == 0; } ) + .base(), + s.end() ); +} + +inline void trim( std::string& s ) +{ + ltrim( s ); + rtrim( s ); +} + +inline std::string ltrim_copy( std::string s ) +{ + ltrim( s ); + return s; +} + +inline std::string rtrim_copy( std::string s ) +{ + rtrim( s ); + return s; +} + +inline std::string trim_copy( std::string s ) +{ + trim( s ); + return s; +} +} /* namespace detail */ +} /* namespace kitty */ + /*! \endcond */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/dynamic_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/dynamic_truth_table.hpp new file mode 100644 index 00000000000..021bc8822f7 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/dynamic_truth_table.hpp @@ -0,0 +1,191 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dynamic_truth_table.hpp + \brief Implements dynamic_truth_table + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! Truth table in which number of variables is known at runtime. + */ +struct dynamic_truth_table +{ + /*! Standard constructor. + + The number of variables provided to the truth table can be + computed at runtime. However, once the truth table is constructed + its number of variables cannot change anymore. + + The constructor computes the number of blocks and resizes the + vector accordingly. + + \param num_vars Number of variables + */ + explicit dynamic_truth_table( uint32_t num_vars ) + : _bits( ( num_vars <= 6 ) ? 1u : ( 1u << ( num_vars - 6 ) ) ), + _num_vars( num_vars ) + { + } + + /*! Empty constructor. + + Creates an empty truth table. It has 0 variables, but no bits, i.e., it is + different from a truth table for the constant function. This constructor is + only used for convenience, if algorithms require the existence of default + constructable classes. + */ + dynamic_truth_table() : _num_vars( 0 ) {} + + /*! Constructs a new dynamic truth table instance with the same number of variables. */ + inline dynamic_truth_table construct() const + { + return dynamic_truth_table( _num_vars ); + } + + /*! Returns number of variables. + */ + inline auto num_vars() const noexcept { return _num_vars; } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return _bits.size(); } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return uint64_t( 1 ) << _num_vars; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend() const noexcept { return _bits.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. The truth + table type has to be complete. The vector of bits is resized accordingly. + + \param other Other truth table + */ + template::value && is_complete_truth_table::value>> + dynamic_truth_table& operator=( const TT& other ) + { + _bits.resize( other.num_blocks() ); + std::copy( other.begin(), other.end(), begin() ); + _num_vars = other.num_vars(); + + if ( _num_vars < 6 ) + { + mask_bits(); + } + + return *this; + } + + /*! Masks the number of valid truth table bits. + + If the truth table has less than 6 variables, it may not use all + the bits. This operation makes sure to zero out all non-valid + bits. + */ + inline void mask_bits() noexcept + { + if ( _num_vars < 6 ) + { + _bits[0u] &= detail::masks[_num_vars]; + } + } + + /*! \cond PRIVATE */ +public: /* fields */ + std::vector _bits; + uint32_t _num_vars; + /*! \endcond */ +}; + +template<> +struct is_truth_table : std::true_type +{ +}; + +template<> +struct is_complete_truth_table : std::true_type +{ +}; + +template<> +struct is_completely_specified_truth_table : std::true_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/enumeration.hpp b/third-party/mockturtle/lib/kitty/kitty/enumeration.hpp new file mode 100644 index 00000000000..e1e00a1625e --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/enumeration.hpp @@ -0,0 +1,105 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file enumeration.hpp + \brief Enumeration routines + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "operators.hpp" + +namespace kitty +{ + +/*! \brief Enumerate all representatives using 1-neighborhood search. + + This function is based on Algorithm 4.2.1 in the PhD thesis + "Analysis of Affine Equivalent Boolean Functions for Cryptography" + by J.E. Fuller (Queensland University of Technology) + + \param functions Vector must be initialized with single seed function, will + contain all enumerated functions after call + \param canonization_fn A canonization function, which takes as input a truth + table and returns a truth table +*/ +template +void fuller_neighborhood_enumeration( std::vector& functions, CanonizationFn&& canonization_fn ) +{ + /* there must be one seed truth table given */ + assert( functions.size() == 1u ); + + /* classify seed function */ + functions.front() = canonization_fn( functions.front() ); + + /* get number of bits from seed truth table */ + const auto num_bits = static_cast( functions.front().num_bits() ); + std::vector neighborhood( num_bits ); + uint32_t num{ 1 }; + std::stack stack; + stack.push( functions.front() ); + + while ( !stack.empty() ) + { + const auto g = stack.top(); + stack.pop(); + + /* Finding connecting classes */ + for ( auto j = 0u; j < num_bits; ++j ) + { + neighborhood[j] = g; + flip_bit( neighborhood[j], j ); + neighborhood[j] = canonization_fn( neighborhood[j] ); + } + + for ( auto j = 0u; j < num_bits; ++j ) + { + bool flag{ false }; + for ( auto i = 0u; i < num; ++i ) + { + if ( neighborhood[j] == functions[i] ) + { + flag = true; + break; + } + } + if ( !flag ) + { + ++num; + functions.push_back( neighborhood[j] ); + stack.push( neighborhood[j] ); + } + } + } +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/esop.hpp b/third-party/mockturtle/lib/kitty/kitty/esop.hpp new file mode 100644 index 00000000000..26f3d829cee --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/esop.hpp @@ -0,0 +1,349 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file esop.hpp + \brief Implements methods to compute exclusive sum-of-products (ESOP) representations + + \author Mathias Soeken + \author Winston Haaswijk +*/ + +#pragma once + +// #ifndef _MSC_VER +// #warning "DEPRECATED: the functions in this file are marked as deprecated. Most recent implementation can be found in https://github.com/hriener/easy/ in the file src/esop/constructors.hpp" +// #endif + +#include +#include +#include + +#include "algorithm.hpp" +#include "constructors.hpp" +#include "cube.hpp" +#include "hash.hpp" +#include "operations.hpp" +#include "operators.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \cond PRIVATE */ +namespace detail +{ + +enum class pkrm_decomposition +{ + positive_davio, + negative_davio, + shannon +}; + +template +using expansion_cache = std::unordered_map, hash>; + +template +uint32_t find_pkrm_expansions( const TT& tt, expansion_cache& cache, uint8_t var_index ) +{ + /* terminal cases */ + if ( is_const0( tt ) ) + { + return 0; + } + if ( is_const0( ~tt ) ) + { + return 1; + } + + /* already computed */ + const auto it = cache.find( tt ); + if ( it != cache.end() ) + { + return it->second.first; + } + + const auto tt0 = cofactor0( tt, var_index ); + const auto tt1 = cofactor1( tt, var_index ); + + const auto ex0 = find_pkrm_expansions( tt0, cache, var_index + 1 ); + const auto ex1 = find_pkrm_expansions( tt1, cache, var_index + 1 ); + const auto ex2 = find_pkrm_expansions( tt0 ^ tt1, cache, var_index + 1 ); + + const auto ex_max = std::max( std::max( ex0, ex1 ), ex2 ); + + uint32_t cost{}; + pkrm_decomposition decomp; + + if ( ex_max == ex0 ) + { + cost = ex1 + ex2; + decomp = pkrm_decomposition::positive_davio; + } + else if ( ex_max == ex1 ) + { + cost = ex0 + ex2; + decomp = pkrm_decomposition::negative_davio; + } + else + { + cost = ex0 + ex1; + decomp = pkrm_decomposition::shannon; + } + cache.insert( { tt, { cost, decomp } } ); + return cost; +} + +inline void add_to_cubes( std::unordered_set>& pkrm, const cube& c, bool distance_one_merging = true ) +{ + /* first check whether cube is already contained; if so, delete it */ + const auto it = pkrm.find( c ); + if ( it != pkrm.end() ) + { + pkrm.erase( it ); + return; + } + + /* otherwise, check if there is a distance-1 cube; if so, merge it */ + if ( distance_one_merging ) + { + for ( auto it = pkrm.begin(); it != pkrm.end(); ++it ) + { + if ( c.distance( *it ) == 1 ) + { + auto new_cube = c.merge( *it ); + pkrm.erase( it ); + add_to_cubes( pkrm, new_cube ); + return; + } + } + } + + /* otherwise, just add the cube */ + pkrm.insert( c ); +} + +inline cube with_literal( const cube& c, uint8_t var_index, bool polarity ) +{ + auto copy = c; + copy.add_literal( var_index, polarity ); + return copy; +} + +template +void optimum_pkrm_rec( std::unordered_set>& pkrm, const TT& tt, const expansion_cache& cache, uint8_t var_index, const cube& c ) +{ + /* terminal cases */ + if ( is_const0( tt ) ) + { + return; + } + if ( is_const0( ~tt ) ) + { + add_to_cubes( pkrm, c ); + return; + } + + const auto& p = cache.at( tt ); + + const auto tt0 = cofactor0( tt, var_index ); + const auto tt1 = cofactor1( tt, var_index ); + + switch ( p.second ) + { + case pkrm_decomposition::positive_davio: + optimum_pkrm_rec( pkrm, tt0, cache, var_index + 1, c ); + optimum_pkrm_rec( pkrm, tt0 ^ tt1, cache, var_index + 1, with_literal( c, var_index, true ) ); + break; + case pkrm_decomposition::negative_davio: + optimum_pkrm_rec( pkrm, tt1, cache, var_index + 1, c ); + optimum_pkrm_rec( pkrm, tt0 ^ tt1, cache, var_index + 1, with_literal( c, var_index, false ) ); + break; + case pkrm_decomposition::shannon: + optimum_pkrm_rec( pkrm, tt0, cache, var_index + 1, with_literal( c, var_index, false ) ); + optimum_pkrm_rec( pkrm, tt1, cache, var_index + 1, with_literal( c, var_index, true ) ); + break; + } +} + +template +void esop_from_pprm_rec( std::unordered_set>& cubes, const TT& tt, uint8_t var_index, const cube& c ) +{ + /* terminal cases */ + if ( is_const0( tt ) ) + { + return; + } + if ( is_const0( ~tt ) ) + { + /* add to cubes, but do not apply distance-1 merging */ + add_to_cubes( cubes, c, false ); + return; + } + + const auto tt0 = cofactor0( tt, var_index ); + const auto tt1 = cofactor1( tt, var_index ); + + esop_from_pprm_rec( cubes, tt0, var_index + 1, c ); + esop_from_pprm_rec( cubes, tt0 ^ tt1, var_index + 1, with_literal( c, var_index, true ) ); +} + +static constexpr uint64_t ANF1[] = { 0, 3, 2, 1 }; +static constexpr uint64_t ANF2[] = { 0, 15, 10, 5, 12, 3, 6, 9, 8, 7, 2, 13, 4, 11, 14, 1 }; +static constexpr uint64_t ANF3[] = { 0, 255, 170, 85, 204, 51, 102, 153, 136, 119, 34, 221, 68, 187, 238, 17, 240, 15, 90, 165, 60, 195, 150, 105, 120, 135, 210, 45, 180, 75, 30, 225, 160, 95, 10, 245, 108, 147, 198, 57, 40, 215, 130, 125, 228, 27, 78, 177, 80, 175, 250, 5, 156, 99, 54, 201, 216, 39, 114, 141, 20, 235, 190, 65, 192, 63, 106, 149, 12, 243, 166, 89, 72, 183, 226, 29, 132, 123, 46, 209, 48, 207, 154, 101, 252, 3, 86, 169, 184, 71, 18, 237, 116, 139, 222, 33, 96, 159, 202, 53, 172, 83, 6, 249, 232, 23, 66, 189, 36, 219, 142, 113, 144, 111, 58, 197, 92, 163, 246, 9, 24, 231, 178, 77, 212, 43, 126, 129, 128, 127, 42, 213, 76, 179, 230, 25, 8, 247, 162, 93, 196, 59, 110, 145, 112, 143, 218, 37, 188, 67, 22, 233, 248, 7, 82, 173, 52, 203, 158, 97, 32, 223, 138, 117, 236, 19, 70, 185, 168, 87, 2, 253, 100, 155, 206, 49, 208, 47, 122, 133, 28, 227, 182, 73, 88, 167, 242, 13, 148, 107, 62, 193, 64, 191, 234, 21, 140, 115, 38, 217, 200, 55, 98, 157, 4, 251, 174, 81, 176, 79, 26, 229, 124, 131, 214, 41, 56, 199, 146, 109, 244, 11, 94, 161, 224, 31, 74, 181, 44, 211, 134, 121, 104, 151, 194, 61, 164, 91, 14, 241, 16, 239, 186, 69, 220, 35, 118, 137, 152, 103, 50, 205, 84, 171, 254, 1 }; + +template +TT algebraic_normal_form( const TT& func ) +{ + switch ( func.num_vars() ) + { + case 0: + return func; + case 1: + { + auto r = func.construct(); + kitty::create_from_words( r, &ANF1[*func.begin()], &ANF1[*func.begin()] + 1 ); + return r; + } + case 2: + { + auto r = func.construct(); + kitty::create_from_words( r, &ANF2[*func.begin()], &ANF2[*func.begin()] + 1 ); + return r; + } + case 3: + { + auto r = func.construct(); + kitty::create_from_words( r, &ANF3[*func.begin()], &ANF3[*func.begin()] + 1 ); + return r; + } + case 4: + return unary_operation( func, []( uint64_t word ) + { + const auto b0 = ANF3[word & 0x000000FF]; + const auto b1 = ANF3[( word & 0x0000FF00 ) >> 8]; + + return b0 | ( ( b0 ^ b1 ) << 8 ); } ); + case 5: + return unary_operation( func, []( uint64_t word ) + { + const auto b0 = ANF3[word & 0xFF]; + const auto b1 = ANF3[( word >> 010 ) & 0xFF]; + const auto b2 = ANF3[( word >> 020 ) & 0xFF]; + const auto b3 = ANF3[( word >> 030 ) & 0xFF]; + + return b0 | ( ( b0 ^ b1 ) << 010 ) | ( ( b0 ^ b2 ) << 020 ) | ( ( b0 ^ b1 ^ b2 ^ b3 ) << 030 ); } ); + default: + auto r = unary_operation( func, []( uint64_t word ) + { + const auto b0 = ANF3[word & 0xFF]; + const auto b1 = ANF3[( word >> 010 ) & 0xFF]; + const auto b2 = ANF3[( word >> 020 ) & 0xFF]; + const auto b3 = ANF3[( word >> 030 ) & 0xFF]; + const auto b4 = ANF3[( word >> 040 ) & 0xFF]; + const auto b5 = ANF3[( word >> 050 ) & 0xFF]; + const auto b6 = ANF3[( word >> 060 ) & 0xFF]; + const auto b7 = ANF3[( word >> 070 ) & 0xFF]; + + return b0 | + ( ( b0 ^ b1 ) << 010 ) | + ( ( b0 ^ b2 ) << 020 ) | + ( ( b0 ^ b1 ^ b2 ^ b3 ) << 030 ) | + ( ( b0 ^ b4 ) << 040 ) | + ( ( b0 ^ b1 ^ b4 ^ b5 ) << 050 ) | + ( ( b0 ^ b2 ^ b4 ^ b6 ) << 060 ) | + ( ( b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7 ) << 070 ); } ); + + for ( auto i = 1u; i < (uint32_t)func.num_blocks(); i = i << 1u ) + { + for ( auto j = 0u; j < (uint32_t)func.num_blocks(); j += i << 1u ) + { + for ( auto k = j; k < j + i; ++k ) + { + *( r.begin() + k + i ) ^= *( r.begin() + k ); + } + } + } + + return r; + } +} +} // namespace detail +/*! \endcond */ + +/*! \brief Computes ESOP representation using optimum PKRM + + This algorithm first computes an ESOP using the algorithm described + in [R. Drechsler, IEEE Trans. C 48(9), 1999, 987–990]. + + The algorithm applies post-optimization to merge distance-1 cubes. + + \param tt Truth table +*/ +template +inline std::vector esop_from_optimum_pkrm( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::unordered_set> cubes; + detail::expansion_cache cache; + + detail::find_pkrm_expansions( tt, cache, 0 ); + detail::optimum_pkrm_rec( cubes, tt, cache, 0, cube() ); + + return std::vector( cubes.begin(), cubes.end() ); +} + +template +inline std::vector esop_from_pprm_slow( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::unordered_set> cubes; + detail::esop_from_pprm_rec( cubes, tt, 0, cube() ); + + return std::vector( cubes.begin(), cubes.end() ); +} + +/*! \brief Computes PPRM representation for a function + + This algorithm applies recursively the positive Davio decomposition which + eventually leads into the PPRM representation of a function. + + \param tt Truth table +*/ +template +std::vector esop_from_pprm( const TT& func ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector cubes; + for_each_one_bit( detail::algebraic_normal_form( func ), [&]( auto bit ) + { cubes.emplace_back( (uint32_t)bit, (uint32_t)bit ); } ); + return cubes; +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/hash.hpp b/third-party/mockturtle/lib/kitty/kitty/hash.hpp new file mode 100644 index 00000000000..684f3d2dd57 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/hash.hpp @@ -0,0 +1,95 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file hash.hpp + \brief Implements hash functions for truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "static_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Hash function for 64-bit word */ +inline std::size_t hash_block( uint64_t word ) +{ + /* from boost::hash_detail::hash_value_unsigned */ + return word ^ ( word + ( word << 6 ) + ( word >> 2 ) ); +} + +/*! \brief Combines two hash values */ +inline void hash_combine( std::size_t& seed, std::size_t other ) +{ + /* from boost::hash_detail::hash_combine_impl */ + const uint64_t m = UINT64_C( 0xc6a4a7935bd1e995 ); + const int r = 47; + + other *= m; + other ^= other >> r; + other *= m; + + seed ^= other; + seed *= m; + + seed += 0xe6546b64; +} + +/*! \brief Computes hash values for truth tables */ +template +struct hash +{ + std::size_t operator()( const TT& tt ) const + { + auto it = std::begin( tt._bits ); + auto seed = hash_block( *it++ ); + + while ( it != std::end( tt._bits ) ) + { + hash_combine( seed, hash_block( *it++ ) ); + } + + return seed; + } +}; + +/*! \cond PRIVATE */ +template +struct hash> +{ + inline std::size_t operator()( const static_truth_table& tt ) const + { + return hash_block( tt._bits ); + } +}; +/*! \endcond */ +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/implicant.hpp b/third-party/mockturtle/lib/kitty/kitty/implicant.hpp new file mode 100644 index 00000000000..4667ac24561 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/implicant.hpp @@ -0,0 +1,244 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file implicant.hpp + \brief Find implicants and prime implicants + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "algorithm.hpp" +#include "cube.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \brief Computes all minterms + + \param tt Truth table +*/ +template +std::vector get_minterms( const TT& tt ) +{ + std::vector m; + m.reserve( count_ones( tt ) ); + for_each_one_bit( tt, [&m]( auto index ) + { m.emplace_back( static_cast( index ) ); } ); + return m; +} + +/*! \cond PRIVATE */ +template +inline std::vector> get_jbuddies( Iterator begin, Iterator end, uint32_t j ) +{ + std::vector> buddies; + + auto mask = uint32_t( 1 ) << j; + auto k = begin; + auto kk = begin; + + while ( true ) + { + k = std::find_if( k, end, [mask]( auto m ) + { return ( m & mask ) == 0; } ); + if ( k == end ) + break; + + if ( kk <= k ) + { + kk = k + 1; + } + + kk = std::find_if( kk, end, [mask, &k]( auto m ) + { return m >= ( *k | mask ); } ); + if ( kk == end ) + break; + + if ( ( *k ^ *kk ) >= ( mask << 1 ) ) + { + k = kk; + continue; + } + + if ( *kk == ( *k | mask ) ) + { + buddies.emplace_back( k, kk ); + } + + ++k; + } + + return buddies; +} +/*! \endcond */ + +/*! \brief Computes all j-buddies in a list of minterms + + Computes all pairs \f$(k, k')\f$ such that \f$k < k'\f$ and the two minterms + at indexes \f$k\f$ and \f$k'\f$ only differ in bit \f$j\f$. + + This algorithm is described by Knuth in Exercise TAOCP 7.1.1-29. + + \param minterms Vector of minterms + \param j Bit position +*/ +inline std::vector::const_iterator, std::vector::const_iterator>> get_jbuddies( const std::vector& minterms, uint32_t j ) +{ + return get_jbuddies( minterms.begin(), minterms.end(), j ); +} + +/*! \brief Computes all prime implicants (from minterms) + + This algorithm computes all prime implicants for a list of minterms. The + running time is at most proportional to \f$mn\f$, where \f$m\f$ is the number + of minterms and \f$n\f$ is the number of variables. + + The algorithm is described in Exercise TAOCP 7.1.1-30 by Knuth and is inspired + by the algorithm described in [E. Morreale, IEEE Trans. EC 16(5), 1967, + 611–620]. + + \param minterms Vector of minterms (as integer values) + \param num_vars Number of variables +*/ +inline std::vector get_prime_implicants_morreale( const std::vector& minterms, unsigned num_vars ) +{ + std::vector cubes; + + const auto n = num_vars; + const auto m = minterms.size(); + + std::vector tags( 2 * m + n, 0 ); + std::vector stack( 2 * m + n, 0 ); + + uint32_t mask = ( 1 << n ) - 1; + uint32_t A{}; + + /* P1 */ + + /* Update tags using j-buddy algorithm */ + for ( auto j = 0u; j < n; ++j ) + { + for ( const auto& p : get_jbuddies( minterms, j ) ) + { + const auto k = std::distance( minterms.begin(), p.first ); + const auto kk = std::distance( minterms.begin(), p.second ); + + tags[k] |= ( 1 << j ); + tags[kk] |= ( 1 << j ); + } + } + + auto t = 0u; + for ( auto s = 0u; s < m; ++s ) + { + if ( tags[s] == 0u ) + { + cubes.emplace_back( minterms[s], mask ); + } + else + { + stack[t] = minterms[s]; + tags[t] = tags[s]; + t++; + } + } + + stack.push_back( 0 ); + + while ( true ) + { + /* P2 */ + auto j = 0u; + if ( stack[t] == t ) + { + while ( j < n && ( ( A >> j ) & 1 ) == 0 ) + { + ++j; + } + } + + while ( j < n && ( ( A >> j ) & 1 ) != 0 ) + { + t = stack[t] - 1; + A &= ~( 1 << j ); + ++j; + } + + if ( j >= n ) + { + /* terminate */ + return cubes; + } + + A |= ( 1 << j ); + + /* P3 */ + const auto r = t; + const auto s = stack.begin() + stack[t]; + + for ( const auto& p : get_jbuddies( s, stack.begin() + r, j ) ) + { + const auto k = std::distance( stack.begin(), p.first ); + const auto kk = std::distance( stack.begin(), p.second ); + const auto x = tags[k] & tags[kk] & ~( 1 << j ); + + if ( x == 0 ) + { + cubes.emplace_back( stack[k], ~A & mask ); + } + else + { + ++t; + stack[t] = stack[k]; + tags[t] = x; + } + } + + ++t; + stack[t] = r + 1; + } +} + +/*! \brief Computes all prime implicants (from truth table) + + Computes minterms from truth table and calls overloaded function. + + \param tt Truth table +*/ +template +std::vector get_prime_implicants_morreale( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + return get_prime_implicants_morreale( get_minterms( tt ), tt.num_vars() ); +} +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/isop.hpp b/third-party/mockturtle/lib/kitty/kitty/isop.hpp new file mode 100644 index 00000000000..fe09dc324d2 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/isop.hpp @@ -0,0 +1,130 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file isop.hpp + \brief Implements methods to compute irredundant sum-of-products (ISOP) representations + + \author Mathias Soeken +*/ + +#pragma once + +#include "cube.hpp" +#include "operations.hpp" +#include "operators.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \cond PRIVATE */ +namespace detail +{ +template +TT isop_rec( const TT& tt, const TT& dc, uint8_t var_index, std::vector& cubes ) +{ + assert( var_index <= tt.num_vars() ); + assert( is_const0( tt & ~dc ) ); + + if ( is_const0( tt ) ) + { + return tt; + } + + if ( is_const0( ~dc ) ) + { + cubes.emplace_back(); /* add empty cube */ + return dc; + } + + assert( var_index > 0 ); + + int var = var_index - 1; + for ( ; var >= 0; --var ) + { + if ( has_var( tt, var ) || has_var( dc, var ) ) + { + break; + } + } + + assert( var >= 0 ); + + /* co-factor */ + const auto tt0 = cofactor0( tt, var ); + const auto tt1 = cofactor1( tt, var ); + const auto dc0 = cofactor0( dc, var ); + const auto dc1 = cofactor1( dc, var ); + + const auto beg0 = cubes.size(); + const auto res0 = isop_rec( tt0 & ~dc1, dc0, var, cubes ); + const auto end0 = cubes.size(); + const auto res1 = isop_rec( tt1 & ~dc0, dc1, var, cubes ); + const auto end1 = cubes.size(); + auto res2 = isop_rec( ( tt0 & ~res0 ) | ( tt1 & ~res1 ), dc0 & dc1, var, cubes ); + + auto var0 = tt.construct(); + create_nth_var( var0, var, true ); + auto var1 = tt.construct(); + create_nth_var( var1, var ); + res2 |= ( res0 & var0 ) | ( res1 & var1 ); + + for ( auto c = beg0; c < end0; ++c ) + { + cubes[c].add_literal( var, false ); + } + for ( auto c = end0; c < end1; ++c ) + { + cubes[c].add_literal( var, true ); + } + + assert( is_const0( tt & ~res2 ) ); + assert( is_const0( res2 & ~dc ) ); + + return res2; +} +} /* namespace detail */ +/* \endcond */ + +/*! \brief Computes ISOP representation + + Computes the irredundant sum-of-products representation using the + Minato-Morreale algorithm [S. Minato, IEEE Trans. CAD 15(4), 1996, + 377-384]. + + \param tt Truth table +*/ +template +inline std::vector isop( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector cubes; + detail::isop_rec( tt, tt, tt.num_vars(), cubes ); + return cubes; +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/karnaugh_map.hpp b/third-party/mockturtle/lib/kitty/kitty/karnaugh_map.hpp new file mode 100644 index 00000000000..0c6936f448f --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/karnaugh_map.hpp @@ -0,0 +1,188 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file karnaugh_map.hpp + \brief karnaugh maps visualizer + + \author Gianluca Radi +*/ + +#pragma once + +#include "bit_operations.hpp" +#include "constructors.hpp" +#include "operators.hpp" +#include +#include +#include +#include +#include + +namespace kitty +{ +template +class karnaugh_map +{ +public: + /*! \brief Destroys K-map + */ + karnaugh_map() = delete; + + /*! \brief Construncts K-map from truth table + \param tt truth table + */ + karnaugh_map( TT tt ) : truth_table( tt ) + { + uint64_t num_var = log2( (double)tt.num_bits() ); + vars_col = num_var >> 1; + vars_row = num_var - vars_col; + col_seq = compute_seq_1ham_dist( vars_col ); + row_seq = compute_seq_1ham_dist( vars_row ); + } + + /*! \brief Prints K-map + \param os output stream (default = cout) + */ + + void print( std::ostream& os = std::cout ) + { + print_space( vars_col, os ); + os << " "; + for ( const auto& i : row_seq ) + { + os << binary( i, vars_row ); + print_space( vars_row, os ); + os << " "; + } + os << std::endl + << std::endl; + + for ( const auto& j : col_seq ) + { + os << binary( j, vars_col ); + os << " "; + for ( const auto& i : row_seq ) + { + uint8_t middle_space = 0; + if ( vars_row > 2 ) + middle_space = vars_row / 2; + print_space( middle_space, os ); + if ( is_dont_care( truth_table, ( j << vars_row ) + i ) ) + os << "-"; + else + { + if ( is_dont_know( truth_table, ( j << vars_row ) + i ) ) + os << "x"; + else + os << get_bit( truth_table, ( j << vars_row ) + i ); + } + print_space( ( vars_row << 1 ) - 1 - middle_space, os ); + os << " "; + } + os << std::endl + << std::endl; + } + } + + /*! \brief Get sequence of values for the least significant variables + */ + std::vector get_row_seq() const { return row_seq; } + + /*! \brief Get sequence of values for the most significant variables + */ + std::vector get_col_seq() const { return col_seq; } + +private: + TT truth_table; + unsigned vars_row; + unsigned vars_col; + std::vector row_seq; + std::vector col_seq; + + /*! \brief Converts unsigned into binary string + \param n unsigned to convert + \param max_var number of bits of the string + */ + std::string binary( uint8_t n, uint8_t max_var ) + { + std::string result; + uint8_t count = 0u; + + do + { + result.insert( result.begin(), '0' + ( n & 1 ) ); + count++; + } while ( n >>= 1 ); + + for ( uint8_t i = 0; i < max_var - count; i++ ) + { + result.insert( result.begin(), '0' ); + } + + return result; + } + + /*! \brief Prints white space. + \param val number of white space to print + \param os output stream + */ + void print_space( uint8_t val, std::ostream& os ) + { + for ( uint8_t i = 0; i < val; i++ ) + { + os << " "; + } + } + + /*! \brief Computes sequence of values with unitary Hamming distance for a + certain number of values + + For example, for 2 as the number of variables, the sequence would be = {0, + 1, 3, 2} (= {00, 01, 11, 10}) + + \param num_var number of variables + */ + std::vector compute_seq_1ham_dist( uint8_t num_var ) + { + if ( num_var == 1 ) + { + return { 0, 1 }; + } + else + { + std::vector res = compute_seq_1ham_dist( num_var - 1 ); + std::vector res_rev( res.rbegin(), res.rend() ); + for ( auto& i : res_rev ) + { + i += ( 1 << ( num_var - 1 ) ); + } + res.insert( res.end(), res_rev.begin(), res_rev.end() ); + return res; + } + } +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/kitty.hpp b/third-party/mockturtle/lib/kitty/kitty/kitty.hpp new file mode 100644 index 00000000000..d3dc329049a --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/kitty.hpp @@ -0,0 +1,70 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file kitty.hpp + \brief Main header for kitty + + \author Mathias Soeken +*/ + +#pragma once + +#include "static_truth_table.hpp" +#include "dynamic_truth_table.hpp" +#include "partial_truth_table.hpp" + +#include "affine.hpp" +#include "algorithm.hpp" +#include "bit_operations.hpp" +#include "cnf.hpp" +#include "constructors.hpp" +#include "cube.hpp" +#include "decomposition.hpp" +#include "enumeration.hpp" +//#include "esop.hpp" // deprecated! +#include "hash.hpp" +#include "implicant.hpp" +#include "isop.hpp" +#include "karnaugh_map.hpp" +#include "npn.hpp" +#include "operations.hpp" +#include "operators.hpp" +#include "permutation.hpp" +#include "print.hpp" +#include "properties.hpp" +#include "spectral.hpp" +#include "spp.hpp" +#include "traits.hpp" + +/* + /\___/\ + ( o o ) + / * \ + \__\_/__/ + / \ + / ___ \ + \/___\/ +*/ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/npn.hpp b/third-party/mockturtle/lib/kitty/kitty/npn.hpp new file mode 100644 index 00000000000..3b18300b78a --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/npn.hpp @@ -0,0 +1,1327 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file npn.hpp + \brief Implements NPN canonization algorithms + + \author Alessandro Tempia Calvino + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "detail/constants.hpp" +#include "operators.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \cond PRIVATE */ + +namespace detail +{ +template +void exact_npn_canonization_null_callback( const TT& tt ) +{ + (void)tt; +} +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Exact P canonization + + Given a truth table, this function finds the lexicographically smallest truth + table in its P class, called P representative. Two functions are in the + same P class, if one can obtain one from the other by input permutation. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole P class. + + The function returns a NPN configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the P representative + - input negations and output negation, which is 0 in this case + - input permutation to apply + + \param tt The truth table + \param fn Callback for each visited truth table in the class (default does nothing) + \return NPN configuration +*/ +template )> +std::tuple> exact_p_canonization( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, 0u, std::vector{} ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, 0u, std::vector{ 0 } ); + } + + assert( num_vars >= 2 && num_vars <= 7 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + + int best_swap = -1; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_swap = static_cast( i ); + tmin = t1; + } + } + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto i = 0; i <= best_swap; ++i ) + { + const auto pos = swaps[i]; + std::swap( perm[pos], perm[pos + 1] ); + } + + return std::make_tuple( tmin, 0u, perm ); +} + +/*! \brief Exact NPN canonization + + Given a truth table, this function finds the lexicographically smallest truth + table in its NPN class, called NPN representative. Two functions are in the + same NPN class, if one can obtain one from the other by input negation, input + permutation, and output negation. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole NPN class. + + The function returns a NPN configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the NPN representative + - input negations and output negation, output negation is stored as bit *n*, + where *n* is the number of variables in `tt` + - input permutation to apply + + \param tt The truth table (with at most 6 variables) + \param fn Callback for each visited truth table in the class (default does nothing) + \return NPN configuration +*/ +template )> +std::tuple> exact_npn_canonization( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + const auto bit = get_bit( tt, 0 ); + return std::make_tuple( unary_not_if( tt, bit ), static_cast( bit ), std::vector{} ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + const auto bit1 = get_bit( tt, 1 ); + return std::make_tuple( unary_not_if( tt, bit1 ), static_cast( bit1 << 1 ), std::vector{ 0 } ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt, t2 = ~tt; + auto tmin = std::min( t1, t2 ); + auto invo = tmin == t2; + + fn( t1 ); + fn( t2 ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + int best_swap = -1; + int best_flip = -1; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + swap_adjacent_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + best_swap = static_cast( i ); + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + swap_adjacent_inplace( t1, 0 ); + flip_inplace( t1, pos ); + swap_adjacent_inplace( t2, 0 ); + flip_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + best_swap = -1; + best_flip = static_cast( j ); + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + swap_adjacent_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + best_swap = static_cast( i ); + best_flip = static_cast( j ); + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + } + } + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto i = 0; i <= best_swap; ++i ) + { + const auto pos = swaps[i]; + std::swap( perm[pos], perm[pos + 1] ); + } + + uint32_t phase = uint32_t( invo ) << num_vars; + for ( auto i = 0; i <= best_flip; ++i ) + { + phase ^= 1 << flips[i]; + } + + return std::make_tuple( tmin, phase, perm ); +} + +/*! \brief Exact N canonization + + Given a truth table, this function finds the lexicographically smallest truth + table in its N class, called N representative. Two functions are in the + same N class, if one can obtain one from the other by input negations. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole N class. + + The function returns a N configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the N representative + - input negations that lead to the representative + + \param tt The truth table + \param fn Callback for each visited truth table in the class (default does nothing) + \return N configurations +*/ +template )> +std::tuple exact_n_canonization( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, 0 ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, 0 ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& flips = detail::flips[num_vars - 2u]; + int best_flip = -1; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_flip = static_cast( j ); + tmin = t1; + } + } + + uint32_t phase = 0; + for ( auto i = 0; i <= best_flip; ++i ) + { + phase ^= 1 << flips[i]; + } + + return std::make_tuple( tmin, phase ); +} + +/*! \brief Exact N canonization given a support size + + Given a truth table, this function finds the lexicographically smallest truth + table in its N class, called N representative. Two functions are in the + same N class, if one can obtain one from the other by input negations. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole N class. + + The function returns a N configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the N representative + - input negations that lead to the representative + + \param tt The truth table + \param support_size Support size used for the canonization + \param fn Callback for each visited truth table in the class (default does nothing) + \return N configurations +*/ +template )> +std::tuple exact_n_canonization_support( const TT& tt, uint32_t support_size, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + assert( support_size <= tt.num_vars() ); + + const auto num_vars = support_size; + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, 0 ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, 0 ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& flips = detail::flips[num_vars - 2u]; + int best_flip = -1; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_flip = static_cast( j ); + tmin = t1; + } + } + + uint32_t phase = 0; + for ( auto i = 0; i <= best_flip; ++i ) + { + phase ^= 1 << flips[i]; + } + + return std::make_tuple( tmin, phase ); +} + +/*! \brief Flip-swap NPN heuristic + + This algorithm will iteratively try to reduce the numeric value of the truth + table by first inverting each input, then inverting the output, and then + swapping each pair of inputs. Every improvement is accepted, the algorithm + stops, if no more improvement can be achieved. + + The function returns a NPN configuration which contains the + necessary transformations to obtain the representative. It is a + tuple of + + - the NPN representative + - input negations and output negation, output negation is stored as + bit *n*, where *n* is the number of variables in `tt` + - input permutation to apply + + \param tt Truth table + \return NPN configuration +*/ +template +std::tuple> flip_swap_npn_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* initialize permutation and phase */ + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase{ 0u }; + + auto npn = tt; + auto improvement = true; + + while ( improvement ) + { + improvement = false; + + /* input inversion */ + for ( auto i = 0u; i < num_vars; ++i ) + { + const auto flipped = flip( npn, i ); + if ( flipped < npn ) + { + npn = flipped; + phase ^= 1 << perm[i]; + improvement = true; + } + } + + /* output inversion */ + const auto flipped = ~npn; + if ( flipped < npn ) + { + npn = flipped; + phase ^= 1 << num_vars; + improvement = true; + } + + /* permute inputs */ + for ( auto d = 1u; d < num_vars - 1; ++d ) + { + for ( auto i = 0u; i < num_vars - d; ++i ) + { + auto j = i + d; + + const auto permuted = swap( npn, i, j ); + if ( permuted < npn ) + { + npn = permuted; + std::swap( perm[i], perm[j] ); + + improvement = true; + } + } + } + } + + return std::make_tuple( npn, phase, perm ); +} + +/*! \cond PRIVATE */ +namespace detail +{ + +template +void sifting_npn_canonization_loop( TT& npn, uint32_t& phase, std::vector& perm ) +{ + auto improvement = true; + auto forward = true; + + const auto n = npn.num_vars(); + + while ( improvement ) + { + improvement = false; + + for ( int i = forward ? 0 : n - 2; forward ? i < static_cast( n - 1 ) : i >= 0; forward ? ++i : --i ) + { + auto local_improvement = false; + for ( auto k = 1u; k < 8u; ++k ) + { + if ( k % 4u == 0u ) + { + const auto next_t = swap( npn, i, i + 1 ); + if ( next_t < npn ) + { + npn = next_t; + std::swap( perm[i], perm[i + 1] ); + local_improvement = true; + } + } + else if ( k % 2u == 0u ) + { + const auto next_t = flip( npn, i + 1 ); + if ( next_t < npn ) + { + npn = next_t; + phase ^= 1 << perm[i + 1]; + local_improvement = true; + } + } + else + { + const auto next_t = flip( npn, i ); + if ( next_t < npn ) + { + npn = next_t; + phase ^= 1 << perm[i]; + local_improvement = true; + } + } + } + + if ( local_improvement ) + { + improvement = true; + } + } + + forward = !forward; + } +} +template +void sifting_p_canonization_loop( TT& p, uint32_t& phase, std::vector& perm ) +{ + (void)phase; + auto improvement = true; + auto forward = true; + + const auto n = p.num_vars(); + + while ( improvement ) + { + improvement = false; + + for ( int i = forward ? 0 : n - 2; forward ? i < static_cast( n - 1 ) : i >= 0; forward ? ++i : --i ) + { + auto local_improvement = false; + + const auto next_t = swap( p, i, i + 1 ); + if ( next_t < p ) + { + p = next_t; + std::swap( perm[i], perm[i + 1] ); + local_improvement = true; + } + + if ( local_improvement ) + { + improvement = true; + } + } + forward = !forward; + } +} +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Sifting NPN heuristic + + The algorithm will always consider two adjacent variables and try all possible + transformations on these two. It will try once in forward direction and once + in backward direction. It will try for the regular function and inverted + function. + + The function returns a NPN configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the NPN representative + - input negations and output negation, output negation is stored as bit *n*, + where *n* is the number of variables in `tt` + - input permutation to apply + + \param tt Truth table + \return NPN configuration +*/ +template +std::tuple> sifting_npn_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* initialize permutation and phase */ + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + uint32_t phase{ 0u }; + + if ( num_vars < 2 ) + { + return std::make_tuple( tt, phase, perm ); + } + + auto npn = tt; + + detail::sifting_npn_canonization_loop( npn, phase, perm ); + + const auto best_perm = perm; + const auto best_phase = phase; + const auto best_npn = npn; + + npn = ~tt; + phase = 1 << num_vars; + std::iota( perm.begin(), perm.end(), 0u ); + + detail::sifting_npn_canonization_loop( npn, phase, perm ); + + if ( best_npn < npn ) + { + perm = best_perm; + phase = best_phase; + npn = best_npn; + } + + return std::make_tuple( npn, phase, perm ); +} + +/*! \brief Sifting P heuristic + + The algorithm will always consider two adjacent variables and try all possible + transformations on these two. It will try once in forward direction and once + in backward direction. It will try for the regular function. + + The function returns a P configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the P representative + - input negations and output negation, which is 0 in this case + - input permutation to apply + + \param tt Truth table + \return NPN configuration +*/ +template +std::tuple> sifting_p_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* initialize permutation and phase */ + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + uint32_t phase{ 0u }; + + if ( num_vars < 2u ) + { + return std::make_tuple( tt, phase, perm ); + } + + auto npn = tt; + + detail::sifting_p_canonization_loop( npn, phase, perm ); + + return std::make_tuple( npn, phase, perm ); +} + +/*! \brief Exact NPN enumeration + + Given a truth table, this function enumerates all the functions in its + NPN class. Two functions are in the same NP class, if one can be obtained + from the other by input negation, input permutation, and output negation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - NPN-enumerated truth table + - input and output negations + - input permutation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the NP class +*/ +template +void exact_npn_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, 0u, std::vector{} ); + fn( ~tt, 1u, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, 0u, std::vector{ 0 } ); + fn( ~tt, 2u, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase = 0; + + fn( t1, phase, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + fn( ~t1, phase | ( 1u << num_vars ), perm ); + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + swap_adjacent_inplace( t1, 0 ); + flip_inplace( t1, pos ); + + std::swap( perm[0], perm[1] ); + phase ^= 1 << perm[pos]; + + fn( t1, phase, perm ); + fn( ~t1, phase | ( 1u << num_vars ), perm ); + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + fn( ~t1, phase | ( 1u << num_vars ), perm ); + } + } +} + +/*! \brief Exact NP enumeration + + Given a truth table, this function enumerates all the functions in its + NP class. Two functions are in the same NP class, if one can be obtained + from the other by input negation and input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - NP-enumerated truth table + - input negations + - input permutation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the NP class +*/ +template +void exact_np_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, 0u, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, 0u, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase = 0; + + fn( t1, phase, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + swap_adjacent_inplace( t1, 0 ); + flip_inplace( t1, pos ); + + std::swap( perm[0], perm[1] ); + phase ^= 1 << perm[pos]; + + fn( t1, phase, perm ); + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + } +} + +/*! \brief Exact multi NP enumeration + + Given multiple truth tables, this function enumerates all the functions in their + NP class. Two functions are in the same NP class, if one can be obtained + from the other by input negation and input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - NP-enumerated truth tables + - input negations + - input permutation to apply + + \param tts Truth tables + \param fn Callback for each enumerated truth table in the NP class +*/ +template +void exact_multi_np_enumeration( const std::vector& tts, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + assert( tts.size() > 0 ); + + const auto num_vars = tts[0].num_vars(); + + for ( auto i = 0; i < tts.size(); ++i ) + assert( tts[i].num_vars() == num_vars ); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tts, 0u, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tts, 0u, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tts; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase = 0; + + fn( t1, phase, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + + for ( auto& tt : t1 ) + swap_adjacent_inplace( tt, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + + for ( auto& tt : t1 ) + { + swap_adjacent_inplace( tt, 0 ); + flip_inplace( tt, pos ); + } + + std::swap( perm[0], perm[1] ); + phase ^= 1 << perm[pos]; + + fn( t1, phase, perm ); + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + + for ( auto& tt : t1 ) + swap_adjacent_inplace( tt, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + } +} + +/*! \brief Exact P enumeration + + Given a truth table, this function enumerates all the functions in its + P class. Two functions are in the same P class, if one can be obtained + from the other by input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - P-enumerated truth table + - input permutation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the P class +*/ +template +void exact_p_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + fn( t1, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, perm ); + } +} + +/*! \brief Exact multi P enumeration + + Given multiple truth tables, this function enumerates all the functions in their + P class. Two functions are in the same P class, if one can be obtained + from the other by input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - P-enumerated truth tables + - input permutation to apply + + \param tt Truth tables + \param fn Callback for each enumerated truth table in the P class +*/ +template +void exact_multi_p_enumeration( const std::vector& tts, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + assert( tts.size() > 0 ); + + const auto num_vars = tts[0].num_vars(); + + for ( auto i = 0; i < tts.size(); ++i ) + assert( tts[i].num_vars == num_vars ); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tts, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tts, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tts; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + fn( t1, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + + for ( auto& tt : t1 ) + swap_adjacent_inplace( tt, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, perm ); + } +} + +/*! \brief Exact N enumeration + + Given a truth table, this function enumerates all the functions in its + N class. Two functions are in the same N class, if one can be obtained + from the other by input negation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - N-enumerated truth table + - input negation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the N class +*/ +template +void exact_n_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, 0 ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, 0 ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + fn( t1, 0 ); + + const auto& flips = detail::flips[num_vars - 2u]; + uint32_t phase = 0; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + phase ^= 1 << pos; + + fn( t1, phase ); + } +} + +/*! \brief Exact N canonization complete + + Given a truth table, this function finds the lexicographically smallest truth + table in its N class, called N representative. Two functions are in the + same N class, if one can obtain one from the other by input negations. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole N class. + + The function returns all the N configurations which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the N representative + - a vector of all input negations that lead to the representative + + \param tt The truth table + \param fn Callback for each visited truth table in the class (default does nothing) + \return N configurations +*/ +template )> +std::tuple> exact_n_canonization_complete( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, std::vector{ 0 } ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, std::vector{ 0 } ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& flips = detail::flips[num_vars - 2u]; + + std::vector best_flip{ -1 }; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_flip.erase( best_flip.begin() + 1, best_flip.end() ); + best_flip[0] = static_cast( j ); + tmin = t1; + } + else if ( t1 == tmin ) + { + best_flip.push_back( static_cast( j ) ); + } + } + + std::vector phases( best_flip.size() ); + uint32_t phase = 0; + int cnt = 0; + for ( auto i = 0u; i < best_flip.size(); ++i ) + { + auto flip = best_flip[i]; + for ( ; cnt <= flip; ++cnt ) + { + phase ^= 1 << flips[cnt]; + } + phases[i] = phase; + } + + return std::make_tuple( tmin, phases ); +} + +/*! \brief Obtain truth table from NPN configuration + + Given an NPN configuration, which contains a representative + function, input/output negations, and input permutations this + function computes the original truth table. + + \param config NPN configuration +*/ +template +TT create_from_npn_config( const std::tuple>& config ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto& from = std::get<0>( config ); + const auto& phase = std::get<1>( config ); + auto perm = std::get<2>( config ); + const auto num_vars = from.num_vars(); + + /* is output complemented? */ + auto res = ( ( phase >> num_vars ) & 1 ) ? ~from : from; + + /* input permutations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( perm[i] == i ) + { + continue; + } + + int k = i; + while ( perm[k] != i ) + { + ++k; + } + + swap_inplace( res, i, k ); + std::swap( perm[i], perm[k] ); + } + + /* input complementations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( ( phase >> i ) & 1 ) + { + flip_inplace( res, i ); + } + } + + return res; +} + +/*! \brief Obtain truth table applying a NPN configuration + + Given an NPN configuration composed of input/output negations, + and input permutations this function applies the transformation + to the input truth table. This function can be used to obtain + the NPN representative function given the NPN transformation. + This function is the inverse of `create_from_npn_config`. + + \param from truth table + \param phase input/output negations to apply + \param perm input permutations to apply +*/ +template +TT apply_npn_transformation( TT const& from, uint32_t phase, std::vector const& perm ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + /* transpose the permutation vector */ + std::vector perm_transposed( perm.size() ); + for ( auto i = 0; i < perm.size(); ++i ) + perm_transposed[perm[i]] = i; + + const auto num_vars = from.num_vars(); + + /* is output complemented? */ + auto res = ( ( phase >> num_vars ) & 1 ) ? ~from : from; + + /* input complementations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( ( phase >> i ) & 1 ) + { + flip_inplace( res, i ); + } + } + + /* input permutations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( perm_transposed[i] == i ) + { + continue; + } + + int k = i; + while ( perm_transposed[k] != i ) + { + ++k; + } + + swap_inplace( res, i, k ); + std::swap( perm_transposed[i], perm_transposed[k] ); + } + + return res; +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/operations.hpp b/third-party/mockturtle/lib/kitty/kitty/operations.hpp new file mode 100644 index 00000000000..db70571844e --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/operations.hpp @@ -0,0 +1,2489 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file operations.hpp + \brief Implements several operations on truth tables + + \author Mathias Soeken + \author Mahyar Emami (mahyar.emami@epfl.ch) +*/ + +#pragma once + +#include "algorithm.hpp" +#include "detail/shift.hpp" +#include "dynamic_truth_table.hpp" +#include "partial_truth_table.hpp" +#include "quaternary_truth_table.hpp" +#include "static_truth_table.hpp" +#include "ternary_truth_table.hpp" +#include "traits.hpp" + +#include +#include +#include +#include +#include + +namespace kitty +{ + +/* forward declarations */ +/*! \cond PRIVATE */ +template +TT create( unsigned num_vars ); + +template<> +dynamic_truth_table create( unsigned num_vars ); +/*! \endcond */ + +/*! \brief Inverts all bits in a truth table */ +template +inline TT unary_not( const TT& tt ) +{ + return unary_operation( tt, []( auto a ) { return ~a; } ); +} + +template +inline ternary_truth_table unary_not( const ternary_truth_table& tt ) +{ + return ternary_truth_table( ~( tt._bits ) & tt._care, tt._care ); +} + +template +inline quaternary_truth_table unary_not( const quaternary_truth_table& tt ) +{ + return quaternary_truth_table( tt._offset, tt._onset ); +} + +/*! Inverts all bits in a truth table, based on a condition */ +template +inline TT unary_not_if( const TT& tt, bool cond ) +{ +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + const auto mask = -static_cast( cond ); +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + return unary_operation( tt, [mask]( auto a ) { return a ^ mask; } ); +} + +/*! \brief Bitwise AND of two truth tables */ +template + +inline TT binary_and( const TT& first, const TT& second ) +{ + return binary_operation( first, second, std::bit_and<>() ); +} + +/*! \brief Bitwise AND of two ternary truth tables + * + * Computation rules: + * - `0 & 0 = 0 & 1 = 1 & 0 = 0` + * - `1 & 1 = 1` + * - `0 & - = - & 0 = 0` + * - `1 & x = x & 1 = x & - = - & x = x & x = x` + */ +template +inline ternary_truth_table binary_and( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + auto const op_bits = []( auto b1, auto c1, auto b2, auto c2 ) { + (void)c1; + (void)c2; + return b1 & b2; + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + return ( c1 & c2 ) | ( ~b1 & c1 ) | ( ~b2 & c2 ); + }; + + return ternary_truth_table( quaternary_operation( first._bits, first._care, second._bits, second._care, op_bits ), + quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ) ); +} + +/*! \brief Bitwise OR of two quaternary truth tables + * + * Computation rules: + * - `0 & 0 = 0 & 1 = 1 & 0 = 0` + * - `1 & 1 = 1` + * - `0 & - = - & 0 = x & 0 = 0 & x = 0` + * - `1 & - = - & 1 = - & - = -` + * - `1 & x = x & 1 = x & - = - & x = x & x = x` + */ +template +inline quaternary_truth_table binary_and( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + auto const op_on = []( auto a, auto b ) { + return a & b; + }; + auto const op_off = []( auto a, auto b, auto c, auto d ) { + return ( ~a & b ) | ( b & c ) | ( a & d ) | ( ~b & ~c & d ); + }; + + return quaternary_truth_table( binary_operation( first._onset, second._onset, op_on ), + quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_off ) ); +} + +/*! \brief Bitwise OR of two truth tables */ +template +inline TT binary_or( const TT& first, const TT& second ) +{ + return binary_operation( first, second, std::bit_or<>() ); +} + +/*! \brief Bitwise OR of two ternary truth tables + * + * Computation rules: + * - `0 | 0 = 0` + * - `0 | 1 = 1 | 0 = 1 | 1 = 1` + * - `1 | - = - | 1 = 1` + * - `0 | - = - | 0 = - | - = -` + */ +template +inline ternary_truth_table binary_or( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + auto const op_bits = []( auto b1, auto c1, auto b2, auto c2 ) { + (void)c1; + (void)c2; + return b1 | b2; + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + return b1 | b2 | ( c1 & c2 ); + }; + + return ternary_truth_table( quaternary_operation( first._bits, first._care, second._bits, second._care, op_bits ), + quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ) ); +} + +/*! \brief Bitwise OR of two quaternary truth tables + * + * Computation rules: + * - `0 | 0 = 0` + * - `0 | 1 = 1 | 0 = 1 | 1 = 1` + * - `1 | - = - | 1 = x | 1 = 1 | x = 1` + * - `0 | - = - | 0 = - | - = -` + * - `0 | x = x | 0 = x | - = - | x = x | x = x` + */ +template +inline quaternary_truth_table binary_or( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + auto const op_on = []( auto a, auto b, auto c, auto d ) { + return ( a & ~b ) | ( b & c ) | ( c & ~d ) | ( a & b & d ); + }; + auto const op_off = []( auto a, auto b ) { + return a & b; + }; + + return quaternary_truth_table( quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_on ), + binary_operation( first._offset, second._offset, op_off ) ); +} + +/*! \brief Bitwise XOR of two truth tables */ +template +inline TT binary_xor( const TT& first, const TT& second ) +{ + return binary_operation( first, second, std::bit_xor<>() ); +} + +/*! \brief Bitwise XOR of two ternary truth tables + * + * Computation rules: + * - `0 ^ 0 = 1 ^ 1 = 0` + * - `0 ^ 1 = 1 ^ 0 = 1` + * - `0 ^ - = - ^ 0 = 1 ^ - = - ^ 1 = - ^ - = -` + */ +template +inline ternary_truth_table binary_xor( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + auto const op_bits = []( auto b1, auto c1, auto b2, auto c2 ) { + return ( b1 ^ b2 ) & ( c1 & c2 ); + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + (void)b1; + (void)b2; + return c1 & c2; + }; + + return ternary_truth_table( quaternary_operation( first._bits, first._care, second._bits, second._care, op_bits ), + quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ) ); +} + +/*! \brief Bitwise XOR of two ternary truth tables + * + * Computation rules: + * - `0 ^ 0 = 1 ^ 1 = 0` + * - `0 ^ 1 = 1 ^ 0 = 1` + * - `0 ^ - = - ^ 0 = 1 ^ - = - ^ 1 = - ^ - = -` + * - `0 ^ x = x ^ 0 = 1 ^ x = x ^ 1 = - ^ x = x ^ - = x ^ x = x` + */ +template +inline quaternary_truth_table binary_xor( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + auto const op_on = []( auto a, auto b, auto c, auto d ) { + return ( b & c ) | ( a & d ); + }; + auto const op_off = []( auto a, auto b, auto c, auto d ) { + return ( a & c ) | ( b & d ); + }; + + return quaternary_truth_table( quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_on ), + quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_off ) ); +} + +/*! \brief Ternary majority of three truth tables */ +template +inline TT ternary_majority( const TT& first, const TT& second, const TT& third ) +{ + return ternary_operation( first, second, third, []( auto a, auto b, auto c ) { return ( a & ( b ^ c ) ) ^ ( b & c ); } ); +} + +/*! \brief Ternary majority of three truth tables */ +template +inline ternary_truth_table ternary_majority( const ternary_truth_table& first, const ternary_truth_table& second, const ternary_truth_table& third ) +{ + auto const op_bits = []( auto a, auto b, auto c ) { + return ( a & ( b ^ c ) ) ^ ( b & c ); + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + return ( b1 & c1 & b2 & c2 ) | ( ( ~b1 ) & c1 & ( ~b2 ) & c2 ); + }; + TT care12 = quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ); + TT care23 = quaternary_operation( second._bits, second._care, third._bits, third._care, op_care ); + TT care13 = quaternary_operation( first._bits, first._care, third._bits, third._care, op_care ); + auto const ternary_or = []( auto a, auto b, auto c ) { + return a | b | c; + }; + return ternary_truth_table( ternary_operation( first._bits, second._bits, third._bits, op_bits ), + ternary_operation( care12, care23, care13, ternary_or ) ); +} + +/*! \brief Ternary majority of three quaternary truth tables + * + * Computation rules: + * When there are two 0, 1, or x the result is 0, 1, x + * - ternary_majority(0, 1, -) = ternary_majority(0, -, -) = ternary_majority(1, -, -) = - + * - ternary_majority(0, 1, x) = ternary_majority(0, x, -) = ternary_majority(1, x, -) = ternary_majority(x, -, -) = x + * - ternary_majority is commutative + */ +template +inline quaternary_truth_table ternary_majority( quaternary_truth_table& first, const quaternary_truth_table& second, const quaternary_truth_table& third ) +{ + return ( ( first & second ) | ( second & third ) ) | ( first & third ); +} + +/*! \brief Performs ternary if-then-else of three truth tables + + \param first Truth table for condition + \param second Truth table for then-case + \param third Truth table for else-case + */ +template +inline TT ternary_ite( const TT& first, const TT& second, const TT& third ) +{ + return ternary_operation( first, second, third, []( auto a, auto b, auto c ) { return ( a & b ) ^ ( ~a & c ); } ); +} + +template +inline ternary_truth_table ternary_ite( const ternary_truth_table& first, const ternary_truth_table& second, const ternary_truth_table& third ) +{ + auto const op_bits = []( auto a, auto b, auto c ) { + return ( a & b ) ^ ( ~a & c ); + }; + auto const op_care1 = []( auto b2, auto c2, auto b3, auto c3 ) { + return ( ( ~b2 ) & c2 & b3 ) | ( b2 & c2 & ( ~b3 ) ) | ( c2 & ( ~c3 ) ) | ( c3 & ( ~c2 ) ) | ( ( ~c3 ) & ( ~c2 ) ); + }; + auto const op_care2 = []( auto b1, auto c1, auto c2, auto c3 ) { + return c1 & ( ( ( ~c2 ) & b1 ) | ( ( ~b1 ) & ( ~c3 ) ) | ( ( ~c3 ) & ( ~c2 ) ) ); + }; + TT care1 = quaternary_operation( second._bits, second._care, third._bits, third._care, op_care1 ); + TT care2 = quaternary_operation( first._bits, first._care, second._care, third._care, op_care2 ); + auto const final_op = []( auto c1, auto res1, auto res2 ) { + return ~( ( ( ~c1 ) & res1 ) | res2 ); + }; + return ternary_truth_table( ternary_operation( first._bits, second._bits, third._bits, op_bits ), + ternary_operation( first._care, care1, care2, final_op ) ); +} + +/*! \brief Performs ternary if-then-else of three quaternary truth tables + + In general, each time the result would depend from a x, x is the result. + For example, ternary_ite( -, 0, x ) = x + However, ternary_ite( x, 0, 0 ) = ternary_ite( -, 0, 0 ) = 0 + ternary_ite( x, 1, 1 ) = ternary_ite( -, 1, 1 ) = 1 + ternary_ite( x, -, - ) = ternary_ite( -, -, - ) = - + + \param first Truth table for condition + \param second Truth table for then-case + \param third Truth table for else-case + */ +template +inline quaternary_truth_table ternary_ite( const quaternary_truth_table& first, const quaternary_truth_table& second, const quaternary_truth_table& third ) +{ + auto const op_1 = []( auto a, auto b, auto c, auto d ) { + return ( a & b ) & ( ( c & d ) | ( ~c & ~d ) ); + }; + auto const op_2 = []( auto a, auto b, auto c ) { + return ( a & b & c ); + }; + auto const op_3 = []( auto a, auto b, auto c ) { + return ( a & b & ~c ); + }; + auto const or_4 = []( auto a, auto b, auto c, auto d ) { + return ( a | b | c | d ); + }; + auto const or_3 = []( auto a, auto b, auto c ) { + return ( a | b | c ); + }; + TT on1 = ternary_operation( first._offset, third._onset, first._onset, op_3 ); + TT on2 = ternary_operation( first._onset, second._onset, first._offset, op_3 ); + TT on3 = ternary_operation( first._offset, second._onset, third._onset, op_2 ); + TT on4 = ternary_operation( first._offset, second._offset, third._onset, op_2 ); + TT on5 = ternary_operation( first._onset, second._onset, third._offset, op_2 ); + TT on6 = quaternary_operation( second._onset, third._onset, second._offset, third._offset, op_1 ); + TT on7 = quaternary_operation( on1, on2, on3, on4, or_4 ); + TT res_onset = ternary_operation( on7, on5, on6, or_3 ); + + TT off1 = ternary_operation( third._offset, first._offset, first._onset, op_3 ); + TT off2 = ternary_operation( first._onset, second._offset, first._offset, op_3 ); + TT off3 = ternary_operation( first._offset, second._offset, third._offset, op_2 ); + TT off4 = ternary_operation( first._offset, second._onset, third._offset, op_2 ); + TT off5 = ternary_operation( first._onset, second._offset, third._onset, op_2 ); + TT off6 = quaternary_operation( second._offset, third._offset, second._onset, third._onset, op_1 ); + TT off7 = quaternary_operation( off1, off2, off3, off4, or_4 ); + TT res_offset = ternary_operation( off7, off5, off6, or_3 ); + + return quaternary_truth_table( res_onset, res_offset ); +} + +/*! \brief Muxes two truth tables based on a variable + + \param var_index Variable index + \param then_ Truth table for the then-case + \param else_ Truth table for the else-case +*/ +template +inline TT mux_var( uint8_t var_index, const TT& then_, const TT& else_ ) +{ + if ( var_index < 6u ) + { + return binary_operation( then_, else_, + [&]( auto a, auto b ) { return ( a & detail::projections[var_index] ) | + ( b & detail::projections_neg[var_index] ); } ); + } + else + { + const auto step = 1u << ( var_index - 6u ); + auto j = 0u; + auto res = then_.construct(); + + std::transform( then_.begin(), then_.end(), else_.begin(), res.begin(), + [&]( auto a, auto b ) { + return ( j++ % ( 2 * step ) ) < step ? b : a; + } ); + + return res; + } +} + +/*! \brief Muxes two ternary truth tables based on a variable + + * Values that are not taken by the projections become 0s. + + \param var_index Variable index + \param then_ Truth table for the then-case + \param else_ Truth table for the else-case +*/ +template +inline ternary_truth_table mux_var( uint8_t var_index, const ternary_truth_table& then_, const ternary_truth_table& else_ ) +{ + auto const projection = [&var_index]( auto a ) { + return ( a & detail::projections[var_index] ); + }; + auto const projection_care = [&var_index]( auto a ) { + return ( ( a & detail::projections[var_index] ) | detail::projections_neg[var_index] ); + }; + auto const projection_neg = [&var_index]( auto a ) { + return ( a & detail::projections_neg[var_index] ); + }; + auto const projection_neg_care = [&var_index]( auto a ) { + return ( ( a & detail::projections_neg[var_index] ) | detail::projections[var_index] ); + }; + ternary_truth_table then_new( unary_operation( then_._bits, projection ), unary_operation( then_._care, projection_care ) ); + ternary_truth_table else_new( unary_operation( else_._bits, projection_neg ), unary_operation( else_._care, projection_neg_care ) ); + return then_new | else_new; +} + +/*! \brief Muxes two ternary truth tables based on a variable + + * Values that are not taken by the projections become 0s. + + \param var_index Variable index + \param then_ Truth table for the then-case + \param else_ Truth table for the else-case +*/ +template +inline quaternary_truth_table mux_var( uint8_t var_index, const quaternary_truth_table& then_, const quaternary_truth_table& else_ ) +{ + auto const projection = [&var_index]( auto a ) { + return ( a & detail::projections[var_index] ); + }; + auto const projection_off = [&var_index]( auto a ) { + return ( ( a & detail::projections[var_index] ) | detail::projections_neg[var_index] ); + }; + auto const projection_neg = [&var_index]( auto a ) { + return ( a & detail::projections_neg[var_index] ); + }; + auto const projection_neg_off = [&var_index]( auto a ) { + return ( ( a & detail::projections_neg[var_index] ) | detail::projections[var_index] ); + }; + quaternary_truth_table then_new( unary_operation( then_._onset, projection ), unary_operation( then_._offset, projection_off ) ); + quaternary_truth_table else_new( unary_operation( else_._onset, projection_neg ), unary_operation( else_._offset, projection_neg_off ) ); + return then_new | else_new; +} + +/*! \brief Checks whether two truth tables are equal + + \param first First truth table + \param second Second truth table +*/ +template +inline bool equal( const TT& first, const TT& second ) +{ + if ( first.num_vars() != second.num_vars() ) + { + return false; + } + + return binary_predicate( first, second, std::equal_to<>() ); +} + +/*! \cond PRIVATE */ +inline bool equal( const partial_truth_table& first, const partial_truth_table& second ) +{ + if ( first.num_bits() != second.num_bits() ) + { + return false; + } + + return binary_predicate( first, second, std::equal_to<>() ); +} /*! \endcond */ + +/*! \brief Checks whether two incompletely specified truth tables are equal + + The template parameter UseDCs allows us to decide if to check for possible assignment + of the don't cares to achieve equality: + - UseDCs = false : Checks if both the careset and the onset coincide + - UseDCs = true : Checks if there is an assignment of the don't cares making the functions equal. + + \param first First truth table + \param second Second truth table +*/ +template +inline bool equal( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + if constexpr ( UseDCs ) + { + const auto care_mask = first._care & second._care; + return equal( first._bits & care_mask, second._bits & care_mask ); + } + else + { + return equal( first._bits, second._bits ) && equal( first._care, second._care ); + } +} + +template +inline bool equal( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return equal( first._onset, second._onset ) && equal( first._offset, second._offset ); +} + +/*! \brief Checks if first truth table implies a second truth table + + \param first First truth table + \param second Second truth table +*/ +template +inline bool implies( const TT& first, const TT& second ) +{ + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( a & ~b ) == 0u; } ); +} + +/*! \brief Checks if first ternary truth table implies a second ternary truth table + + \param first First truth table + \param second Second truth table +*/ +template +inline bool implies( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return is_const0( first & ~second ); +} + +/*! \brief Checks if first quaternary truth table implies a second quaternary truth table + + \param first First truth table + \param second Second truth table +*/ +template +inline bool implies( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return is_const0( first & ~second ); +} + +/*! \brief Checks whether a truth table is lexicographically smaller than another + + Comparison is initiated from most-significant bit. + \param first First truth table + \param second Second truth table +*/ +template +inline bool less_than( const TT& first, const TT& second ) +{ + return std::lexicographical_compare( first._bits.rbegin(), first._bits.rend(), + second._bits.rbegin(), second._bits.rend() ); +} + +/*! \cond PRIVATE */ +template +inline bool less_than( const static_truth_table& first, const static_truth_table& second ) +{ + return first._bits < second._bits; +} +/*! \endcond */ + +/*! \brief Checks whether a ternary truth table is lexicographically smaller than another + + Comparison is initiated from most-significant bit and don't cares are considered as 0. + \param first First truth table + \param second Second truth table +*/ +template +inline bool less_than( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return first._bits < second._bits; +} + +/*! \brief Checks whether a quaternary truth table is lexicographically smaller than another + + Comparison is initiated from most-significant bit + Don't cares are considered as 0 and don't knows are considered as 1. + \param first First truth table + \param second Second truth table +*/ +template +inline bool less_than( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return first._onset < second._onset; +} + +/*! \brief Checks whether truth table is contant 0 + + \param tt Truth table +*/ +template +inline bool is_const0( const TT& tt ) +{ + return std::all_of( std::begin( tt._bits ), std::end( tt._bits ), []( uint64_t word ) { return word == 0; } ); +} + +/*! \cond PRIVATE */ +template +inline bool is_const0( const static_truth_table& tt ) +{ + return tt._bits == 0; +} + +/*! \brief Checks whether a ternary truth table is contant 0 + + \param tt Truth table +*/ +template +inline bool is_const0( const ternary_truth_table& tt ) +{ + if constexpr ( UseDCs ) + { + return is_const0( tt._bits & tt._care ); + } + else + { + return is_const0( tt._bits | ~tt._care ); + } +} + +/*! \brief Checks whether a quaternary truth table is constant composed by only - and 0. + + \param tt Truth table +*/ +template +inline bool is_const0( const quaternary_truth_table& tt ) +{ + return is_const0( ~tt._offset ); +} + +/*! \endcond */ + +/*! \brief Checks whether the intersection of two truth tables is empty + + \param first First truth table + \param second Second truth table + \param polarity1 Polarity of the first truth table + \param polarity2 Polarity of the second truth table +*/ +template::value>> +bool intersection_is_empty( const TT& first, const TT& second ) +{ + if constexpr ( polarity1 && polarity2 ) + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( a & b ) == 0u; } ); + else if constexpr ( !polarity1 && polarity2 ) + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( ~a & b ) == 0u; } ); + else if constexpr ( polarity1 && !polarity2 ) + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( a & ~b ) == 0u; } ); + else // !polarity1 && !polarity2 + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( ~a & ~b ) == 0u; } ); +} + +/*! \brief Checks whether the intersection of three truth tables is empty + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param polarity1 Polarity of the first truth table + \param polarity2 Polarity of the second truth table + \param polarity3 Polarity of the first truth table +*/ +template::value>> +bool intersection_is_empty( const TT& first, const TT& second, const TT& third ) +{ + if constexpr ( polarity1 && polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & b & c ) == 0u; } ); + else if constexpr ( !polarity1 && polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & b & c ) == 0u; } ); + else if constexpr ( polarity1 && !polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & ~b & c ) == 0u; } ); + else if constexpr ( polarity1 && polarity2 && !polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & b & ~c ) == 0u; } ); + else if constexpr ( !polarity1 && !polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & ~b & c ) == 0u; } ); + else if constexpr ( polarity1 && !polarity2 && !polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & ~b & ~c ) == 0u; } ); + else if constexpr ( !polarity1 && polarity2 && !polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & b & ~c ) == 0u; } ); + else // !polarity1 && !polarity2 && !polarity3 + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & ~b & ~c ) == 0u; } ); +} + +/*! \brief Checks whether truth table depends on given variable index + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var( const TT& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + return std::any_of( std::begin( tt._bits ), std::end( tt._bits ), + [var_index]( uint64_t word ) { return ( ( word >> ( uint64_t( 1 ) << var_index ) ) & detail::projections_neg[var_index] ) != + ( word & detail::projections_neg[var_index] ); } ); + } + + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + if ( tt._bits[i + j] != tt._bits[i + j + step] ) + { + return true; + } + } + } + return false; +} + +/*! \cond PRIVATE */ +template +bool has_var( const static_truth_table& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + return ( ( tt._bits >> ( 1 << var_index ) ) & detail::projections_neg[var_index] ) != + ( tt._bits & detail::projections_neg[var_index] ); +} +/*! \endcond */ + +/*! \brief Checks whether a quaternary truth table depends on given variable index. + This function returns false if the truth table potentially does not depend + on the variable (due to don't cares) and returns true if the truth table potentially + depends on the variable (due to don't knows). + + For example, has_var( -01-, 0 ) = has_var( -01-, 1 ) = false. + In the first case the truth table is considered to be 0011 and + in the second case the truth table is considered to be 1010. + + Yet, has_var( x01-, 0 ) = has_var( x01-, 1 ) = true. + In the first case we cannot assume x = 0 and + in the second case we cannot assume x = 1. + + Finally, has_var( x0-0, 1 ) = true, since the don't care should be + equal to an unknown value. + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var( const quaternary_truth_table& tt, uint8_t var_index ) +{ + auto const compare_func = []( auto a, auto b, auto c, auto d ) { + return ( ~a & ~b & ~c & ~d ) | ( b & d ) | ( a & c ); + }; + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + auto const projection = [&var_index]( auto a ) { + return ( a & detail::projections[var_index] ); + }; + auto const projection_neg = [&var_index]( auto a ) { + return ( a & detail::projections_neg[var_index] ); + }; + auto proj_pos_on = unary_operation( tt._onset, projection ); + auto proj_pos_off = unary_operation( tt._offset, projection ); + auto proj_neg_on = unary_operation( tt._onset, projection_neg ) << ( 1 << var_index ); + auto proj_neg_off = unary_operation( tt._offset, projection_neg ) << ( 1 << var_index ); + return !is_const0( ~quaternary_operation( proj_pos_on, proj_pos_off, proj_neg_on, proj_neg_off, compare_func ) ); + } + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + auto tta = create( tt._onset.num_vars() ); + auto ttb = create( tt._onset.num_vars() ); + auto ttc = create( tt._onset.num_vars() ); + auto ttd = create( tt._onset.num_vars() ); + tta._bits[0] = get_block( tt._onset, i + j ); + ttb._bits[0] = get_block( tt._offset, i + j ); + ttc._bits[0] = get_block( tt._onset, i + j + step ); + ttd._bits[0] = get_block( tt._offset, i + j + step ); + if ( !is_const0( ~quaternary_operation( tta, ttb, ttc, ttd, compare_func ) ) ) + { + return true; + } + } + } + return false; +} + +/*! \brief Checks whether a ternary truth table depends on given variable index.\ + + When the template parameter UseDCs is false, don't cares are treated like zeros. + When the template parameter UseDCs is true, this function returns: + - true if the onset shows that the function depends on the variable. + - false if a don't cares assignments makes the function independent of the variable. + + For example, let the hexadecimal representation of the onset be 0xF0000000, and + the hexadecimal representation of the careset be 0xF0000000. This function is + independent of the variable 2, with projection function 0xF0F0F0F0 for the following + onset, careset pair ( 0xFF000000, 0xFF000000 ). + + Reassigning the careset and the onset is essential when checking if an incompletely + specified function depends on multiple variables, since different variables might + require different don't cares assignments to achieve indendence on different variables. + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + if constexpr ( UseDCs ) + { + ternary_truth_table tt0 = tt; + ternary_truth_table tt1 = tt; + cofactor0_inplace( tt0, var_index ); + cofactor1_inplace( tt1, var_index ); + const TT diff = tt0._bits ^ tt1._bits; + const TT mask = tt0._care & tt1._care; + if ( kitty::count_ones( diff & mask ) > 0 ) + return true; + /* Adjust the careset and the onset to avoid contradictions. */ + tt._care |= ( ~mask ) & diff; + tt._bits = tt0._bits | tt1._bits; + return false; + } + else + { + return has_var( tt._bits, var_index ); + } +} + +/*! \brief Checks whether a ternary truth table depends on given variable index.\ + + When the template parameter UseDCs is false, don't cares are treated like zeros. + When the template parameter UseDCs is true, this function returns: + - true if the onset shows that the function depends on the variable. + - false if a don't cares assignments makes the function independent of the variable. + + For example, let the hexadecimal representation of the onset be 0xF0000000, and + the hexadecimal representation of the careset be 0xF0000000. This function is + independent of the variable 2, with projection function 0xF0F0F0F0 for the following + onset, careset pair ( 0xFF000000, 0xFF000000 ). + + Warning. This function DOES NOT perform the reassignment. Use has_var_inplace if that + is the desired behavior. + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var( ternary_truth_table const& tt, uint8_t var_index ) +{ + ternary_truth_table ttc = tt; + return has_var_inplace( ttc, var_index ); +} + +/*! \brief Computes the next lexicographically larger truth table + + This methods updates `tt` to become the next lexicographically + larger truth table. If `tt` is already the largest truth table, the + updated truth table will contain all zeros. + + \param tt Truth table +*/ +template +void next_inplace( TT& tt ) +{ + if ( tt.num_vars() <= 6u ) + { + tt._bits[0]++; + tt.mask_bits(); + } + else + { + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); ++i ) + { + /* If incrementing the word does not lead to an overflow, we're done*/ + if ( ++tt._bits[i] != 0 ) + { + break; + } + } + } +} + +/*! \cond PRIVATE */ +template +inline void next_inplace( static_truth_table& tt ) +{ + tt._bits++; + tt.mask_bits(); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +inline void next_inplace( partial_truth_table& tt ) +{ + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); ++i ) + { + /* If incrementing the word does not lead to an overflow, we're done*/ + if ( ++tt._bits[i] != 0u ) + { + break; + } + } + tt.mask_bits(); +} +/*! \endcond */ + +/*! \brief Computes the next lexicographically larger truth table + + This methods updates `tt` to become the next lexicographically + larger truth table. If `tt` is already the largest truth table, the + updated truth table will contain all zeros. + + Don't cares are treated like zeros, so the truth tables 1110 and 111- will become 1111. + + This method never increase the number of don't cares of the input truth table. + + For example, truth table -101 will become -110, not -11-. + + \param tt Truth table +*/ +template +void next_inplace( ternary_truth_table& tt ) +{ + auto copy = tt; + next_inplace( tt._bits ); + tt._care = tt._care | ( tt._bits ^ copy._bits ); +} + +/*! \brief Computes the next lexicographically larger truth table + + This methods updates `tt` to become the next lexicographically + larger truth table. If `tt` is already the largest truth table, the + updated truth table will contain all zeros. + + Lexicographical increasing order for quaternary truth table: + + 0, 1, -, x + + \param tt Truth table +*/ + +template +void next_inplace( quaternary_truth_table& tt ) +{ + auto copy = tt; + int64_t first_bit_on = find_first_one_bit( tt._onset ); + int64_t first_bit_of = find_first_one_bit( tt._offset ); + if ( first_bit_on == -1 ) + first_bit_on = tt._onset.num_bits(); + if ( first_bit_of == -1 ) + first_bit_of = tt._offset.num_bits(); + if ( first_bit_of < first_bit_on ) + { + clear_bit( tt._offset, first_bit_of ); + set_bit( tt._onset, first_bit_of ); + for ( int64_t i = 0; i < first_bit_of; i++ ) + { + set_bit( tt._offset, i ); + clear_bit( tt._onset, i ); + } + } + else + { + if ( first_bit_of > first_bit_on ) + { + set_bit( tt._offset, first_bit_on ); + for ( int64_t i = 0; i < first_bit_on; i++ ) + { + set_bit( tt._offset, i ); + clear_bit( tt._onset, i ); + } + } + else + { + if ( uint64_t( first_bit_of ) == tt._offset.num_bits() && uint64_t( first_bit_on ) == tt._onset.num_bits() ) + set_bit( tt._offset, first_bit_of - 1 ); + else + { + clear_bit( tt._onset, first_bit_on ); + clear_bit( tt._offset, first_bit_on ); + } + for ( int64_t i = 0; i < first_bit_of; i++ ) + { + set_bit( tt._offset, i ); + clear_bit( tt._onset, i ); + } + } + } +} + +/*! \brief Returns the next lexicographically larger truth table + + Out-of-place variant for `next_inplace`. + + \param tt Truth table +*/ +template +inline TT next( const TT& tt ) +{ + auto copy = tt; + next_inplace( copy ); + return copy; +} + +/*! \brief Computes co-factor with respect to 0 + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +void cofactor0_inplace( TT& tt, uint8_t var_index ) +{ + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + std::transform( std::begin( tt._bits ), std::end( tt._bits ), + std::begin( tt._bits ), + [var_index]( uint64_t word ) { return ( ( word & detail::projections_neg[var_index] ) << ( uint64_t( 1 ) << var_index ) ) | + ( word & detail::projections_neg[var_index] ); } ); + } + else + { + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + tt._bits[i + j + step] = tt._bits[i + j]; + } + } + } +} + +/*! \cond PRIVATE */ +template +void cofactor0_inplace( static_truth_table& tt, uint8_t var_index ) +{ + tt._bits = ( ( tt._bits & detail::projections_neg[var_index] ) << ( 1 << var_index ) ) | + ( tt._bits & detail::projections_neg[var_index] ); +} +/*! \endcond */ + +/*! \brief Computes co-factor with respect to 0 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor0_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + cofactor0_inplace( tt._bits, var_index ); + cofactor0_inplace( tt._care, var_index ); +} + +/*! \brief Computes co-factor with respect to 0 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor0_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + cofactor0_inplace( tt._onset, var_index ); + cofactor0_inplace( tt._offset, var_index ); +} + +/*! \brief Returns co-factor with respect to 0 + + \param tt Truth table + \param var_index Variable index +*/ +template +TT cofactor0( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + cofactor0_inplace( copy, var_index ); + return copy; +} + +/*! \brief Computes co-factor with respect to 1 + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +void cofactor1_inplace( TT& tt, uint8_t var_index ) +{ + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + std::transform( std::begin( tt._bits ), std::end( tt._bits ), + std::begin( tt._bits ), + [var_index]( uint64_t word ) { return ( word & detail::projections[var_index] ) | + ( ( word & detail::projections[var_index] ) >> ( uint64_t( 1 ) << var_index ) ); } ); + } + else + { + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + tt._bits[i + j] = tt._bits[i + j + step]; + } + } + } +} + +/*! \cond PRIVATE */ +template +void cofactor1_inplace( static_truth_table& tt, uint8_t var_index ) +{ + tt._bits = ( tt._bits & detail::projections[var_index] ) | ( ( tt._bits & detail::projections[var_index] ) >> ( 1 << var_index ) ); +} +/*! \endcond */ + +/*! \brief Computes co-factor with respect to 1 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor1_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + cofactor1_inplace( tt._bits, var_index ); + cofactor1_inplace( tt._care, var_index ); +} + +/*! \brief Computes co-factor with respect to 1 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor1_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + cofactor1_inplace( tt._onset, var_index ); + cofactor1_inplace( tt._offset, var_index ); +} + +/*! \brief Returns co-factor with respect to 1 + + \param tt Truth table + \param var_index Variable index +*/ +template +TT cofactor1( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + cofactor1_inplace( copy, var_index ); + return copy; +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap_adjacent` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void swap_adjacent_inplace( TT& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() - 1 ); + + /* permute within each word */ + if ( var_index < 5 ) + { + const auto shift = uint64_t( 1 ) << var_index; + std::transform( std::begin( tt._bits ), std::end( tt._bits ), std::begin( tt._bits ), + [shift, var_index]( uint64_t word ) { + return ( word & detail::permutation_masks[var_index][0] ) | + ( ( word & detail::permutation_masks[var_index][1] ) << shift ) | + ( ( word & detail::permutation_masks[var_index][2] ) >> shift ); + } ); + } + /* permute (half) parts of words */ + else if ( var_index == 5 ) + { + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + const auto tmp = *it; + auto it2 = it + 1; + *it = ( tmp & 0xffffffff ) | ( *it2 << 0x20 ); + *it2 = ( *it2 & UINT64_C( 0xffffffff00000000 ) ) | ( tmp >> 0x20 ); + it += 2; + } + } + /* permute comlete words */ + else + { + const auto step = 1 << ( var_index - 6 ); + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) + { + std::swap( *( it + i + step ), *( it + i + 2 * step ) ); + } + it += 4 * step; + } + } +} + +/*! \cond PRIVATE */ +template +void swap_adjacent_inplace( static_truth_table& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + const auto shift = uint64_t( 1 ) << var_index; + + tt._bits = ( tt._bits & detail::permutation_masks[var_index][0] ) | + ( ( tt._bits & detail::permutation_masks[var_index][1] ) << shift ) | + ( ( tt._bits & detail::permutation_masks[var_index][2] ) >> shift ); +} +/*! \endcond */ + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap_adjacent` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void swap_adjacent_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + swap_adjacent_inplace( tt._bits, var_index ); + swap_adjacent_inplace( tt._care, var_index ); +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap_adjacent` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void swap_adjacent_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + swap_adjacent_inplace( tt._onset, var_index ); + swap_adjacent_inplace( tt._offset, var_index ); +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will return a new truth table with the result. + + \param tt Truth table + \param var_index A variable +*/ +template +inline TT swap_adjacent( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + swap_adjacent_inplace( copy, var_index ); + return copy; +} + +/*! \brief Swaps two variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap` instead. + + \param tt Truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template::value>> +void swap_inplace( TT& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + if ( var_index1 == var_index2 ) + { + return; + } + + if ( var_index1 > var_index2 ) + { + std::swap( var_index1, var_index2 ); + } + + if ( tt.num_vars() <= 6 ) + { + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + tt._bits[0] = ( tt._bits[0] & pmask[0] ) | ( ( tt._bits[0] & pmask[1] ) << shift ) | ( ( tt._bits[0] & pmask[2] ) >> shift ); + } + else if ( var_index2 <= 5 ) + { + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + std::transform( std::begin( tt._bits ), std::end( tt._bits ), std::begin( tt._bits ), + [shift, &pmask]( uint64_t word ) { + return ( word & pmask[0] ) | ( ( word & pmask[1] ) << shift ) | ( ( word & pmask[2] ) >> shift ); + } ); + } + else if ( var_index1 <= 5 ) /* in this case, var_index2 > 5 */ + { + const auto step = 1 << ( var_index2 - 6 ); + const auto shift = 1 << var_index1; + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) + { + const auto low_to_high = ( *( it + i ) & detail::projections[var_index1] ) >> shift; + const auto high_to_low = ( *( it + i + step ) << shift ) & detail::projections[var_index1]; + *( it + i ) = ( *( it + i ) & ~detail::projections[var_index1] ) | high_to_low; + *( it + i + step ) = ( *( it + i + step ) & detail::projections[var_index1] ) | low_to_high; + } + it += 2 * step; + } + } + else + { + const auto step1 = 1 << ( var_index1 - 6 ); + const auto step2 = 1 << ( var_index2 - 6 ); + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = 0; i < step2; i += 2 * step1 ) + { + for ( auto j = 0; j < step1; ++j ) + { + std::swap( *( it + i + j + step1 ), *( it + i + j + step2 ) ); + } + } + it += 2 * step2; + } + } +} + +/*! \cond PRIVATE */ +template +inline void swap_inplace( static_truth_table& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + if ( var_index1 == var_index2 ) + { + return; + } + + if ( var_index1 > var_index2 ) + { + std::swap( var_index1, var_index2 ); + } + + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + tt._bits = ( tt._bits & pmask[0] ) | ( ( tt._bits & pmask[1] ) << shift ) | ( ( tt._bits & pmask[2] ) >> shift ); +} +/* \endcond */ + +/*! \brief Swaps two variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap` instead. + + \param tt Ternary truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template::value>> +void swap_inplace( ternary_truth_table& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + swap_inplace( tt._bits, var_index1, var_index2 ); + swap_inplace( tt._care, var_index1, var_index2 ); +} + +/*! \brief Swaps two variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap` instead. + + \param tt Ternary truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template::value>> +void swap_inplace( quaternary_truth_table& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + swap_inplace( tt._onset, var_index1, var_index2 ); + swap_inplace( tt._offset, var_index1, var_index2 ); +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will return a new truth table with the result. + + \param tt Truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template +inline TT swap( const TT& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + auto copy = tt; + swap_inplace( copy, var_index1, var_index2 ); + return copy; +} + +/*! \brief Flips a variable in a truth table + + The function flips variable `var_index` in `tt`. The function will + change `tt` in-place. If `tt` should not be changed, one can use + `flip` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void flip_inplace( TT& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + if ( tt.num_blocks() == 1 ) + { + const auto shift = 1 << var_index; + tt._bits[0] = ( ( tt._bits[0] << shift ) & detail::projections[var_index] ) | ( ( tt._bits[0] & detail::projections[var_index] ) >> shift ); + } + else if ( var_index < 6 ) + { + const auto shift = 1 << var_index; + std::transform( std::begin( tt._bits ), std::end( tt._bits ), std::begin( tt._bits ), + [var_index, shift]( uint64_t word ) { + return ( ( word << shift ) & detail::projections[var_index] ) | ( ( word & detail::projections[var_index] ) >> shift ); + } ); + } + else + { + const auto step = 1 << ( var_index - 6 ); + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) + { + std::swap( *( it + i ), *( it + i + step ) ); + } + it += 2 * step; + } + } +} + +/*! \cond PRIVATE */ +template +inline void flip_inplace( static_truth_table& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + const auto shift = 1 << var_index; + tt._bits = ( ( tt._bits << shift ) & detail::projections[var_index] ) | ( ( tt._bits & detail::projections[var_index] ) >> shift ); +} +/* \endcond */ + +/*! \brief Flips a variable in a ternary truth table + + The function flips variable `var_index` in `tt`. The function will + change `tt` in-place. If `tt` should not be changed, one can use + `flip` instead. + + \param tt Ternary truth table + \param var_index A variable +*/ +template::value>> +void flip_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + flip_inplace( tt._bits, var_index ); + flip_inplace( tt._care, var_index ); +} + +/*! \brief Flips a variable in a ternary truth table + + The function flips variable `var_index` in `tt`. The function will + change `tt` in-place. If `tt` should not be changed, one can use + `flip` instead. + + \param tt Ternary truth table + \param var_index A variable +*/ +template::value>> +void flip_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + flip_inplace( tt._onset, var_index ); + flip_inplace( tt._offset, var_index ); +} + +/*! \brief Flips a variable in a truth table + + The function flips variable `var_index` in `tt`. The function will + not change `tt` and return the result as a copy. + + \param tt Truth table + \param var_index A variable +*/ +template +inline TT flip( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + flip_inplace( copy, var_index ); + return copy; +} + +/*! \brief Reorders truth table to have minimum base + + This function will reorder variables, such that there are no + "holes". For example, the function \f$ x_0 \land x_2 \f$ will be + changed to \f$ x_0 \land x_1 \f$ by swapping \f$ x_1 \f$ with \f$ + x_2 \f$. That is all variables that are not in the functional + support will be moved to the back. Note that the size of the truth + table is not changed, because for `static_truth_table` one cannot + compute it at compile-time. + + The function changes the truth table and returns a vector with all + variable indexes that were in the functional support of the original + function. + + \param tt Truth table + */ +template::value>> +std::vector min_base_inplace( TT& tt ) +{ + std::vector support; + + auto k = 0u; + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( !has_var( tt, i ) ) + { + continue; + } + if ( k < i ) + { + swap_inplace( tt, k, i ); + } + support.push_back( i ); + ++k; + } + + return support; +} + +/*! \brief Reorders truth table to have minimum base + + This function will reorder variables, such that there are no + "holes". For example, the function \f$ x_0 \land x_2 \f$ will be + changed to \f$ x_0 \land x_1 \f$ by swapping \f$ x_1 \f$ with \f$ + x_2 \f$. That is all variables that are not in the functional + support will be moved to the back. Note that the size of the truth + table is not changed, because for `static_truth_table` one cannot + compute it at compile-time. + + The function changes the truth table and returns a vector with all + variable indexes that were in the functional support of the original + function. + + \param tt Truth table + */ +template::value>> +std::vector min_base_inplace( ternary_truth_table& tt ) +{ + std::vector support; + + auto k = 0u; + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( !has_var( tt, i ) ) + { + continue; + } + if ( k < i ) + { + swap_inplace( tt, k, i ); + } + support.push_back( i ); + ++k; + } + + return support; +} + +/*! \brief Expands truth table from minimum base to original based on support + + This is the inverse operation to `min_base_inplace`, where the + support is used to swap variables back to their original positions. + + \param tt Truth table + \param support Original indexes of support variables +*/ +template +void expand_inplace( TT& tt, const std::vector& support ) +{ + for ( int i = static_cast( support.size() ) - 1; i >= 0; --i ) + { + assert( i <= support[i] ); + swap_inplace( tt, i, support[i] ); + } +} + +/*! \brief Extends smaller truth table to larger one + + The most significant variables will not be in the functional support of the + resulting truth table, but the method is helpful to align a truth table when + being used with another one. + + \param tt Larger truth table to create + \param from Smaller truth table to copy from +*/ +template::value>> +void extend_to_inplace( TT& tt, const TTFrom& from ) +{ + assert( tt.num_vars() >= from.num_vars() ); + + if ( from.num_vars() < 6 ) + { + auto mask = *from.begin(); + + for ( auto i = from.num_vars(); i < std::min( 6, tt.num_vars() ); ++i ) + { + mask |= ( mask << ( 1 << i ) ); + } + + std::fill( tt.begin(), tt.end(), mask ); + } + else + { + auto it = tt.begin(); + while ( it != tt.end() ) + { + it = std::copy( from.cbegin(), from.cend(), it ); + } + } +} + +/*! \brief Extends smaller ternary truth table to larger one + + The most significant variables will not be in the functional support of the + resulting truth table, but the method is helpful to align a truth table when + being used with another one. + + \param tt Larger ternary truth table to create + \param from Smaller ternary truth table to copy from +*/ +template::value>> +void extend_to_inplace( ternary_truth_table& tt, const ternary_truth_table& from ) +{ + extend_to_inplace( tt._bits, from._bits ); + extend_to_inplace( tt._care, from._care ); +} + +/*! \brief Extends smaller ternary truth table to larger one + + The most significant variables will not be in the functional support of the + resulting truth table, but the method is helpful to align a truth table when + being used with another one. + + \param tt Larger ternary truth table to create + \param from Smaller ternary truth table to copy from +*/ +template::value>> +void extend_to_inplace( quaternary_truth_table& tt, const quaternary_truth_table& from ) +{ + extend_to_inplace( tt._onset, from._onset ); + extend_to_inplace( tt._offset, from._offset ); +} + +/*! \brief Extends smaller truth table to larger static one + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating static truth tables. The + template parameter `NumVars` must be equal or larger to the number of + variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline static_truth_table extend_to( const TTFrom& from ) +{ + static_truth_table tt; + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Extends smaller truth table to larger dynamic one + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating dynamic truth tables. + The parameter `num_vars` must be equal or larger to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline dynamic_truth_table extend_to( const TTFrom& from, unsigned num_vars ) +{ + auto tt = create( num_vars ); + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Extends smaller ternary truth table to larger one of the same underlying type + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating dynamic truth tables. + The parameter `num_vars` must be equal or larger to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline ternary_truth_table extend_to( const ternary_truth_table& from, unsigned num_vars ) +{ + auto tt = ternary_truth_table( num_vars ); + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Extends smaller ternary truth table to larger one of the same underlying type + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating dynamic truth tables. + The parameter `num_vars` must be equal or larger to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline quaternary_truth_table extend_to( const quaternary_truth_table& from, unsigned num_vars ) +{ + auto tt = quaternary_truth_table( num_vars ); + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger truth table to smaller one + + The function expects that the most significant bits, which are cut off, are + not in the functional support of the original function. Only then it is + ensured that the resulting function is equivalent. + + \param tt Smaller truth table to create + \param from Larger truth table to copy from +*/ +template::value>> +void shrink_to_inplace( TT& tt, const TTFrom& from ) +{ + assert( tt.num_vars() <= from.num_vars() ); + + std::copy( from.begin(), from.begin() + tt.num_blocks(), tt.begin() ); + + if ( tt.num_vars() < 6 ) + { + tt.mask_bits(); + } +} + +/*! \brief Shrinks larger ternary truth table to smaller one + + The function expects that the most significant bits, which are cut off, are + not in the functional support of the original function. Only then it is + ensured that the resulting function is equivalent. + + \param tt Smaller ternary truth table to create + \param from Larger ternary truth table to copy from +*/ +template::value>> +void shrink_to_inplace( ternary_truth_table& tt, const ternary_truth_table& from ) +{ + shrink_to_inplace( tt._bits, from._bits ); + shrink_to_inplace( tt._care, from._care ); +} + +/*! \brief Shrinks larger ternary truth table to smaller one + + The function expects that the most significant bits, which are cut off, are + not in the functional support of the original function. Only then it is + ensured that the resulting function is equivalent. + + \param tt Smaller ternary truth table to create + \param from Larger ternary truth table to copy from +*/ +template::value>> +void shrink_to_inplace( quaternary_truth_table& tt, const quaternary_truth_table& from ) +{ + shrink_to_inplace( tt._onset, from._onset ); + shrink_to_inplace( tt._offset, from._offset ); +} + +/*! \brief Shrinks larger truth table to smaller static one + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating static truth tables. The template + parameter `NumVars` must be equal or smaller to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline static_truth_table shrink_to( const TTFrom& from ) +{ + static_truth_table tt; + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger truth table to smaller dynamic one + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating dynamic tables. The parameter + `num_vars` must be equal or smaller to the number of variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline dynamic_truth_table shrink_to( const TTFrom& from, unsigned num_vars ) +{ + auto tt = create( num_vars ); + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger ternary truth table to smaller one of the same underlying type + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating dynamic tables. The parameter + `num_vars` must be equal or smaller to the number of variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline ternary_truth_table shrink_to( const ternary_truth_table& from, unsigned num_vars ) +{ + auto tt = ternary_truth_table( num_vars ); + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger ternary truth table to smaller one of the same underlying type + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating dynamic tables. The parameter + `num_vars` must be equal or smaller to the number of variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline quaternary_truth_table shrink_to( const quaternary_truth_table& from, unsigned num_vars ) +{ + auto tt = quaternary_truth_table( num_vars ); + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Left-shift truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +void shift_left_inplace( TT& tt, uint64_t shift ) +{ + /* small truth table */ + if ( tt.num_vars() <= 6 ) + { + tt._bits[0] <<= shift; + tt.mask_bits(); + return; + } + + /* large shift */ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0 ) + { + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0 ) + { + const auto rshift = 64u - rem; + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = ( tt._bits[i] << rem ) | ( tt._bits[i - 1] >> rshift ); + } + tt._bits[div] = tt._bits[0] << rem; + } + else + { + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = tt._bits[i]; + } + tt._bits[div] = tt._bits[0]; + } + + std::fill_n( std::begin( tt._bits ), div, 0u ); + } +} + +/*! \cond PRIVATE */ +template +inline void shift_left_inplace( static_truth_table& tt, uint64_t shift ) +{ + tt._bits <<= shift; + tt.mask_bits(); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +inline void shift_left_inplace( partial_truth_table& tt, uint64_t shift ) +{ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0u ) + { + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0u ) + { + const auto rshift = 64u - rem; + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = ( tt._bits[i] << rem ) | ( tt._bits[i - 1] >> rshift ); + } + tt._bits[div] = tt._bits[0] << rem; + } + else + { + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = tt._bits[i]; + } + tt._bits[div] = tt._bits[0]; + } + + std::fill_n( std::begin( tt._bits ), div, 0u ); + tt.mask_bits(); + } +} +/*! \endcond */ + +/*! \brief Left-shift ternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_left_inplace( ternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt._bits, shift ); + shift_left_inplace( tt._care, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._care, i ); + } +} + +/*! \brief Left-shift quaternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_left_inplace( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt._onset, shift ); + shift_left_inplace( tt._offset, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._offset, i ); + } +} + +/*! \brief Left-shift truth table + + Out-of-place variant of `shift_left_inplace`. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline TT shift_left( const TT& tt, uint64_t shift ) +{ + auto copy = tt; + shift_left_inplace( copy, shift ); + return copy; +} + +/*! \brief Right-shift truth table + + Drops overflowing least-significant bits and fills up most-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +void shift_right_inplace( TT& tt, uint64_t shift ) +{ + /* small truth table */ + if ( tt.num_vars() <= 6 ) + { + tt._bits[0] >>= shift; + tt.mask_bits(); + return; + } + + /* large shift */ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0 ) + { + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0 ) + { + const auto rshift = 64u - rem; + for ( auto i = div; i < last; ++i ) + { + tt._bits[i - div] = ( tt._bits[i] >> rem ) | ( tt._bits[i + 1] << rshift ); + } + tt._bits[last - div] = tt._bits[last] >> rem; + } + else + { + for ( auto i = div; i <= last; ++i ) + { + tt._bits[i - div] = tt._bits[i]; + } + } + + std::fill_n( std::begin( tt._bits ) + ( tt.num_blocks() - div ), div, 0u ); + } +} + +/*! \cond PRIVATE */ +template +inline void shift_right_inplace( static_truth_table& tt, uint64_t shift ) +{ + tt._bits >>= shift; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +inline void shift_right_inplace( partial_truth_table& tt, uint64_t shift ) +{ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0u ) + { + tt.mask_bits(); + + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0u ) + { + const auto rshift = 64u - rem; + for ( auto i = div; i < last; ++i ) + { + tt._bits[i - div] = ( tt._bits[i] >> rem ) | ( tt._bits[i + 1] << rshift ); + } + tt._bits[last - div] = tt._bits[last] >> rem; + } + else + { + for ( auto i = div; i <= last; ++i ) + { + tt._bits[i - div] = tt._bits[i]; + } + } + + std::fill_n( std::begin( tt._bits ) + ( tt.num_blocks() - div ), div, 0u ); + } +} +/*! \endcond */ + +/*! \brief Right-shift ternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_right_inplace( ternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt._bits, shift ); + shift_right_inplace( tt._care, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._care, tt._care.num_bits() - 1 - i ); + } +} + +/*! \brief Right-shift quaternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_right_inplace( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt._onset, shift ); + shift_right_inplace( tt._offset, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._offset, tt._offset.num_bits() - 1 - i ); + } +} + +/*! \brief Right-shift truth table + + Out-of-place variant of `shift_right_inplace`. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline TT shift_right( const TT& tt, uint64_t shift ) +{ + auto copy = tt; + shift_right_inplace( copy, shift ); + return copy; +} + +/*! \brief Composes a truth table. + + Given a function `f`, and a set of truth tables as arguments, computes the + composed truth table. For example, if `f(x1, x2) = 1001` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 1100. This function can + be regarded as a general operator with arity `vars.size()` where the behavior + of the operator is given by f. + + \param f The outer function + \param vars The ordered set of input variables + \return The composed truth table with vars.size() variables +*/ +template::value>> +inline auto compose_truth_table( const TTf& f, const std::vector& vars ) +{ + assert( vars.size() == static_cast( f.num_vars() ) ); + auto composed = vars[0].construct(); + + for ( uint64_t i = 0u; i < composed.num_bits(); ++i ) + { + uint64_t index = 0u; + for ( uint64_t j = 0u; j < vars.size(); ++j ) + { + index += get_bit( vars[j], i ) << j; + } + if ( get_bit( f, index ) ) + { + set_bit( composed, i ); + } + else + { + clear_bit( composed, i ); + } + } + + return composed; +} + +/*! \brief Composes a truth table. + + Given a function `f`, and a set of truth tables as arguments, computes the + composed truth table. For example, if `f(x1, x2) = 1001` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 1000. + In case of don't cares this method treats them as "don't knows". + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 100-. + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= -010}`, the function returns -00-. + However, if `f(x1, x2) = 1100` and + `vars = {x1 = 1001, x2= -010}`, the function returns 1001. + + This function does not support truth quaternary table with more than 6 variables. + + \param f The outer function + \param vars The ordered set of input variables + \return The composed truth table with vars.size() variables +*/ +template::value>> +inline auto compose_truth_table( const ternary_truth_table& f, const std::vector>& vars ) +{ + assert( vars.size() == static_cast( f.num_vars() ) ); + auto a = create( vars[0].num_vars() ); + auto b = create( vars[0].num_vars() ); + auto composed = ternary_truth_table( a, b ); + + for ( uint64_t i = 0u; i < composed.num_bits(); ++i ) + { + auto f_copy = f; + uint64_t cnt_care = 0u; + for ( uint64_t j = 0u; j < vars.size(); ++j ) + { + auto tt_mask = f._bits.construct(); + auto const projection = [&j, &f]( auto a ) { + return ( a | detail::projections[f.num_vars() - 1 - j] ); + }; + auto const projection_neg = [&j, &f]( auto a ) { + return ( a | detail::projections_neg[f.num_vars() - j - 1] ); + }; + if ( !is_dont_care( vars[j], i ) ) + { + auto bit = get_bit( vars[j], i ); + if ( bit ) + { + auto ttt_mask = ternary_truth_table( unary_operation( tt_mask, projection ) ); + f_copy &= ttt_mask; + } + else + { + auto ttt_mask = ternary_truth_table( unary_operation( tt_mask, projection_neg ) ); + f_copy &= ttt_mask; + } + } + else + { + ++cnt_care; + } + } + if ( is_const0( f_copy ) ) + { + set_bit( composed, i, false ); + } + else + { + if ( count_ones( f_copy._bits ) == ( 1 << cnt_care ) ) // count_ones( f_copy._onset & ~f_copy._offset ) count real ones in f_copy + { + set_bit( composed, i ); + } + else + { + set_dont_care( composed, i ); + } + } + } + + return composed; +} + +/*! \brief Composes a truth table. + + Given a function `f`, and a set of truth tables as arguments, computes the + composed truth table. For example, if `f(x1, x2) = 1001` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 1000. + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 100-. + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= -010}`, the function returns 100-. + If `f(x1, x2) = 1100` and + `vars = {x1 = 1x01, x2= -01x}`, the function returns 1x01. + + This function does not support truth quaternary table with more than 6 variables. + + \param f The outer function + \param vars The ordered set of input variables + \return The composed truth table with vars.size() variables +*/ +template::value>> +inline auto compose_truth_table( const quaternary_truth_table& f, const std::vector>& vars ) +{ + assert( vars.size() == static_cast( f.num_vars() ) ); + auto a = create( vars[0].num_vars() ); + auto b = create( vars[0].num_vars() ); + auto composed = quaternary_truth_table( a, b ); + + for ( uint64_t i = 0u; i < composed.num_bits(); ++i ) + { + auto f_copy = f; + uint64_t cnt_care = 0u; + bool dont_know_found = false; + for ( uint64_t j = 0u; j < vars.size(); ++j ) + { + auto tt_mask = f._onset.construct(); + auto const projection = [&j, &f]( auto a ) { + return ( a | detail::projections[f.num_vars() - 1 - j] ); + }; + auto const projection_neg = [&j, &f]( auto a ) { + return ( a | detail::projections_neg[f.num_vars() - j - 1] ); + }; + if ( !is_dont_care( vars[j], i ) && !is_dont_know( vars[j], i ) ) + { + auto bit = get_bit( vars[j], i ); + if ( bit ) + { + auto ttt_mask = quaternary_truth_table( unary_operation( tt_mask, projection ) ); + f_copy &= ttt_mask; + } + else + { + auto ttt_mask = quaternary_truth_table( unary_operation( tt_mask, projection_neg ) ); + f_copy &= ttt_mask; + } + } + else + { + ++cnt_care; + if ( is_dont_know( vars[j], i ) ) + dont_know_found = true; + } + } + if ( is_const0( f_copy ) ) + { + if ( count_ones( f_copy._onset ) == ( 1 << cnt_care ) ) + set_dont_care( composed, i ); + else + set_bit( composed, i, false ); + } + else + { + if ( count_ones( f_copy._onset ) == ( 1 << cnt_care ) ) + { + set_bit( composed, i ); + } + else + { + if ( ( !dont_know_found && is_const0( ~f_copy._onset & ~f_copy._offset ) ) ) + set_dont_care( composed, i ); + else + set_dont_know( composed, i ); + } + } + } + + return composed; +} + +/*! \brief Shifts a small truth table with respect to a mask + + This function only works for truth tables with up to 6 inputs. The function + rearranges the variables according to a mask. For example, assume the 3-input + truth table \f$ x_0 \land x_1 \f$, which is not defined on \f$ x_2 \f$. + Applying this funtion with a mask `0b101` yields the function + \f$ x_0 \land x_2 \f$, and the mask `0b110` yields the function + \f$ x_1 \land x_2 \f$. The bits in the mask provide the new positions. It + is important that the positions in the mask do not exceed the truth table + size, since all operations are performed in-place and cannot change the + number of variables of the truth table. + + \param tt Truth table + \param mask Shift mask +*/ +template::value>> +inline void shift_with_mask_inplace( TT& f, uint8_t mask ) +{ + assert( f.num_vars() <= 6 ); + + *f.begin() = detail::compute_shift( *f.begin(), mask | ( 1 << f.num_vars() ) ); +} + +/*! \brief Shifts a small truth table with respect to a mask + + This function only works for truth tables with up to 6 inputs. The function + rearranges the variables according to a mask. For example, assume the 3-input + truth table \f$ x_0 \land x_1 \f$, which is not defined on \f$ x_2 \f$. + Applying this funtion with a mask `0b101` yields the function + \f$ x_0 \land x_2 \f$, and the mask `0b110` yields the function + \f$ x_1 \land x_2 \f$. The bits in the mask provide the new positions. It + is important that the positions in the mask do not exceed the truth table + size, since all operations are performed in-place and cannot change the + number of variables of the truth table. + The final result will not contain don't cares, they are all turned into 0s. + + \param tt Truth table + \param mask Shift mask +*/ +template::value>> +inline void shift_with_mask_inplace( ternary_truth_table& f, uint8_t mask ) +{ + assert( f.num_vars() <= 6 ); + + shift_with_mask_inplace( f._bits, mask ); + f._care = ~f._care.construct(); +} + +template::value>> +inline void shift_with_mask_inplace( quaternary_truth_table& f, uint8_t mask ) +{ + std::vector mask_from = {}; + std::vector mask_to = {}; + for ( auto i = 0u; i < f.num_vars(); i++ ) + { + if ( has_var( f, i ) ) + mask_from.push_back( i ); + } + for ( uint64_t count = 0u; mask > 0; mask = (int)mask / 2, count++ ) + { + if ( mask % 2 ) + mask_to.push_back( count ); + } + assert( mask_to.size() == mask_from.size() ); + std::vector index_remove = {}; + for ( auto i = 0u; i < mask_from.size(); i++ ) + { + auto it = std::find( mask_to.begin(), mask_to.end(), mask_from[i] ); + if ( it != mask_to.end() ) + { + mask_to.erase( it ); + mask_from.erase( mask_from.begin() + i ); + } + } + for ( auto i = 0u; i < mask_from.size(); i++ ) + { + swap_inplace( f, mask_from[i], mask_to[i] ); + } +} + +/*! \brief Shifts a small truth table with respect to a mask + + Out-of-place variant of `shift_with_mask_inplace`. + + \param tt Truth table + \param mask Shift mask +*/ +template +inline TT shift_with_mask( const TT& f, uint8_t mask ) +{ + auto copy = f; + shift_with_mask_inplace( copy, mask ); + return copy; +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/operators.hpp b/third-party/mockturtle/lib/kitty/kitty/operators.hpp new file mode 100644 index 00000000000..bc7e76c8fc3 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/operators.hpp @@ -0,0 +1,507 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file operators.hpp + \brief Implements operator shortcuts to operations + + \author Mathias Soeken +*/ + +#pragma once + +#include "dynamic_truth_table.hpp" +#include "operations.hpp" +#include "static_truth_table.hpp" +#include "partial_truth_table.hpp" +#include "ternary_truth_table.hpp" +#include "quaternary_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Operator for unary_not */ +inline dynamic_truth_table operator~( const dynamic_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +template +inline static_truth_table operator~( const static_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +inline partial_truth_table operator~( const partial_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +template +inline ternary_truth_table operator~( const ternary_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +template +inline quaternary_truth_table operator~( const quaternary_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for binary_and */ +inline dynamic_truth_table operator&( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +template +inline static_truth_table operator&( const static_truth_table& first, const static_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +inline partial_truth_table operator&( const partial_truth_table& first, const partial_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +template +inline ternary_truth_table operator&( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +template +inline quaternary_truth_table operator&( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +inline void operator&=( dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +template +inline void operator&=( static_truth_table& first, const static_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +inline void operator&=( partial_truth_table& first, const partial_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +template +inline void operator&=( ternary_truth_table& first, const ternary_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +template +inline void operator&=( quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_or */ +inline dynamic_truth_table operator|( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +template +inline static_truth_table operator|( const static_truth_table& first, const static_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +inline partial_truth_table operator|( const partial_truth_table& first, const partial_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +template +inline ternary_truth_table operator|( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +template +inline quaternary_truth_table operator|( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +inline void operator|=( dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +template +inline void operator|=( static_truth_table& first, const static_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +inline void operator|=( partial_truth_table& first, const partial_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +template +inline void operator|=( ternary_truth_table& first, const ternary_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +template +inline void operator|=( quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_xor */ +inline dynamic_truth_table operator^( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +template +inline static_truth_table operator^( const static_truth_table& first, const static_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +inline partial_truth_table operator^( const partial_truth_table& first, const partial_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +template +inline ternary_truth_table operator^( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +template +inline quaternary_truth_table operator^( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +inline void operator^=( dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +template +inline void operator^=( static_truth_table& first, const static_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +inline void operator^=( partial_truth_table& first, const partial_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +template +inline void operator^=( ternary_truth_table& first, const ternary_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +template +inline void operator^=( quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for equal */ +inline bool operator==( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +template +inline bool operator==( const static_truth_table& first, const static_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +inline bool operator==( const partial_truth_table& first, const partial_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +template +inline bool operator==( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +template +inline bool operator==( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +inline bool operator!=( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +template +inline bool operator!=( const static_truth_table& first, const static_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equal */ +inline bool operator!=( const partial_truth_table& first, const partial_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +template +inline bool operator!=( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +template +inline bool operator!=( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for less_than */ +inline bool operator<( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +template +inline bool operator<( const static_truth_table& first, const static_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +inline bool operator<( const partial_truth_table& first, const partial_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +template +inline bool operator<( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +template +inline bool operator<( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for left_shift */ +inline dynamic_truth_table operator<<( const dynamic_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +template +inline static_truth_table operator<<( const static_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +inline partial_truth_table operator<<( const partial_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +template +inline ternary_truth_table operator<<( const ternary_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +template +inline quaternary_truth_table operator<<( const quaternary_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +inline void operator<<=( dynamic_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +template +inline void operator<<=( static_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +inline void operator<<=( partial_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +template +inline void operator<<=( ternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +template +inline void operator<<=( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift */ +inline dynamic_truth_table operator>>( const dynamic_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +template +inline static_truth_table operator>>( const static_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +inline partial_truth_table operator>>( const partial_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +template +inline ternary_truth_table operator>>( const ternary_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +template +inline quaternary_truth_table operator>>( const quaternary_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +inline void operator>>=( dynamic_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +template +inline void operator>>=( static_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +inline void operator>>=( partial_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +template +inline void operator>>=( ternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +template +inline void operator>>=( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +} // namespace kitty diff --git a/third-party/mockturtle/lib/kitty/kitty/partial_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/partial_truth_table.hpp new file mode 100644 index 00000000000..fdf2812eab7 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/partial_truth_table.hpp @@ -0,0 +1,300 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file partial_truth_table.hpp + \brief Implements partial_truth_table + + \author Siang-Yun Lee +*/ + +#pragma once + +#include "detail/constants.hpp" +#include "traits.hpp" + +#include +#include +#include +#include +#include +#include + +namespace kitty +{ + +/*! Truth table with resizable, arbitrary number of bits + */ +struct partial_truth_table +{ + /*! \brief Standard constructor. + \param num_bits Number of bits in use initially + */ + explicit partial_truth_table( uint32_t num_bits ) + : _bits( num_bits ? ( ( ( num_bits - 1 ) >> 6 ) + 1 ) : 0 ), + _num_bits( num_bits ) + { + } + + /*! \brief Empty constructor. + + Creates an empty truth table. It has no bit in use. This constructor is + only used for convenience, if algorithms require the existence of default + constructable classes. + */ + partial_truth_table() : _num_bits( 0 ) {} + + /*! \brief Constructs a new partial truth table instance with the same number of bits and blocks. */ + inline partial_truth_table construct() const + { + return partial_truth_table( _num_bits ); + } + + /*! \brief Returns number of (allocated) blocks. + */ + inline auto num_blocks() const noexcept { return _bits.size(); } + + /*! \brief Returns number of (used) bits. + */ + inline auto num_bits() const noexcept { return _num_bits; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend() const noexcept { return _bits.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. The truth + table type is arbitrary. The vector of bits is resized accordingly. + + \param other Other truth table + */ + template::value>> + partial_truth_table& operator=( const TT& other ) + { + _bits.resize( other.num_blocks() ); + std::copy( other.begin(), other.end(), begin() ); + _num_bits = 1 << other.num_vars(); + + return *this; + } + + /*! \brief Masks the number of valid truth table bits. + + If not all the bits in the last block are used up, + we block out the remaining bits (fill with zero). + Bits are used from LSB. + */ + inline void mask_bits() noexcept + { + if ( _num_bits & 0x3f ) + { + _bits.back() &= 0xFFFFFFFFFFFFFFFF >> ( 64 - ( _num_bits & 0x3f ) ); + } + } + + /*! \brief Resize the truth table to have the given number of bits. + + If the desired length is larger than the current length, zeros will + be filled up in the end of the truth table. If the desired length + is shorter than the current length, extra bits in the end of the + truth table will be discarded. + + \param num_bits Desired length (number of bits) + */ + inline void resize( int num_bits ) noexcept + { + _num_bits = num_bits; + + unsigned needed_blocks = num_bits ? ( ( ( num_bits - 1 ) >> 6 ) + 1 ) : 0; + _bits.resize( needed_blocks, 0u ); + + mask_bits(); + } + + /*! \brief Add a bit to the end of the truth table. + \param bit Bit to be added + */ + inline void add_bit( bool bit ) noexcept + { + resize( _num_bits + 1 ); + if ( bit ) + { + _bits.back() |= (uint64_t)1 << ( ( _num_bits & 0x3f ) - 1 ); + } + } + + /*! \brief Add several bits to the end of the truth table. + \param bits Bits to be added + */ + inline void add_bits( std::vector& bits ) noexcept + { + for ( unsigned i = 0; i < bits.size(); ++i ) + { + add_bit( bits.at( i ) ); + } + } + + /*! \brief Add several bits to the end of the truth table. + \param bits Bits to be added + \param num_bits Number of bits in `bits` to be added (counted from LSB) + */ + inline void add_bits( uint64_t bits, int num_bits = 64 ) noexcept + { + assert( num_bits <= 64 ); + + if ( ( _num_bits & 0x3f ) + num_bits <= 64 ) /* no need for a new block */ + { + if ( _bits.size() == 0u ) + { + _bits.emplace_back( 0u ); + } + _bits.back() |= bits << ( _num_bits & 0x3f ); + } + else + { + auto first_half_len = 64 - ( _num_bits & 0x3f ); + _bits.back() |= bits << ( _num_bits & 0x3f ); + _bits.emplace_back( 0u ); + _bits.back() |= ( bits & ( 0xFFFFFFFFFFFFFFFF >> ( 64 - num_bits ) ) ) >> first_half_len; + } + _num_bits += num_bits; + } + + /*! \brief Overwrite the value at position `from` to position `to`. + \param from Position of the bit to be copied + \param to Position of the bit to be overwritten + */ + inline void copy_bit( uint32_t from, uint32_t to ) + { + if ( ( _bits[from >> 6] >> ( from & 0x3f ) ) & 0x1 ) + { + /* set_bit( to ) */ + _bits[to >> 6] |= uint64_t( 1 ) << ( to & 0x3f ); + } + else + { + /* clear_bit( to ) */ + _bits[to >> 6] &= ~( uint64_t( 1 ) << ( to & 0x3f ) ); + } + } + + /*! \brief Erase the bit at the given position by swapping with the + last bit and shrinking the truth table. + + This method is faster than `erase_bit` but do not keep the order + of the bits. + + \param position Position of the bit to be erased + */ + inline void erase_bit_swap( uint32_t position ) + { + assert( position < _num_bits ); + /* move the last bit (`_num_bits - 1`) to `position` */ + copy_bit( _num_bits - 1, position ); + resize( _num_bits - 1 ); + } + + /*! \brief Erase the bit at the given position and shift all the following bits. + + This method keeps the order of the bits but is slower and + do not keep the position of the bits. + + \param position Position of the bit to be erased + */ + inline void erase_bit_shift( uint32_t position ) + { + assert( position < _num_bits ); + uint64_t mask = 0xFFFFFFFFFFFFFFFF << ( position & 0x3f ); + _bits[position >> 6] = ( _bits[position >> 6] & ~mask ) | ( _bits[position >> 6] >> 1 & mask ); + uint32_t hole = position | 0x3f; + while ( hole < _num_bits - 1 ) + { + copy_bit( hole + 1, hole ); + hole += 64; + _bits[hole >> 6] >>= 1; + } + resize( _num_bits - 1 ); + } + + /*! \cond PRIVATE */ +public: /* fields */ + std::vector _bits; + uint32_t _num_bits; + /*! \endcond */ +}; + +template<> +struct is_truth_table : std::true_type +{ +}; + +template<> +struct is_complete_truth_table : std::false_type +{ +}; + +template<> +struct is_completely_specified_truth_table : std::true_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/permutation.hpp b/third-party/mockturtle/lib/kitty/kitty/permutation.hpp new file mode 100644 index 00000000000..5388f4fef1a --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/permutation.hpp @@ -0,0 +1,286 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file permutation.hpp + \brief Efficient permutation of truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "bit_operations.hpp" +#include "operators.hpp" +#include "print.hpp" + +namespace kitty +{ + +namespace detail +{ +template +std::pair compute_permutation_masks_pair( const TT& tt, std::vector& left, std::vector& right, unsigned step ) +{ + const auto n = tt.num_vars(); + const auto loops = ( 1 << ( n - 1 - step ) ); + const auto diff = ( 1 << step ); + + const auto offset = 1 << ( n - 1 ); + + struct node_t + { + std::vector::iterator a, b; /* numbers in left or right */ + unsigned lf{}, rf{}; /* left field right field */ + bool visited = false; + }; + + std::vector nodes( tt.num_bits() ); + + /* compute graph */ + auto idx1 = 0u, idx2 = 0u; + auto it1 = std::begin( left ); + for ( auto l1 = 0; l1 < loops; ++l1 ) + { + for ( auto c1 = 0; c1 < diff; ++c1 ) + { + auto it2 = std::begin( right ); + idx2 = offset; + + nodes[idx1].a = it1; + nodes[idx1].b = it1 + diff; + + for ( auto l2 = 0; l2 < loops; ++l2 ) + { + for ( auto c2 = 0; c2 < diff; ++c2 ) + { + if ( idx1 == 0u ) + { + nodes[idx2].a = it2; + nodes[idx2].b = it2 + diff; + } + + /* pair elements */ + auto& n1 = nodes[idx1]; + auto& n2 = nodes[idx2]; + + /* connect graph */ + if ( *n1.a == *n2.a ) + { + n1.lf = idx2; + n2.lf = idx1; + } + else if ( *n1.a == *n2.b ) + { + n1.lf = idx2; + n2.rf = idx1; + } + if ( *n1.b == *n2.a ) + { + n1.rf = idx2; + n2.lf = idx1; + } + else if ( *n1.b == *n2.b ) + { + n1.rf = idx2; + n2.rf = idx1; + } + + ++it2; + ++idx2; + } + it2 += diff; + } + ++it1; + ++idx1; + } + it1 += diff; + } + + /* traverse graph and compute masks */ + auto mask_left = tt.construct(); + auto mask_right = tt.construct(); + + while ( true ) + { + auto idx = 0; + + while ( idx < offset && nodes[idx].visited ) + { + ++idx; + } + + if ( idx == offset ) + { + break; + } + + auto left_side = true; + auto nr = *nodes[idx].a; + auto start = idx; + + do + { + auto& n = nodes[idx]; + + auto match = *n.a == nr; + + nr = match ? *n.b : *n.a; + idx = match ? n.rf : n.lf; + n.visited = true; + + if ( left_side != match ) + { + std::swap( *n.a, *n.b ); + + if ( left_side ) + { + set_bit( mask_left, std::distance( left.begin(), n.a ) ); + } + else + { + set_bit( mask_right, std::distance( right.begin(), n.a ) ); + } + } + + left_side = !left_side; + + } while ( idx != start ); + } + + return std::make_pair( mask_left, mask_right ); +} +} /* namespace detail */ + +/*! \brief Applies delta-swap operation + + The delta-swap operation swaps all position pairs \f$(i, i+\delta)\f$, for + which \f$\omega\f$ is set 1 at position \f$i\f$. + + See also Eq. 7.1.3-(69) in The Art of Computer Programming. + + \param tt Truth table + \param delta Index distance delta + \param omega Enable mask +*/ +template +inline void delta_swap_inplace( TT& tt, uint64_t delta, const TT& omega ) +{ + const auto y = ( tt ^ ( tt >> delta ) ) & omega; + tt = tt ^ y ^ ( y << delta ); +} + +/*! \brief Applies delta-swap operation + + Out-of-place variant for `delta_swap_inplace`. +*/ +template +TT delta_swap( const TT& tt, uint64_t delta, const TT& omega ) +{ + auto copy = tt; + delta_swap_inplace( copy, delta, omega ); + return copy; +} + +/*! \brief Permutes a truth table using a sequence of delta-swaps + + Masks is an array containing the \f$\omega\f$ masks. The \f$\delta\f$ values + are chosen as increasing and decreasing powers of 2, as described in Eq. + 7.1.3-(71) of The Art of Computer Programming. + + \param tt Truth table + \param masks Array of omega-masks +*/ +template +void permute_with_masks_inplace( TT& tt, const std::vector& masks ) +{ + for ( auto k = 0u; k < tt.num_vars(); ++k ) + { + delta_swap_inplace( tt, uint64_t( 1 ) << k, masks[k] ); + } + + for ( int k = tt.num_vars() - 2, i = tt.num_vars(); k >= 0; --k, ++i ) + { + delta_swap_inplace( tt, uint64_t( 1 ) << k, masks[i] ); + } +} + +/*! \brief Permutes a truth table using a sequence of delta-swaps + + Out-of-place variant of `permute_with_masks_inplace`. +*/ +template +TT permute_with_masks( const TT& tt, const std::vector& masks ) +{ + auto copy = tt; + permute_with_masks_inplace( copy, masks ); + return copy; +} + +/*! \brief Computes permutation bitmasks + + These bitmasks can be used with the `permute_with_masks` algorithm. The + algorithm to compute these masks is described in The Art of Computer + Programming, Section 7.1.3 'Bit permutation in general'. + + The input truth table can be arbitrary but is used to determine the type and + size of of the returned permutation masks. + + \param tt Base truth table to derive types and size + \param permutation Permutation +*/ +template +std::vector compute_permutation_masks( const TT& tt, const std::vector& permutation ) +{ + std::vector masks; + + std::vector left( permutation.size() ), right = permutation; + std::iota( left.begin(), left.end(), 0u ); + + for ( auto i = 0u; i < tt.num_vars() - 1u; ++i ) + { + const auto pair = detail::compute_permutation_masks_pair( tt, left, right, i ); + + masks.insert( masks.begin() + i, pair.second ); + masks.insert( masks.begin() + i, pair.first ); + } + + auto mask = tt.construct(); + for ( uint64_t i = 0u; i < ( static_cast( tt.num_bits() ) >> 1 ); ++i ) + { + if ( left[i] != right[i] ) + { + set_bit( mask, i ); + } + } + masks.insert( masks.begin() + tt.num_vars() - 1, mask ); + + return masks; +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/print.hpp b/third-party/mockturtle/lib/kitty/kitty/print.hpp new file mode 100644 index 00000000000..e7e0980391d --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/print.hpp @@ -0,0 +1,570 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file print.hpp + \brief Implements functions to print truth tables + + \author Mathias Soeken + \author Rassul Bairamkulov +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "algorithm.hpp" +#include "karnaugh_map.hpp" +#include "operations.hpp" +#include "constructors.hpp" +#include "isop.hpp" + +namespace kitty +{ + +namespace detail +{ + +inline std::string to_binary( uint16_t value, uint32_t num_vars ) +{ + std::string res( num_vars, '0' ); + auto it = res.end() - 1; + while ( value ) + { + if ( value & 1 ) + { + *it = '1'; + } + value >>= 1; + if ( it == res.begin() ) + { + break; + } + --it; + } + return res; +} + +inline void print_xmas_tree( + std::ostream& os, uint32_t num_vars, + const std::vector, + std::vector>>& style_predicates = {} ) +{ + /* create rows */ + std::vector> current( 1, { 0 } ), next; + + for ( auto i = 0u; i < num_vars; ++i ) + { + for ( const auto& row : current ) + { + if ( row.size() != 1u ) + { + next.emplace_back(); + + std::transform( row.begin() + 1, row.end(), + std::back_inserter( next.back() ), + []( auto cell ) + { return cell << 1; } ); + } + next.emplace_back( 1, row.front() << 1 ); + std::transform( row.begin(), row.end(), std::back_inserter( next.back() ), + []( auto cell ) + { return ( cell << 1 ) ^ 1; } ); + } + + std::swap( current, next ); + next.clear(); + } + + for ( const auto& row : current ) + { + /* white space padding to center columns */ + os << std::string( ( ( num_vars + 1 ) - row.size() ) / 2 * ( num_vars + 1 ), ' ' ); + for ( const auto& col : row ) + { + os << " "; + for ( const auto& pred : style_predicates ) + { + if ( pred.first( col ) ) + { + for ( auto style : pred.second ) + { + os << "\033[" << style << "m"; + } + } + } + os << to_binary( col, num_vars ) << "\033[0m"; + } + os << "\n"; + } +} + +} // namespace detail + +/*! \brief Prints truth table in binary representation + + The most-significant bit will be the first character of the string. + + \param tt Truth table + \param os Output stream +*/ +template +void print_binary( const TT& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + for_each_block_reversed( tt, [&os, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + if (word & 1) { + *it = '1'; + } + ++it; + word >>= 1; + } + os << chunk; } ); +} + +/*! \cond PRIVATE */ +inline void print_binary( const partial_truth_table& tt, + std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + bool first = true; + for_each_block_reversed( tt, [&tt, &os, chunk_size, &first]( auto word ) + { + std::string chunk( chunk_size, '0' ); + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + if (word & 1) { + *it = '1'; + } + ++it; + word >>= 1; + } + + if (first && (chunk_size == 64) && (tt.num_bits() % 64)) { + first = false; + os << chunk.substr(64 - (tt.num_bits() % 64)); + } else { + os << chunk; + } } ); +} +/*! \endcond */ + +template +void print_binary( const ternary_truth_table& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + std::string tt_string = ""; + for_each_block_reversed( tt._bits, [&tt_string, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + auto it = chunk.rbegin(); + while ( word && it != chunk.rend() ) + { + if ( word & 1 ) + { + *it = '1'; + } + ++it; + word >>= 1; + } + tt_string += chunk; } ); + for ( auto i = 0u; i < tt.num_bits(); i++ ) + { + if ( is_dont_care( tt, tt.num_bits() - 1 - i ) ) + { + tt_string[i] = '-'; + } + } + os << tt_string; +} + +template +void print_binary( const quaternary_truth_table& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + std::string tt_string = ""; + for_each_block_reversed( tt._onset, [&tt_string, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + auto it = chunk.rbegin(); + while ( word && it != chunk.rend() ) + { + if ( word & 1 ) + { + *it = '1'; + } + ++it; + word >>= 1; + } + tt_string += chunk; } ); + for ( auto i = 0u; i < tt.num_bits(); i++ ) + { + if ( is_dont_care( tt, tt.num_bits() - 1 - i ) ) + tt_string[i] = '-'; + if ( is_dont_know( tt, tt.num_bits() - 1 - i ) ) + tt_string[i] = 'x'; + } + os << tt_string; +} + +/*! \brief Prints K-map of given truth table. + + Columns represent the values of the least significant variables, rows of the + most significant ones. + + \param tt Truth table + \param os Output stream (default = cout) +*/ +template +void print_kmap( const TT& tt, std::ostream& os = std::cout ) +{ + karnaugh_map kmap( tt ); + kmap.print( os ); +} + +/*! \brief Prints truth table in hexadecimal representation + + The most-significant bit will be the first character of the string. + + \param tt Truth table + \param os Output stream +*/ +template::value>> +void print_hex( const TT& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = + std::min( tt.num_vars() <= 1 ? 1 : ( tt.num_bits() >> 2 ), 16 ); + + for_each_block_reversed( tt, [&os, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + auto hex = word & 0xf; + if (hex < 10) { + *it = '0' + static_cast(hex); + } else { + *it = 'a' + static_cast(hex - 10); + } + ++it; + word >>= 4; + } + os << chunk; } ); +} + +/*! \cond PRIVATE */ +inline void print_hex( const partial_truth_table& tt, + std::ostream& os = std::cout ) +{ + bool first = true; + for_each_block_reversed( tt, [&tt, &os, &first]( auto word ) + { + std::string chunk( 16, '0' ); + + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + auto hex = word & 0xf; + if (hex < 10) { + *it = '0' + static_cast(hex); + } else { + *it = 'a' + static_cast(hex - 10); + } + ++it; + word >>= 4; + } + + if (first && (tt.num_bits() % 64)) { + first = false; + os << chunk.substr((tt.num_bits() % 4) + ? (15 - ((tt.num_bits() >> 2) % 16)) + : (16 - ((tt.num_bits() >> 2) % 16))); + } else { + os << chunk; + } } ); +} +/*! \endcond */ + +/*! \brief Prints truth table in raw binary presentation (for file I/O) + + This function is useful to store large truth tables in binary files + or `std::stringstream`. Each word is stored into 8 characters. + + \param tt Truth table + \param os Output stream +*/ +template>::value>> +void print_raw( const TT& tt, std::ostream& os ) +{ + for_each_block( tt, [&os]( auto word ) + { os.write( reinterpret_cast( &word ), sizeof( word ) ); } ); +} + +/*! \brief Returns truth table as a string in binary representation + + Calls `print_binary` internally on a string stream. + + \param tt Truth table +*/ +template +inline std::string to_binary( const TT& tt ) +{ + std::stringstream st; + print_binary( tt, st ); + return st.str(); +} + +/*! \brief Returns truth table as a string in hexadecimal representation + + Calls `print_hex` internally on a string stream. + + \param tt Truth table +*/ +template::value>> +inline std::string to_hex( const TT& tt ) +{ + std::stringstream st; + print_hex( tt, st ); + return st.str(); +} + +/*! \brief Prints minterms of a Boolean function in christmas tree pattern + + This function prints all minterms of a Boolean function and arranges them + according to the christmas tree pattern as described in Section 7.2.1.6 in + The Art of Computer Programming by Donald E. Knuth. Minterms from the + off-set are printed in red, minterms from the on-set are printed in green. + + \param tt Truth table + \param os Output stream +*/ +template::value>, typename = std::enable_if_t::value>> + +void print_xmas_tree_for_function( const TT& tt, std::ostream& os = std::cout ) +{ + detail::print_xmas_tree( os, tt.num_vars(), + { { [&]( auto v ) + { return get_bit( tt, v ); }, + { 32 } }, + { [&]( auto v ) + { return !get_bit( tt, v ); }, + { 31 } } } ); +} + +/*! \brief Prints all Boolean functions of n variables in christmas tree pattern + + This function prints all Boolean functions of n variables and arranges them + according to the christmas tree pattern as described in Section 7.2.1.6 in + The Art of Computer Programming by Donald E. Knuth. Functions can be printed + in different styles according to some properties. + + \param tt Number of variables + \param style_predicates Each pair has a predicate `bool(TT const&)` to check + whether a certain property holds for the truth table + the element in the tree represents. If this predicate + evaluates to true, then the second element in the pair + are indexes of style (ANSI term) to change the + string in the output. + \param os Output stream +*/ +template +void print_xmas_tree_for_functions( + uint32_t num_vars, + const std::vector, + std::vector>>& style_predicates = {}, + std::ostream& os = std::cout ) +{ + + std::vector, std::vector>> + _preds; + std::transform( style_predicates.begin(), style_predicates.end(), + std::back_inserter( _preds ), [&]( const auto& p ) + { return std::make_pair( + [&]( uint16_t v ) + { + auto tt = create( num_vars ); + std::copy( &v, &v + 1, tt.begin() ); + return p.first( tt ); + }, + p.second ); } ); + + detail::print_xmas_tree( os, 1 << num_vars, _preds ); +} + +/*! \brief Creates an expression for an ANF form + * + * \param anf Truth table in ANF encoding + */ +template::value>> +std::string anf_to_expression( const TT& anf ) +{ + const auto terms = count_ones( anf ); + + if ( terms == 0u ) + { + return "0"; + } + + std::string expr; + + for_each_one_bit( anf, [&]( auto bit ) + { + if ( bit == 0 ) + { + + expr += "1"; + return; + } + auto weight = __builtin_popcount(static_cast(bit)); + if (weight != 1) { + expr += "("; + } + for (auto i = 0u; i < anf.num_vars(); ++i) { + if ((bit >> i) & 1) { + expr += std::string(1, 'a' + i); + } + } + if (weight != 1) { + expr += ")"; + } } ); + + return terms == 1 ? expr : "[" + expr + "]"; +} + +/*! \brief Creates an expression for an ANF form + + All don't cares are considered to be zero. + + It is a "restricted" ANF for the ternary turth table. + * + * \param anf Ternary truth table in ANF encoding + */ +template::value>> +std::string anf_to_expression( const ternary_truth_table& anf ) +{ + return anf_to_expression( anf._bits ); +} + +/*! \brief Creates an expression in the sum of products format. + + Does not perform any optimizations. Useful when constructing GENLIB-compatible expressions + + \param tt Truth table + \param os Output stream + */ +template::value>> +void print_sop_expression( TT tt, std::ostream& os = std::cout ) +{ + /* compare to constants */ + if ( is_const0( tt ) ) + { + os << "CONST0"; + return; + } + else if ( is_const0( ~tt ) ) + { + os << "CONST1"; + return; + } + + /* extract product terms */ + auto cubes = kitty::isop( tt ); + + bool first_cube = true; // Controls insertion of '|'. It's false for the first cube to avoid leading '|'. + + /* write product terms */ + for ( auto cube : cubes ) + { + auto bits = cube._bits; + auto mask = cube._mask; + + bool brackets = __builtin_popcount( mask ) > 1; + + if ( !first_cube ) + { + os << '|'; + } + + if ( brackets ) + { + os << '('; + } + + bool first_literal = true; + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( mask & 1 ) + { + if ( !first_literal ) + { + os << '&'; + } + if ( !( bits & 1 ) ) + { + os << '!'; + } + os << static_cast( 'a' + i ); + first_literal = false; + } + bits >>= 1; + mask >>= 1; + } + + if ( brackets ) + { + os << ')'; + } + first_cube = false; + } +} + +/*! \brief Creates an expression in the sum of products format. + + Does not perform any optimizations. Useful when constructing GENLIB-compatible expressions + + \param tt Truth table + */ +template::value>> +std::string to_sop_expression( TT tt ) +{ + std::stringstream os; + print_sop_expression( tt, os ); // Use the function to write into the stringstream + return os.str(); // Convert the stringstream to string and return it +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/properties.hpp b/third-party/mockturtle/lib/kitty/kitty/properties.hpp new file mode 100644 index 00000000000..4a969c55ad4 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/properties.hpp @@ -0,0 +1,374 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file properties.hpp + \brief Implements property checks for Boolean function + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "bit_operations.hpp" +#include "esop.hpp" +#include "operations.hpp" +#include "operators.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \brief Returns the Chow parameter of a function + The Chow parameters is a set of values \f$N(f), \Sigma(f)\f$, where \f$N(f)\f$ + is the size of the ON-set, and \f$\Sigma(f)\f$ is the sum of all input + assignments in the ON-set. For example for \f$f = x_1 \lor x_2\f$ the + function returns \f$(3, (2,2))\f$. + \param tt Truth table +*/ +template::value>> +std::pair> chow_parameters( const TT& tt ) +{ + assert( tt.num_vars() <= 32 ); + + const auto n = tt.num_vars(); + const auto nf = static_cast( count_ones( tt ) ); + + std::vector sf( n, 0u ); + for_each_one_bit( tt, [&sf]( auto minterm ) + { + for ( auto i = 0u; minterm; ++i ) + { + if ( minterm & 1 ) + { + ++sf[i]; + } + minterm >>= 1; + } } ); + + return { nf, sf }; +} + +/*! \brief Checks whether a function is canalizing + \param tt Truth table +*/ +template::value>> +bool is_canalizing( const TT& tt ) +{ + uint32_t f1or{}, f0or{}; + uint32_t f1and, f0and; + + uint32_t max = static_cast( ( uint64_t( 1 ) << tt.num_vars() ) - 1 ); + f1and = f0and = max; + + for ( uint32_t i = 0u; i < static_cast( tt.num_bits() ); ++i ) + { + if ( get_bit( tt, i ) == 0 ) + { + f0and &= i; + f0or |= i; + } + else + { + f1and &= i; + f1or |= i; + } + + if ( f0and == 0 && f1and == 0 && f0or == max && f1or == max ) + { + return false; + } + } + + return true; +} + +/*! \brief Checks whether a function is Horn + A function is Horn, if it can be represented using Horn clauses. + \param tt Truth table +*/ +template +bool is_horn( const TT& tt ) +{ + for ( uint32_t i = 1u; i < static_cast( tt.num_bits() ); ++i ) + { + for ( uint32_t j = 0u; j < i; ++j ) + { + if ( get_bit( tt, j ) && get_bit( tt, i ) && !get_bit( tt, i & j ) ) + { + return false; + } + } + } + + return true; +} + +/*! \brief Checks whether a function is Krom + A function is Krom, if it can be represented using Krom clauses. + \param tt Truth table +*/ +template +bool is_krom( const TT& tt ) +{ + for ( uint32_t i = 2u; i < static_cast( tt.num_bits() ); ++i ) + { + for ( uint32_t j = 1u; j < i; ++j ) + { + for ( uint32_t k = 0u; k < j; ++k ) + { + const auto maj = ( i & j ) | ( i & k ) | ( j & k ); + if ( get_bit( tt, k ) && get_bit( tt, j ) && get_bit( tt, i ) && !get_bit( tt, maj ) ) + { + return false; + } + } + } + } + + return true; +} + +/*! \brief Checks whether a function is symmetric in a pair of variables + A function is symmetric in two variables, if it is invariant to swapping them. + \param tt Truth table + \param var_index1 Index of first variable + \param var_index2 Index of second variable +*/ +template +bool is_symmetric_in( const TT& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + return tt == swap( tt, var_index1, var_index2 ); +} + +/*! \brief Checks whether a function is monotone + A function is monotone if f(x) ≤ f(y) whenever x ⊆ y + \param tt Truth table +*/ +template::value>> +bool is_monotone( const TT& tt ) +{ + auto numvars = tt.num_vars(); + + for ( auto i = 0u; i < numvars; i++ ) + { + auto const tt1 = cofactor0( tt, i ); + auto const tt2 = cofactor1( tt, i ); + for ( auto bit = 0; bit < ( 2 << ( numvars - 1 ) ); bit++ ) + { + if ( get_bit( tt1, bit ) <= get_bit( tt2, bit ) ) + { + continue; + } + else + { + return false; + } + } + } + return true; +} + +/*! \brief Checks whether a function is selfdual + A function is selfdual if !f(x, y, ..., z) = f(!x, !y, ..., !z) + \param tt Truth table +*/ +template::value>> +bool is_selfdual( const TT& tt ) +{ + auto numvars = tt.num_vars(); + auto tt1 = tt; + auto tt2 = ~tt1; + for ( auto i = 0u; i < numvars; i++ ) + { + tt1 = flip( tt1, i ); + } + + return tt2 == tt1; +} + +/*! \brief Checks if a function is normal + A function is normal iff f(0, ..., 0) = 0. + \param tt Truth table +*/ +template +bool is_normal( const TT& tt ) +{ + return !get_bit( tt, 0u ); +} + +/*! \brief Checks if a function is trivial + A function is trival if it is equal to (or the complement of) a + variable or constant zero. + \param tt Truth table +*/ +template::value>> +bool is_trivial( const TT& tt ) +{ + /* compare to constants */ + if ( is_const0( tt ) || is_const0( ~tt ) ) + return true; + + /* compare to variables */ + TT tt_check = tt; + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + create_nth_var( tt_check, i ); + if ( tt == tt_check || tt == ~tt_check ) + return true; + } + + return false; +} + +/*! \brief Generate runlength encoding of a function + This function iterates through the bits of a function and calls a function + for each runlength and value. For example, if this function is called for + the AND function 1000, it will call `fn` first with arguments `false` and `3`, + and then a second time with arguments `true`, and `1`. + \param tt Truth table + \param fn Function of signature `void(bool, uint32_t)` +*/ +template +void foreach_runlength( const TT& tt, Fn&& fn ) +{ + bool current = get_bit( tt, 0 ); + uint32_t length{ 1u }; + + for ( auto i = 1ull; i < tt.num_bits(); ++i ) + { + if ( static_cast( get_bit( tt, i ) ) != current ) + { + fn( current, length ); + current = !current; + length = 1u; + } + else + { + ++length; + } + } + + fn( current, length ); +} + +/*! \brief Returns the runlength encoding pattern of a function + This function does only count the lengths, e.g., for 1000 it will return + `{3, 1}`, and so it does for the NAND function 0111. + \param tt Truth table +*/ +template +std::vector runlength_pattern( const TT& tt ) +{ + std::vector pattern; + foreach_runlength( tt, [&]( bool, uint32_t length ) + { pattern.push_back( length ); } ); + return pattern; +} + +/*! \brief Compute polynomial degree + The polyomial degree is the number of variables in the largest monomial in + the functoons ANF (PPRM). + \param tt Truth table +*/ +template +inline uint32_t polynomial_degree( const TT& tt ) +{ + const auto cubes = esop_from_pprm( tt ); + if ( cubes.empty() ) /* zero function */ + { + return 0u; + } + const auto max = std::max_element( cubes.begin(), cubes.end(), + []( auto const& c1, auto const& c2 ) + { return c1.num_literals() < c2.num_literals(); } ); + return max->num_literals(); +} + +/*! \brief Returns the absolute distinguishing power of a function + The absolute distinguishing power of a function f is the number of + distinguishing bit pair {i,j} such that f(i) != f(j). + \param tt Truth table +*/ +template +inline uint64_t absolute_distinguishing_power( const TT& tt ) +{ + return count_zeros( tt ) * count_ones( tt ); +} + +/*! \brief Returns the relative distinguishing power of a function wrt. to a target function + Quantifies the number of distinguishing bit pairs in the target function + that can be distinguished by another function. + \param tt Truth table of function + \param target_tt Truth table of target function +*/ +template +inline uint64_t relative_distinguishing_power( const TT& tt, const TT& target_tt ) +{ + return count_ones( ~tt & ~target_tt ) * count_ones( tt & target_tt ) + count_ones( ~tt & target_tt ) * count_ones( tt & ~target_tt ); +} + +/*! \brief Return true iff each distinguishing bit pair of the target + function is also distinguishable by the divisor functions + \param target Truth table of the target functions + \param divisors Truth tables of the divisor functions +*/ +template +bool is_covered_with_divisors( TT const& target, std::vector const& divisors ) +{ + /* iterate over all bit pairs of the target function */ + for ( uint32_t j = 1u; j < target.num_bits(); ++j ) + { + for ( uint32_t i = 0u; i < j; ++i ) + { + /* check if the bit pair is distinguished by the target function */ + if ( get_bit( target, i ) != get_bit( target, j ) ) + { + /* check if this bit pair is also distinguished by a divisor function */ + bool found = false; + for ( const auto& d : divisors ) + { + if ( get_bit( d, i ) != get_bit( d, j ) ) + { + found = true; + break; + } + } + + if ( !found ) + { + return false; + } + } + } + } + return true; +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/quaternary_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/quaternary_truth_table.hpp new file mode 100644 index 00000000000..14996badd0f --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/quaternary_truth_table.hpp @@ -0,0 +1,265 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file quaternary_truth_table.hpp + \brief Implements quaternary_truth_table + + \author Siang-Yun Lee + \author Gianluca Radi +*/ + +#pragma once + +#include +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" + +namespace kitty +{ + +template +struct quaternary_truth_table +{ + /*! \brief Standard constructor. + + Initialize the truth table using the constructor of the inner + truth table type. + + \param n Number of variables or number of bits (when `TT = partial_truth_table`) + */ + explicit quaternary_truth_table( uint32_t n ) + : _onset( n ), _offset( n ) + { + } + + /*! \brief Empty constructor. + + Creates an empty truth table by calling the empty constructor + of the inner truth table type. + */ + quaternary_truth_table() : _onset(), _offset() {} + + /*! \brief Construct from onset and offset. + + Meanings of the values of (offset, onset) at each bit position: + 00 is a don't-know (x) or not involved in the cube, + 01 is a positive literal (1), 10 is a negative literal (0), and + 11 is a don't-care (-), meaning that both 0 and 1 are accepted. + + \param onset Onset truth table. + \param offset Offset truth table. + */ + quaternary_truth_table( TT const& onset, TT const& offset ) + : _onset( onset ), _offset( offset ) + { + } + + /*! \brief Construct from a binary truth table. + + Initialize the truth table as equivalent to a binary truth table. + (All bits are cared and known, being either `0` or `1`, without any + `-` or `x`.) + + \param binary Binary truth table. + */ + quaternary_truth_table( TT const& binary ) + : _onset( binary ), _offset( ~binary ) + { + } + + /*! Constructs a new quaternary_truth_table instance of the same size. */ + inline quaternary_truth_table construct() const + { + if constexpr ( std::is_same_v ) + return quaternary_truth_table( _onset.num_bits() ); + else + return quaternary_truth_table( _onset.num_vars() ); + } + + /*! Returns number of variables. + */ + template::value>> + auto num_vars() const noexcept { return _onset.num_vars(); } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return _onset.num_blocks(); } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return _onset.num_bits(); } + + /*! \brief Begin iterator to onset. + */ + inline auto begin_onset() noexcept { return _onset.begin(); } + + /*! \brief End iterator to onset. + */ + inline auto end_onset() noexcept { return _onset.end(); } + + /*! \brief Begin iterator to onset. + */ + inline auto begin_onset() const noexcept { return _onset.begin(); } + + /*! \brief End iterator to onset. + */ + inline auto end_onset() const noexcept { return _onset.end(); } + + /*! \brief Reverse begin iterator to onset. + */ + inline auto rbegin_onset() noexcept { return _onset.rbegin(); } + + /*! \brief Reverse end iterator to onset. + */ + inline auto rend_onset() noexcept { return _onset.rend(); } + + /*! \brief Constant begin iterator to onset. + */ + inline auto cbegin_onset() const noexcept { return _onset.cbegin(); } + + /*! \brief Constant end iterator to onset. + */ + inline auto cend_onset() const noexcept { return _onset.cend(); } + + /*! \brief Constant reverse begin iterator to onset. + */ + inline auto crbegin_onset() const noexcept { return _onset.crbegin(); } + + /*! \brief Constant teverse end iterator to onset. + */ + inline auto crend_onset() const noexcept { return _onset.crend(); } + + /*! \brief Begin iterator to offset. + */ + inline auto begin_offset() noexcept { return _offset.begin(); } + + /*! \brief End iterator to offset. + */ + inline auto end_offset() noexcept { return _offset.end(); } + + /*! \brief Begin iterator to offset. + */ + inline auto begin_offset() const noexcept { return _offset.begin(); } + + /*! \brief End iterator to offset. + */ + inline auto end_offset() const noexcept { return _offset.end(); } + + /*! \brief Reverse begin iterator to offset. + */ + inline auto rbegin_offset() noexcept { return _offset.rbegin(); } + + /*! \brief Reverse end iterator to offset. + */ + inline auto rend_offset() noexcept { return _offset.rend(); } + + /*! \brief Constant begin iterator to offset. + */ + inline auto cbegin_offset() const noexcept { return _offset.cbegin(); } + + /*! \brief Constant end iterator to offset. + */ + inline auto cend_offset() const noexcept { return _offset.cend(); } + + /*! \brief Constant reverse begin iterator to offset. + */ + inline auto crbegin_offset() const noexcept { return _offset.crbegin(); } + + /*! \brief Constant teverse end iterator to offset. + */ + inline auto crend_offset() const noexcept { return _offset.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. + The other truth table must also be quaternary, and the inner type of + the other truth table must be assignable to the inner type of this + truth table. + + \param other Other truth table + */ + template + quaternary_truth_table& operator=( const quaternary_truth_table& other ) + { + _onset = other._onset; + _offset = other._offset; + + return *this; + } + + /*! \brief Assigned by a binary truth table. + + Replaces with a truth table equivalent to a binary truth table. + (All bits are cared and known, being either `0` or `1`, without any + `-` or `x`.) + + \param other Binary truth table. + */ + template + quaternary_truth_table& operator=( TT const& other ) + { + _onset = other; + _offset = ~other; + + return *this; + } + + /*! \brief Masks valid truth table bits. + + This operation makes sure to zero out all unused bits. + */ + inline void mask_bits() noexcept + { + _onset.mask_bits(); + _offset.mask_bits(); + } + + /*! \cond PRIVATE */ +public: /* fields */ + TT _onset; + TT _offset; + /*! \endcond */ +}; + +template +struct is_truth_table> : is_truth_table +{ +}; + +template +struct is_complete_truth_table> : is_complete_truth_table +{ +}; + +template +struct is_completely_specified_truth_table> : std::false_type +{ +}; + +} // namespace kitty diff --git a/third-party/mockturtle/lib/kitty/kitty/spectral.hpp b/third-party/mockturtle/lib/kitty/kitty/spectral.hpp new file mode 100644 index 00000000000..3451e972e42 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/spectral.hpp @@ -0,0 +1,1204 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * Copyright (C) 2017-2025 University of Victoria + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file spectral.hpp + \brief Implements methods for spectral classification + + Original implementation by D. Michael Miller (University of + Victoria, BC, Canada). Modified C++ implementation and integration + into kitty by Mathias Soeken. + + \author D. Michael Miller + \author Mathias Soeken +*/ + +#pragma once + +#include "bit_operations.hpp" +#include "constructors.hpp" +#include "esop.hpp" +#include "detail/mscfix.hpp" +#include "traits.hpp" + +#include +#include +#include +#include + +namespace kitty +{ + +namespace detail +{ +struct spectral_operation +{ + enum class kind : uint16_t + { + none, + permutation, + input_negation, + output_negation, + spectral_translation, + disjoint_translation + }; + + spectral_operation() = default; + explicit spectral_operation( kind _kind, uint16_t _var1 = 0, uint16_t _var2 = 0 ) : _kind( _kind ), _var1( _var1 ), _var2( _var2 ) {} + + kind _kind{ kind::none }; + uint16_t _var1{ 0 }; + uint16_t _var2{ 0 }; +}; + +inline void fast_hadamard_transform( std::vector& s, bool reverse = false ) +{ + unsigned k{}; + int t{}; + + for ( auto m = 1u; m < s.size(); m <<= 1u ) + { + for ( auto i = 0u; i < s.size(); i += ( m << 1u ) ) + { + for ( auto j = i, p = k = i + m; j < p; ++j, ++k ) + { + t = s[j]; + s[j] += s[k]; + s[k] = t - s[k]; + } + } + } + + if ( reverse ) + { + for ( auto i = 0u; i < s.size(); ++i ) + { + s[i] /= static_cast( s.size() ); + } + } +} + +class spectrum +{ +public: + spectrum() = delete; + ~spectrum() = default; + + spectrum( const spectrum& other ) : _s( std::begin( other._s ), std::end( other._s ) ) {} + spectrum( spectrum&& other ) noexcept : _s( std::move( other._s ) ) {} + + spectrum& operator=( const spectrum& other ) + { + if ( this != &other ) + { + _s = other._s; + } + return *this; + } + + spectrum& operator=( spectrum&& other ) noexcept + { + _s = std::move( other._s ); + return *this; + } + +private: + explicit spectrum( std::vector _s ) : _s( std::move( _s ) ) {} + +public: + template + static spectrum from_truth_table( const TT& tt ) + { + std::vector _s( tt.num_bits(), 1 ); + for_each_one_bit( tt, [&_s]( auto bit ) + { _s[bit] = -1; } ); + fast_hadamard_transform( _s ); + return spectrum( _s ); + } + + template + void to_truth_table( TT& tt ) const + { + auto copy = _s; + fast_hadamard_transform( copy, true ); + + clear( tt ); + for ( auto i = 0u; i < copy.size(); ++i ) + { + if ( copy[i] == -1 ) + { + set_bit( tt, i ); + } + } + } + + auto permutation( unsigned i, unsigned j ) + { + spectral_operation op( spectral_operation::kind::permutation, i, j ); + + for ( auto k = 0u; k < _s.size(); ++k ) + { + if ( ( k & i ) > 0 && ( k & j ) == 0 ) + { + std::swap( _s[k], _s[k - i + j] ); + } + } + + return op; + } + + auto input_negation( unsigned i ) + { + spectral_operation op( spectral_operation::kind::input_negation, i ); + for ( auto k = 0u; k < _s.size(); ++k ) + { + if ( ( k & i ) > 0 ) + { + _s[k] = -_s[k]; + } + } + + return op; + } + + auto output_negation() + { + for ( auto& coeff : _s ) + { + coeff = -coeff; + } + return spectral_operation( spectral_operation::kind::output_negation ); + } + + auto spectral_translation( int i, int j ) + { + spectral_operation op( spectral_operation::kind::spectral_translation, i, j ); + + for ( auto k = 0u; k < _s.size(); ++k ) + { + if ( ( k & i ) > 0 && ( k & j ) == 0 ) + { + std::swap( _s[k], _s[k + j] ); + } + } + + return op; + } + + auto disjoint_translation( int i ) + { + spectral_operation op( spectral_operation::kind::disjoint_translation, i ); + + for ( auto k = 0u; k < _s.size(); ++k ) + { + if ( ( k & i ) > 0 ) + { + std::swap( _s[k], _s[k - i] ); + } + } + + return op; + } + + void apply( const spectral_operation& op ) + { + switch ( op._kind ) + { + case spectral_operation::kind::none: + assert( false ); + break; + case spectral_operation::kind::permutation: + permutation( op._var1, op._var2 ); + break; + case spectral_operation::kind::input_negation: + input_negation( op._var1 ); + break; + case spectral_operation::kind::output_negation: + output_negation(); + break; + case spectral_operation::kind::spectral_translation: + spectral_translation( op._var1, op._var2 ); + break; + case spectral_operation::kind::disjoint_translation: + disjoint_translation( op._var1 ); + break; + } + } + + inline auto operator[]( std::vector::size_type pos ) + { + return _s[pos]; + } + + inline auto operator[]( std::vector::size_type pos ) const + { + return _s[pos]; + } + + inline auto size() const + { + return _s.size(); + } + + inline auto cbegin() const + { + return _s.cbegin(); + } + + inline auto cend() const + { + return _s.cend(); + } + + void print( std::ostream& os, const std::vector& order ) const + { + os << std::setw( 4 ) << _s[order.front()]; + + for ( auto it = order.begin() + 1; it != order.end(); ++it ) + { + os << " " << std::setw( 4 ) << _s[*it]; + } + } + + inline const auto& coefficients() const + { + return _s; + } + +private: + std::vector _s; +}; + +inline std::vector get_rw_coeffecient_order( uint32_t num_vars ) +{ + auto size = uint32_t( 1 ) << num_vars; + std::vector map( size, 0u ); + auto p = std::begin( map ) + 1; + + for ( uint32_t i = 1u; i <= num_vars; ++i ) + { + for ( uint32_t j = 1u; j < size; ++j ) + { + if ( __builtin_popcount( j ) == static_cast( i ) ) + { + *p++ = j; + } + } + } + + return map; +} + +template +class miller_spectral_canonization_impl +{ +public: + explicit miller_spectral_canonization_impl( const TT& func, bool hasInputNegation = true, bool hasOutputNegation = true, bool hasDisjointTranslation = true ) + : func( func ), + num_vars( func.num_vars() ), + num_vars_exp( 1 << num_vars ), + spec( spectrum::from_truth_table( func ) ), + best_spec( spec ), + transforms( 100u ), + hasInputNegation( hasInputNegation ), + hasOutputNegation( hasOutputNegation ), + hasDisjointTranslation( hasDisjointTranslation ) + { + } + + template + std::pair run( Callback&& fn ) + { + order = get_rw_coeffecient_order( num_vars ); + const auto exact = normalize(); + + fn( best_transforms ); + + TT tt = func.construct(); + spec.to_truth_table( tt ); + return { tt, exact }; + } + + void set_limit( unsigned limit ) + { + step_limit = limit; + } + +private: + unsigned transformation_costs( const std::vector& transforms ) + { + auto costs = 0u; + for ( const auto& t : transforms ) + { + costs += ( t._kind == spectral_operation::kind::permutation ) ? 3u : 1u; + } + return costs; + } + + void closer( spectrum& lspec ) + { + for ( auto i = 0u; i < lspec.size(); ++i ) + { + const auto j = order[i]; + if ( lspec[j] == best_spec[j] ) + { + continue; + } + if ( abs( lspec[j] ) > abs( best_spec[j] ) || + ( abs( lspec[j] ) == abs( best_spec[j] ) && lspec[j] > best_spec[j] ) ) + { + update_best( lspec ); + return; + } + + if ( abs( lspec[j] ) < abs( best_spec[j] ) || + ( abs( lspec[j] ) == abs( best_spec[j] ) && lspec[j] < best_spec[j] ) ) + { + return; + } + } + + if ( transformation_costs( transforms ) < transformation_costs( best_transforms ) ) + { + update_best( lspec ); + } + } + + bool normalize_rec( spectrum& lspec, unsigned v ) + { + if ( ++step_counter == step_limit ) + return false; + + if ( v == num_vars_exp ) /* leaf case */ + { + /* invert function if necessary */ + if ( hasOutputNegation && lspec[0u] < 0 ) + { + insert( lspec.output_negation() ); + } + /* invert any variable as necessary */ + if ( hasInputNegation ) + { + for ( auto i = 1u; i < num_vars_exp; i <<= 1 ) + { + if ( lspec[i] < 0 ) + { + insert( lspec.input_negation( i ) ); + } + } + } + + closer( lspec ); + return true; + } + + auto min = 0, max = 0; + const auto p = std::accumulate( lspec.cbegin() + v, lspec.cend(), + std::make_pair( min, max ), + []( auto a, auto sv ) + { + return std::make_pair( std::min( a.first, abs( sv ) ), std::max( a.second, abs( sv ) ) ); + } ); + min = p.first; + max = p.second; + + if ( max == 0 ) + { + auto& spec2 = specs.at( num_vars_exp ); + spec2 = lspec; + if ( !normalize_rec( spec2, num_vars_exp ) ) + return false; + } + else + { + for ( auto i = 1u; i < lspec.size(); ++i ) + { + auto j = order[i]; + if ( abs( lspec[j] ) != max ) + { + continue; + } + + /* k = first one bit in j starting from pos v */ + auto k = j & ~( v - 1 ); /* remove 1 bits until v */ + if ( k == 0 ) + { + continue; /* are there bit left? */ + } + k = k - ( k & ( k - 1 ) ); /* extract lowest bit */ + j ^= k; /* remove bit k from j */ + + auto& spec2 = specs.at( v << 1 ); + spec2 = lspec; + + const auto save = transform_index; + + /* spectral translation to all other 1s in j */ + while ( j ) + { + auto p = j - ( j & ( j - 1 ) ); + insert( spec2.spectral_translation( k, p ) ); + j ^= p; + } + + if ( k != v ) + { + insert( spec2.permutation( k, v ) ); + } + + if ( !normalize_rec( spec2, v << 1 ) ) + return false; + + if ( v == 1 && min == max ) + { + return true; + } + transform_index = save; + } + } + + return true; + } + + bool normalize() + { + /* find maximum absolute element index in spectrum (by order) */ + auto j = *std::max_element( order.cbegin(), order.cend(), [this]( auto p1, auto p2 ) + { return abs( spec[p1] ) < abs( spec[p2] ); } ); + + /* if max element is not the first element */ + if ( j ) + { + auto k = j - ( j & ( j - 1 ) ); /* LSB of j */ + j ^= k; /* delete bit in j */ + + while ( j ) + { + auto p = j - ( j & ( j - 1 ) ); /* next LSB of j */ + j ^= p; /* delete bit in j */ + insert( spec.spectral_translation( k, p ) ); + } + if ( hasDisjointTranslation ) + { + insert( spec.disjoint_translation( k ) ); + } + } + + for ( auto v = 1u; v <= num_vars_exp; v <<= 1 ) + { + specs.insert( { v, spec } ); + } + + update_best( spec ); + const auto result = normalize_rec( spec, 1 ); + spec = best_spec; + return result; + } + + void insert( const spectral_operation& trans ) + { + if ( transform_index >= transforms.size() ) + { + transforms.resize( transforms.size() << 1 ); + } + assert( transform_index < transforms.size() ); + transforms[transform_index++] = trans; + } + + void update_best( const spectrum& lspec ) + { + best_spec = lspec; + best_transforms.resize( transform_index ); + std::copy( transforms.begin(), transforms.begin() + transform_index, best_transforms.begin() ); + } + + std::ostream& print_spectrum( std::ostream& os ) + { + os << "[i]"; + + for ( auto i = 0u; i < spec.size(); ++i ) + { + auto j = order[i]; + if ( j > 0 && __builtin_popcount( order[i - 1] ) < __builtin_popcount( j ) ) + { + os << " |"; + } + os << " " << std::setw( 3 ) << spec[j]; + } + return os << std::endl; + } + +private: + const TT& func; + + unsigned num_vars; + unsigned num_vars_exp; + spectrum spec; + spectrum best_spec; + std::unordered_map specs; + + std::vector order; + std::vector transforms; + std::vector best_transforms; + unsigned transform_index{ 0u }; + + unsigned step_counter{ 0u }; + unsigned step_limit{ 0u }; + + bool hasInputNegation{ true }; + bool hasOutputNegation{ true }; + bool hasDisjointTranslation{ true }; +}; + +inline void exact_spectral_canonization_null_callback( const std::vector& operations ) +{ + (void)operations; +} +} /* namespace detail */ + +/*! \brief Exact spectral canonization + + The function can be passed as second argument a callback that is called with a + vector of spectral operations necessary to transform the input function into + the representative. + + \param tt Truth table + \param fn Callback to retrieve list of transformations (optional) + */ +template +inline TT exact_spectral_canonization( const TT& tt, Callback&& fn = detail::exact_spectral_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + detail::miller_spectral_canonization_impl impl( tt ); + return impl.run( fn ).first; +} + +/*! \brief Exact spectral canonization (with recursion limit) + + This function gets as additional argument a recursion limit. The canonization + is stopped once this limit is reached and the current representative at this + point is being returned. The return value is a pair, where the first + entry is the representative, and the second entry indicates whether the + solution is known to be exact or not. + + A function can be passed as second argument that is callback called with a + vector of spectral operations necessary to transform the input function into + the representative. + + \param tt Truth table + \param limit Recursion limit + \param fn Callback to retrieve list of transformations (optional) + */ +template +inline std::pair exact_spectral_canonization_limit( const TT& tt, unsigned step_limit, Callback&& fn = detail::exact_spectral_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + detail::miller_spectral_canonization_impl impl( tt ); + impl.set_limit( step_limit ); + return impl.run( fn ); +} + +namespace detail +{ + +template +struct anf_spectrum +{ +public: + explicit anf_spectrum( const TT& tt ) + : _anf( detail::algebraic_normal_form( tt ) ), + _khots( tt.num_vars() + 1, tt.construct() ) + { + for ( auto i = 0u; i <= tt.num_vars(); ++i ) + { + create_equals( _khots[i], i ); + } + } + +private: + auto permutation( unsigned i, unsigned j ) + { + spectral_operation op( spectral_operation::kind::permutation, 1 << i, 1 << j ); + + swap_inplace( _anf, i, j ); + + return op; + } + + auto input_negation( unsigned i ) + { + spectral_operation op( spectral_operation::kind::input_negation, 1 << i ); + + _anf ^= cofactor1( _anf, i ) & nth_var( _anf.num_vars(), i, true ); + + return op; + } + + auto output_negation() + { + flip_bit( _anf, 0 ); + + return spectral_operation( spectral_operation::kind::output_negation ); + } + + auto spectral_translation( int i, int j ) + { + spectral_operation op( spectral_operation::kind::spectral_translation, 1 << i, 1 << j ); + + _anf ^= ( cofactor1( _anf, i ) ^ cofactor0( cofactor1( _anf, i ), j ) ) & nth_var( _anf.num_vars(), j ) & nth_var( _anf.num_vars(), i, true ); + + return op; + } + + auto disjoint_translation( int i ) + { + spectral_operation op( spectral_operation::kind::disjoint_translation, 1 << i ); + + flip_bit( _anf, UINT64_C( 1 ) << i ); + + return op; + } + + template + void foreach_term_of_size( uint32_t k, Fn&& fn, int64_t start = 0 ) + { + auto term = find_first_one_bit( _anf & _khots[k], start ); + + while ( term != -1 ) + { + fn( term ); + term = find_first_one_bit( _anf & _khots[k], term + 1 ); + } + } + + void insert( const spectral_operation& op ) + { + _transforms.push_back( op ); + } + +public: + bool classify() + { + bool repeat = true; + auto rounds = 0u; + while ( repeat && rounds++ < 2u ) + { + if ( count_ones( _anf ) < 2 ) + { + break; + } + + repeat = false; + for ( auto k = 2u; k < _anf.num_vars(); ++k ) + { + foreach_term_of_size( k, [&]( auto term ) + { + auto mask = _anf.construct(); + create_from_cubes( mask, {cube( static_cast( term ), static_cast( term ) )} ); + + const auto i = find_first_one_bit( _anf & _khots[k + 1] & mask ); + if ( i != -1 ) + { + insert( input_negation( detail::log2[i & ~term] ) ); + repeat = true; + } + else + { + foreach_term_of_size( k, [&]( auto term2 ) { + if ( __builtin_popcount( static_cast( term ^ term2 ) ) == 2 ) + { + const auto vari = detail::log2[term & ~term2]; + const auto varj = detail::log2[term2 & ~term]; + + insert( spectral_translation( vari, varj ) ); + repeat = true; + } + }, term + 1 ); + } } ); + } + } + + if ( get_bit( _anf, 0 ) ) + { + insert( output_negation() ); + } + + for ( auto i = 0u; i < _anf.num_vars(); ++i ) + { + if ( get_bit( _anf, UINT64_C( 1 ) << i ) ) + { + insert( disjoint_translation( i ) ); + } + } + + auto mask = 0u; + bool disjoint = true; + for_each_one_bit( _anf, [&]( auto word ) + { + if ( word & mask ) { + disjoint = false; + } else { + mask |= word; + } } ); + + if ( disjoint ) + { + auto dest_var = 0; + for ( auto k = 2u; k < _anf.num_vars(); ++k ) + { + foreach_term_of_size( k, [&]( auto term ) + { + while ( term != 0 ) + { + const auto vari = detail::log2[term & ~( term - 1 )]; + + // swap i with dest_var + if ( vari != dest_var ) + { + insert( permutation( vari, dest_var ) ); + } + ++dest_var; + + term &= ( term - 1 ); + } } ); + } + + return true; + } + + return false; + } + + const TT& anf() const + { + return _anf; + } + + TT truth_table() const + { + return detail::algebraic_normal_form( _anf ); + } + + template + void get_transformations( Callback&& fn ) const + { + fn( _transforms ); + } + +private: + TT _anf; + std::vector _khots; + std::vector _transforms; +}; + +} // namespace detail + +/*! \brief Exact spectral canonization (with Reed-Muller preprocessor) + + A function can be passed as second argument that is callback called with a + vector of spectral operations necessary to transform the input function into + the representative. + + The algorithm is based on the paper: M. Soeken, E. Testa, D.M. Miller: A + hybrid spectral method for checking Boolean function equivalence, PACRIM 2019. + + \param tt Truth table + \param fn Callback to retrieve list of transformations (optional) + */ +template +inline TT hybrid_exact_spectral_canonization( const TT& tt, Callback&& fn = detail::exact_spectral_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + (void)fn; + + std::vector transforms; + + detail::anf_spectrum anf( tt ); + + bool is_disjoint = anf.classify(); + anf.get_transformations( [&]( const auto& t ) + { std::copy( t.begin(), t.end(), std::back_inserter( transforms ) ); } ); + + if ( !is_disjoint ) + { + const auto miller_repr = exact_spectral_canonization( anf.truth_table(), [&]( const auto& t ) + { std::copy( t.begin(), t.end(), std::back_inserter( transforms ) ); } ); + detail::anf_spectrum anf_post( miller_repr ); + anf_post.classify(); + anf_post.get_transformations( [&]( const auto& t ) + { std::copy( t.begin(), t.end(), std::back_inserter( transforms ) ); } ); + fn( transforms ); + return anf_post.truth_table(); + } + else + { + fn( transforms ); + return anf.truth_table(); + } +} + +/*! \brief Print spectral representation of a function in RW order + + \param tt Truth table + \param os Output stream + */ +template +inline void print_spectrum( const TT& tt, std::ostream& os = std::cout ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto spectrum = detail::spectrum::from_truth_table( tt ); + spectrum.print( os, detail::get_rw_coeffecient_order( tt.num_vars() ) ); +} + +/*! \brief Returns the Rademacher-Walsh spectrum of a truth table + + The order of coefficients is in the same order as input assignments + to the truth table. + + \param tt Truth table + */ +template +inline std::vector rademacher_walsh_spectrum( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto spectrum = detail::spectrum::from_truth_table( tt ); + return spectrum.coefficients(); +} + +/*! \brief Returns the autocorrelation spectrum of a truth table + + The autocorrelation spectrum gives an indication of the imbalance of all first + order derivates of a Boolean function. + + The spectral coefficient \f$r(x)\f$ for input \f$x \in \{0,1\}^n\f$ is + computed by \f$r(x) = \sum_{y\in\{0,1\}^n}\hat f(y)\hat f(x \oplus y)\f$, + where \f$\hat f\f$ is the \f$\{-1,1\}\f$ encoding of the input function + \f$f\f$. + + \param tt Truth table for input function \f$f\f$ + */ +template +inline std::vector autocorrelation_spectrum( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector spectrum( 1, static_cast( tt.num_bits() ) ); + spectrum.reserve( tt.num_bits() ); + + for ( uint64_t i = 1; i < tt.num_bits(); ++i ) + { + int32_t sum{ 0 }; + for ( uint64_t j = 0; j < tt.num_bits(); ++j ) + { + sum += ( get_bit( tt, j ) ? -1 : 1 ) * ( get_bit( tt, i ^ j ) ? -1 : 1 ); + } + + spectrum.push_back( sum ); + } + + return spectrum; +} + +/*! \brief Returns distribution of absolute spectrum coefficients + + This functions returns a vector with \f$2^{n-1} + 1\f$ nonnegative entries, + in which the entry at position \f$i\f$ indicates how many coeffecients in + the spectum have absolute value \f$2i\f$. The compression is possible, since + all spectra in this package have positive coefficients. + + \param spectrum Spectrum +*/ +inline std::vector spectrum_distribution( const std::vector& spectrum ) +{ + std::vector dist( spectrum.size() / 2 + 1 ); + + for ( auto c : spectrum ) + { + dist[abs( c ) >> 1]++; + } + + return dist; +} + +/*! \brief Returns unique index for a spectral equivalence class + + This functions works for functions with up to 5 inputs. It uses the + distribution of coefficients in the Rademacher Walsh spectrum, and in case + of ambiguity also the coeffcienents in the auto-correlation spectrum. It + does not compute the transformation sequences in order to get to the + class representative and is therefore faster than canoninization. + + \param tt Truth table +*/ +template +inline uint32_t get_spectral_class( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + assert( tt.num_vars() <= 5 ); + + const auto rwd = spectrum_distribution( rademacher_walsh_spectrum( tt ) ); + + switch ( tt.num_vars() ) + { + case 0u: + case 1u: + return 0; + case 2u: + return rwd[2] ? 0 : 1; + break; + case 3u: + { + if ( rwd[4] ) + return 0; + else if ( rwd[3] ) + return 1; + else if ( rwd[2] ) + return 2; + } + case 4u: + { + if ( rwd[8] ) + return 0; + else if ( rwd[7] ) + return 1; + else if ( rwd[6] ) + return 2; + else if ( rwd[5] ) + return 3; + else if ( rwd[4] ) + return rwd[4] == 4 ? 4 : 5; + else if ( rwd[3] ) + return 6; + else if ( rwd[2] ) + return 7; + } + break; + + case 5u: + { + if ( rwd[16] ) + return 0; + else if ( rwd[15] ) + return 1; + else if ( rwd[14] ) + return 2; + else if ( rwd[13] ) + return 3; + else if ( rwd[12] ) + return rwd[4] == 7 ? 4 : 5; + else if ( rwd[11] ) + return rwd[5] == 1 ? 6 : 7; + else if ( rwd[10] ) + { + switch ( rwd[2] ) + { + case 30: + return 8; + case 15: + return 9; + case 14: + return 10; + default: + return 11; + } + } + else if ( rwd[9] ) + { + switch ( rwd[3] ) + { + case 15: + return 12; + case 12: + return 13; + case 9: + return 14; + case 6: + return 15; + default: + return 16; + } + } + else if ( rwd[8] ) + { + switch ( rwd[0] ) + { + case 28: + return 17; + case 19: + return 18; + case 22: + return 19; + case 7: + return 20; + case 9: + return 21; + case 10: + return 22; + case 11: + return 23; + default: + return 24; + } + } + else if ( rwd[7] ) + { + switch ( rwd[1] ) + { + case 15: + { + const auto acd = spectrum_distribution( autocorrelation_spectrum( tt ) ); + return acd[2] == 22 ? 25 : 26; + } + break; + case 18: + return 27; + case 19: + return 28; + case 21: + return 29; + default: + return 30; + } + } + else if ( rwd[6] ) + { + switch ( rwd[2] ) + { + case 28: + { + const auto acd = spectrum_distribution( autocorrelation_spectrum( tt ) ); + return acd[0] == 12 ? 31 : 32; + } + break; + case 15: + return 33; + case 14: + { + const auto acd = spectrum_distribution( autocorrelation_spectrum( tt ) ); + return acd[0] == 15 ? 34 : 35; + } + break; + case 13: + return 36; + case 12: + { + const auto acd = spectrum_distribution( autocorrelation_spectrum( tt ) ); + return acd[0] == 18 ? 37 : 38; + } + break; + default: + return 39; + } + } + else if ( rwd[5] ) + { + switch ( rwd[3] ) + { + case 16: + return 40; + default: + { + const auto acd = spectrum_distribution( autocorrelation_spectrum( tt ) ); + switch ( acd[2] ) + { + case 25: + return 41; + case 27: + return 42; + default: + return 43; + } + } + break; + } + } + else if ( rwd[4] ) + { + switch ( rwd[0] ) + { + case 16: + { + const auto acd = spectrum_distribution( autocorrelation_spectrum( tt ) ); + switch ( acd[0] ) + { + case 30: + return 44; + case 15: + return 45; + default: + return 46; + } + } + break; + default: + return 47; + } + } + } + break; + } + + return 48; +} + +namespace detail +{ +static std::vector spectral_repr[] = { + { 0x0 }, + { 0x0 }, + { 0x0, 0x8 }, + { 0x00, 0x80, 0x88 }, + { 0x0000, 0x8000, 0x8080, 0x0888, 0x8888, 0x2a80, 0xf888, 0x7888 }, + { 0x00000000, 0x80000000, 0x80008000, 0x00808080, 0x80808080, 0x08888000, 0xaa2a2a80, 0x88080808, 0x2888a000, 0xf7788000, 0xa8202020, 0x08880888, 0xbd686868, 0xaa808080, 0x7e686868, 0x2208a208, 0x08888888, 0x88888888, 0xea404040, 0x2a802a80, 0x73d28c88, 0xea808080, 0xa28280a0, 0x13284c88, 0xa2220888, 0xaae6da80, 0x58d87888, 0x8c88ac28, 0x8880f880, 0x9ee8e888, 0x4268c268, 0x16704c80, 0x78888888, 0x4966bac0, 0x372840a0, 0x5208d288, 0x7ca00428, 0xf8880888, 0x2ec0ae40, 0xf888f888, 0x58362ec0, 0x0eb8f6c0, 0x567cea40, 0xf8887888, 0x78887888, 0xe72890a0, 0x268cea40, 0x6248eac0 } }; +} + +/*! \brief Returns spectral representative using lookup + * + * This function returns the spectral representative for functions with up to 5 + * variables, but does not give the possibility to obtain the transformation + * sequence to obtain the function from the representative. + * + * \brief func Truth table for function with at most 5 variables. + */ +template +TT spectral_representative( const TT& func ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + auto r = func.construct(); + const auto index = get_spectral_class( func ); + const auto word = detail::spectral_repr[func.num_vars()][index]; + kitty::create_from_words( r, &word, &word + 1 ); + return r; +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/spp.hpp b/third-party/mockturtle/lib/kitty/kitty/spp.hpp new file mode 100644 index 00000000000..1c4ac9defd3 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/spp.hpp @@ -0,0 +1,156 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file spp.hpp + \brief Implements methods to compute SPP representations + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "cube.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \brief Merges products in an ESOP into pseudo products + + Given, e.g., the two cubes `abc` and `abd`, this algorithm will combine them + into a single cube `ab(c+d)`. The term `(c+d)` is stored as a literal in the + first argument of the result pair, starting free indexes after `num_vars`. + Which literals are used is stored in the second argument of the result pair, + as bitpattern over the original inputs. + + \param esop ESOP form + \param num_vars Number of variables in ESOP form +*/ +inline std::pair, std::vector> simple_spp( const std::vector& esop, uint32_t num_vars ) +{ + auto next_free = num_vars; + std::vector sums; + auto e = esop.size(); + auto copy = esop; + const auto var_mask = ( 1u << num_vars ) - 1; + for ( auto i = 0u; i < e; ++i ) + { + auto& c = copy[i]; + if ( ( c._mask & 0b1111 ) != c._mask ) + { + continue; + } + + const auto it = std::find_if( copy.begin() + i, copy.begin() + e, + [&]( auto const& c2 ) + { + bool cnd1 = ( c2._mask & var_mask ) == c2._mask; + const auto same_mask = c._mask & c2._mask; + bool cnd2 = ( c._bits & same_mask ) == ( c2._bits & same_mask ); + bool cnd3 = __builtin_popcount( c._mask & ~c2._mask ) == 1; + bool cnd4 = __builtin_popcount( ~c._mask & c2._mask ) == 1; + + return cnd1 && cnd2 && cnd3 && cnd4; + } ); + if ( it != copy.begin() + e ) + { + auto to_delete = c._mask ^ it->_mask; + c._mask &= ~to_delete; + c.add_literal( next_free++, __builtin_popcount( ( c._bits | it->_bits ) & to_delete ) % 2 == 0 ); + c._bits &= ~to_delete; + sums.push_back( to_delete ); + --e; + std::swap( *it, copy[e] ); + } + } + + copy.resize( e ); + return { copy, sums }; +} + +/*! \brief Creates truth table from SPP + + This method is helpful to check which truth table is computed by an SPP form. +*/ +template +void create_from_spp( TT& tt, const std::vector& cubes, const std::vector& sums ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + clear( tt ); + + for ( auto cube : cubes ) + { + auto product = ~tt.construct(); /* const1 of same size */ + + auto bits = cube._bits; + auto mask = cube._mask; + + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( mask & 1 ) + { + auto var = tt.construct(); + create_nth_var( var, i, !( bits & 1 ) ); + product &= var; + } + bits >>= 1; + mask >>= 1; + } + + for ( auto i = 0u; i < sums.size(); ++i ) + { + if ( mask & 1 ) + { + auto ssum = tt.construct(); + for ( auto j = 0u; j < tt.num_vars(); ++j ) + { + if ( ( sums[i] >> j ) & 1 ) + { + auto var = tt.construct(); + create_nth_var( var, j ); + ssum ^= var; + } + } + if ( !( bits & 1 ) ) + { + ssum = ~ssum; + } + product &= ssum; + } + bits >>= 1; + mask >>= 1; + } + + tt ^= product; + } +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/static_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/static_truth_table.hpp new file mode 100644 index 00000000000..437ba8af0b8 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/static_truth_table.hpp @@ -0,0 +1,290 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file static_truth_table.hpp + \brief Implements static_truth_table + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! Truth table in which number of variables is known at compile time. + + We dispatch on the Boolean template parameter to distinguish between + a small truth table (up to 6 variables) and a large truth table + (more than 6 variables). A small truth table fits into a single + block and therefore dedicated optimizations are possible. + */ +template +struct static_truth_table; + +/*! Truth table (for up to 6 variables) in which number of variables is known at compile time. + */ +template +struct static_truth_table +{ + /*! \cond PRIVATE */ + enum + { + NumBits = uint64_t( 1 ) << NumVars + }; + /*! \endcond */ + + /*! Constructs a new static truth table instance with the same number of variables. */ + inline static_truth_table construct() const + { + return static_truth_table(); + } + + /*! Returns number of variables. + */ + inline auto num_vars() const noexcept { return NumVars; } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return 1u; } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return NumBits; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return &_bits; } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return ( &_bits ) + 1; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return &_bits; } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return &_bits; } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return ( &_bits ) + 1; } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return &_bits; } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return &_bits; } + + /*! \brief Constant everse end iterator to bits. + */ + inline auto crend() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Assign other truth table if number of variables match. + + This replaces the current truth table with another truth table, if `other` + has the same number of variables. Otherwise, the truth table is not + changed. + + \param other Other truth table + */ + template::value && is_complete_truth_table::value>> + static_truth_table& operator=( const TT& other ) + { + if ( other.num_vars() == num_vars() ) + { + std::copy( other.begin(), other.end(), begin() ); + } + + return *this; + } + + /*! Masks the number of valid truth table bits. + + If the truth table has less than 6 variables, it may not use all + the bits. This operation makes sure to zero out all non-valid + bits. + */ + inline void mask_bits() noexcept { _bits &= detail::masks[NumVars]; } + + /*! \cond PRIVATE */ +public: /* fields */ + uint64_t _bits = 0; + /*! \endcond */ +}; + +/*! Truth table (more than 6 variables) in which number of variables is known at compile time. + */ +template +struct static_truth_table +{ + /*! \cond PRIVATE */ + enum + { + NumBlocks = ( NumVars <= 6 ) ? 1u : ( 1u << ( NumVars - 6 ) ) + }; + + enum + { + NumBits = uint64_t( 1 ) << NumVars + }; + /*! \endcond */ + + /*! Standard constructor. + + The number of variables provided to the truth table must be known + at runtime. The number of blocks will be computed as a compile + time constant. + */ + static_truth_table() + { + _bits.fill( 0 ); + } + + /*! Constructs a new static truth table instance with the same number of variables. */ + inline static_truth_table construct() const + { + return static_truth_table(); + } + + /*! Returns number of variables. + */ + inline auto num_vars() const noexcept { return NumVars; } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return NumBlocks; } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return NumBits; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend() const noexcept { return _bits.crend(); } + + /*! \brief Assign other truth table if number of variables match. + + This replaces the current truth table with another truth table, if `other` + has the same number of variables. Otherwise, the truth table is not + changed. + + \param other Other truth table + */ + template::value>> + static_truth_table& operator=( const TT& other ) + { + if ( other.num_bits() == num_bits() ) + { + std::copy( other.begin(), other.end(), begin() ); + } + + return *this; + } + + /*! Masks the number of valid truth table bits. + + We know that we will have at least 7 variables in this data + structure. + */ + inline void mask_bits() noexcept {} + + /*! \cond PRIVATE */ +public: /* fields */ + std::array _bits; + /*! \endcond */ +}; + +template +struct is_truth_table> : std::true_type +{ +}; + +template +struct is_complete_truth_table> : std::true_type +{ +}; + +template +struct is_completely_specified_truth_table> : std::true_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/ternary_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/ternary_truth_table.hpp new file mode 100644 index 00000000000..0fe5a40e6b0 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/ternary_truth_table.hpp @@ -0,0 +1,262 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file ternary_truth_table.hpp + \brief Implements ternary_truth_table + + \author Siang-Yun Lee + \author Gianluca Radi +*/ + +#pragma once + +#include +#include +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" +#include "partial_truth_table.hpp" + +namespace kitty +{ + +template +struct ternary_truth_table +{ + /*! \brief Standard constructor. + + Initialize the truth table using the constructor of the inner + truth table type. + + \param n Number of variables or number of bits (when `TT = partial_truth_table`) + */ + explicit ternary_truth_table( uint32_t n ) + : _care( n ), _bits( n ) + { + } + + /*! \brief Empty constructor. + + Creates an empty truth table by calling the empty constructor + of the inner truth table type. + */ + ternary_truth_table() : _care(), _bits() {} + + /*! \brief Construct from bits and care. + + When `care` bit is 0, `bits` bit must be 0, meaning a don't care bit. + + \param bits Bits truth table. + \param care Care truth table. + */ + ternary_truth_table( TT const& bits, TT const& care ) + : _care( care ), _bits( bits ) + { + } + + /*! \brief Construct from a binary truth table. + + Initialize the truth table as equivalent to a binary truth table. + (All bits are cared.) + + \param binary Binary truth table. + */ + ternary_truth_table( TT const& binary ) + : _care( ~( binary.construct() ) ), _bits( binary ) + { + } + + /*! Constructs a new ternary_truth_table instance of the same size. */ + inline ternary_truth_table construct() const + { + if constexpr ( std::is_same_v ) + return ternary_truth_table( _bits.num_bits() ); + else + return ternary_truth_table( _bits.num_vars() ); + } + + /*! Returns number of variables. + */ + template::value>> + auto num_vars() const noexcept { return _bits.num_vars(); } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return _bits.num_blocks(); } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return _bits.num_bits(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin_bits() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end_bits() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin_bits() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end_bits() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin_bits() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend_bits() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin_bits() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend_bits() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin_bits() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend_bits() const noexcept { return _bits.crend(); } + + /*! \brief Begin iterator to care. + */ + inline auto begin_care() noexcept { return _care.begin(); } + + /*! \brief End iterator to care. + */ + inline auto end_care() noexcept { return _care.end(); } + + /*! \brief Begin iterator to care. + */ + inline auto begin_care() const noexcept { return _care.begin(); } + + /*! \brief End iterator to care. + */ + inline auto end_care() const noexcept { return _care.end(); } + + /*! \brief Reverse begin iterator to care. + */ + inline auto rbegin_care() noexcept { return _care.rbegin(); } + + /*! \brief Reverse end iterator to care. + */ + inline auto rend_care() noexcept { return _care.rend(); } + + /*! \brief Constant begin iterator to care. + */ + inline auto cbegin_care() const noexcept { return _care.cbegin(); } + + /*! \brief Constant end iterator to care. + */ + inline auto cend_care() const noexcept { return _care.cend(); } + + /*! \brief Constant reverse begin iterator to care. + */ + inline auto crbegin_care() const noexcept { return _care.crbegin(); } + + /*! \brief Constant teverse end iterator to care. + */ + inline auto crend_care() const noexcept { return _care.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. + The other truth table must also be ternary, and the inner type of + the other truth table must be assignable to the inner type of this + truth table. + + \param other Other truth table + */ + template + ternary_truth_table& operator=( const ternary_truth_table& other ) + { + _bits = other._bits; + _care = other._care; + + return *this; + } + + /*! \brief Assigned by a binary truth table. + + Replaces with a truth table equivalent to a binary truth table. + (All bits are cared.) + + \param other Binary truth table. + */ + template + ternary_truth_table& operator=( TT const& other ) + { + _bits = other; + _care = ~( other.construct() ); + + return *this; + } + + /*! \brief Masks valid truth table bits. + + This operation makes sure to zero out all unused bits. + */ + inline void mask_bits() noexcept + { + _care.mask_bits(); + _bits.mask_bits(); + } + + /*! \cond PRIVATE */ +public: /* fields */ + TT _care; + TT _bits; + /*! \endcond */ +}; + +template +struct is_truth_table> : is_truth_table +{ +}; + +template +struct is_complete_truth_table> : is_complete_truth_table +{ +}; + +template +struct is_completely_specified_truth_table> : std::false_type +{ +}; + +} // namespace kitty diff --git a/third-party/mockturtle/lib/kitty/kitty/traits.hpp b/third-party/mockturtle/lib/kitty/kitty/traits.hpp new file mode 100644 index 00000000000..5281acd21dd --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/traits.hpp @@ -0,0 +1,55 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file traits.hpp + \brief Type traits for truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include + +namespace kitty +{ + +template +struct is_truth_table : std::false_type +{ +}; + +template +struct is_complete_truth_table : std::false_type +{ +}; + +template +struct is_completely_specified_truth_table : std::false_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/lorina/lorina/aiger.hpp b/third-party/mockturtle/lib/lorina/lorina/aiger.hpp new file mode 100644 index 00000000000..c0384c19a37 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/aiger.hpp @@ -0,0 +1,922 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aiger.hpp + \brief Implements Aiger parser + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "common.hpp" +#include "diagnostics.hpp" +#include "detail/utils.hpp" +#include +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for the binary AIGER format. + * + * Callbacks for the AIGER format. + */ +class aiger_reader +{ +public: + /*! Latch input values */ + enum latch_init_value + { + ZERO = 0 /*!< Initialized with 0 */ + , ONE /*!< Initialized with 1 */ + , NONDETERMINISTIC /*!< Not initialized (non-deterministic value) */ + }; + +public: + /*! \brief Callback method for parsed header. + * + * \param m Maximum variable index + * \param i Number of inputs + * \param l Number of latches + * \param o Number of outputs + * \param a Number of AND gates + */ + virtual void on_header( uint64_t m, uint64_t i, uint64_t l, uint64_t o, uint64_t a ) const + { + (void)m; + (void)i; + (void)l; + (void)o; + (void)a; + } + + /*! \brief Callback method for parsed header. + * + * \param m Maximum variable index + * \param i Number of inputs + * \param l Number of latches + * \param o Number of outputs + * \param a Number of AND gates + * \param b Number of bad states properties + * \param c Number of invariant constraints + * \param j Number of justice properties + * \param f Number of fairness constraints + */ + virtual void on_header( uint64_t m, uint64_t i, uint64_t l, uint64_t o, uint64_t a, + uint64_t b, uint64_t c, uint64_t j, uint64_t f ) const + { + on_header( m, i, l, o, a ); + (void)b; + (void)c; + (void)j; + (void)f; + } + + /*! \brief Callback method for parsed input. + * + * \param pos Position in input list + * \param lit Assigned literal + */ + virtual void on_input( uint32_t pos, uint32_t lit ) const + { + (void)pos; + (void)lit; + } + + /*! \brief Callback method for parsed output. + * + * \param pos Position in output list + * \param lit Assigned literal + */ + virtual void on_output( uint32_t pos, uint32_t lit ) const + { + (void)pos; + (void)lit; + } + + /*! \brief Callback method for parsed latch. + * + * \param index Index of the latch + * \param next Assigned (next) literal + * \param reset Initial value of the latch + */ + virtual void on_latch( uint32_t index, uint32_t next, latch_init_value reset ) const + { + (void)index; + (void)next; + (void)reset; + } + + /*! \brief Callback method for parsed AND gate. + * + * \param index Index of the AND gate + * \param left_lit Assigned left literal + * \param right_lit Assigned right literal + */ + virtual void on_and( uint32_t index, uint32_t left_lit, uint32_t right_lit ) const + { + (void)index; + (void)left_lit; + (void)right_lit; + } + + /*! \brief Callback method for parsed bad state property. + * + * \param index Position in the list of bad state property + * \param lit Assigned literal + */ + virtual void on_bad_state( uint32_t pos, uint32_t lit ) const + { + (void)pos; + (void)lit; + } + + /*! \brief Callback method for parsed constraint. + * + * \param index Position in the list of constraint + * \param lit Assigned literal + */ + virtual void on_constraint( uint32_t pos, uint32_t lit ) const + { + (void)pos; + (void)lit; + } + + /*! \brief Callback method for parsed fairness constraints. + * + * \param index Position in the list of fairness constraint + * \param lit Assigned literal + */ + virtual void on_fairness( uint32_t pos, uint32_t lit ) const + { + (void)pos; + (void)lit; + } + + /*! \brief Callback method for parsed header of justice property. + * + * \param index Position in the list of the justice property + * \param size Number of assigned literals + */ + virtual void on_justice_header( uint32_t pos, uint64_t size ) const + { + (void)pos; + (void)size; + } + + /*! \brief Callback method for parsed justice property. + * + * \param index Position in the list of the justice property + * \param lits Assigned literals + */ + virtual void on_justice( uint32_t pos, const std::vector& lits ) const + { + (void)pos; + (void)lits; + } + + /*! \brief Callback method for parsed input name. + * + * \param pos Position in input list + * \param name Input name + */ + virtual void on_input_name( uint32_t pos, const std::string& name ) const + { + (void)pos; + (void)name; + } + + /*! \brief Callback method for parsed latch name. + * + * \param pos Position in latch list + * \param name Latch name + */ + virtual void on_latch_name( uint32_t pos, const std::string& name ) const + { + (void)pos; + (void)name; + } + + /*! \brief Callback method for parsed output name. + * + * \param pos Position in output list + * \param name Output name + */ + virtual void on_output_name( uint32_t pos, const std::string& name ) const + { + (void)pos; + (void)name; + } + + /*! \brief Callback method for a parsed name of a bad state property. + * + * \param pos Position in list of bad state property + * \param name Name of the bad state property + */ + virtual void on_bad_state_name( uint32_t pos, const std::string& name ) const + { + (void)pos; + (void)name; + } + + /*! \brief Callback method for a parsed name of an invariant constraint. + * + * \param pos Position in constraint list + * \param name Constraint name + */ + virtual void on_constraint_name( uint32_t pos, const std::string& name ) const + { + (void)pos; + (void)name; + } + + /*! \brief Callback method for a parsed name of a justice property. + * + * \param pos Position in the list of justice property + * \param name Name of the fairness constraint + */ + virtual void on_justice_name( uint32_t pos, const std::string& name ) const + { + (void)pos; + (void)name; + } + + /*! \brief Callback method for a parsed name of a fairness constraint. + * + * \param pos Position in list of fairness constraint + * \param name Name of the fairness constraint + */ + virtual void on_fairness_name( uint32_t pos, const std::string& name ) const + { + (void)pos; + (void)name; + } + + /*! \brief Callback method for parsed comment. + * + * \param comment Comment + */ + virtual void on_comment( const std::string& comment ) const + { + (void)comment; + } +}; /* aiger_reader */ + +/*! \brief An AIGER reader for prettyprinting ASCII AIGER. + * + * Callbacks for prettyprinting of ASCII AIGER. + * + */ +class ascii_aiger_pretty_printer : public aiger_reader +{ +public: + /*! \brief Constructor of the ASCII AIGER pretty printer. + * + * \param os Output stream + */ + ascii_aiger_pretty_printer( std::ostream& os = std::cout ) + : _os( os ) + { + } + + void on_header( uint64_t m, uint64_t i, uint64_t l, uint64_t o, uint64_t a, + uint64_t b, uint64_t c, uint64_t j, uint64_t f ) const override + { + _os << fmt::format( "aag {0} {1} {2} {3} {4} {5} {6} {7} {8}", + m, i, l, o, a, b, c, j, f ) + << std::endl; + } + + void on_input( uint32_t index, uint32_t lit ) const override + { + (void)index; + _os << lit << std::endl; + } + + void on_output( uint32_t index, uint32_t lit ) const override + { + (void)index; + _os << lit << std::endl; + } + + void on_latch( uint32_t index, uint32_t next, latch_init_value init_value ) const override + { + _os << ( 2u * index ) << ' ' << next; + switch( init_value ) + { + case 0: + _os << '0'; + break; + case 1: + _os << '1'; + break; + default: + break; + } + _os << std::endl; + } + + void on_and( uint32_t index, uint32_t left_lit, uint32_t right_lit ) const override + { + _os << ( 2u * index ) << ' ' << left_lit << ' ' << right_lit << std::endl; + } + + void on_bad_state( uint32_t index, uint32_t lit ) const override + { + (void)index; + _os << lit << std::endl; + } + + void on_constraint( uint32_t index, uint32_t lit ) const override + { + (void)index; + _os << lit << std::endl; + } + + void on_justice_header( uint32_t index, uint64_t size ) const override + { + (void)index; + _os << size << std::endl; + } + + void on_justice( uint32_t index, const std::vector& lits ) const override + { + (void)index; + for ( const auto& l : lits ) + { + _os << l << std::endl; + } + } + + void on_fairness( uint32_t index, uint32_t lit ) const override + { + (void)index; + _os << lit << std::endl; + } + + void on_input_name( uint32_t pos, const std::string& name ) const override + { + _os << "i" << pos << ' ' << name << std::endl; + } + + void on_latch_name( uint32_t pos, const std::string& name ) const override + { + _os << "l" << pos << ' ' << name << std::endl; + } + + void on_output_name( uint32_t pos, const std::string& name ) const override + { + _os << "o" << pos << ' ' << name << std::endl; + } + + void on_bad_state_name( uint32_t pos, const std::string& name ) const override + { + _os << "b" << pos << ' ' << name << std::endl; + } + + void on_constraint_name( uint32_t pos, const std::string& name ) const override + { + _os << "c" << pos << ' ' << name << std::endl; + } + + void on_justice_name( uint32_t pos, const std::string& name ) const override + { + _os << "j" << pos << ' ' << name << std::endl; + } + + void on_fairness_name( uint32_t pos, const std::string& name ) const override + { + _os << "f" << pos << ' ' << name << std::endl; + } + + void on_comment( const std::string& comment ) const override + { + _os << "c" << std::endl + << comment << std::endl; + } + + std::ostream& _os; /*!< Output stream */ +}; /* ascii_aiger_pretty_printer */ + +namespace aig_regex +{ +static std::regex header( R"(^aig (\d+) (\d+) (\d+) (\d+) (\d+)( \d+)?( \d+)?( \d+)?( \d+)?$)" ); +static std::regex ascii_header( R"(^aag (\d+) (\d+) (\d+) (\d+) (\d+)( \d+)?( \d+)?( \d+)?( \d+)?$)" ); +static std::regex input( R"(^i(\d+) (.*)$)" ); +static std::regex latch( R"(^l(\d+) (.*)$)" ); +static std::regex output( R"(^o(\d+) (.*)$)" ); +static std::regex bad_state( R"(^b(\d+) (.*)$)" ); +static std::regex constraint( R"(^c(\d+) (.*)$)" ); +static std::regex justice( R"(^j(\d+) (.*)$)" ); +static std::regex fairness( R"(^f(\d+) (.*)$)" ); +} // namespace aig_regex + +/*! \brief Reader function for ASCII AIGER format. + * + * Reads ASCII AIGER format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader An AIGER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_ascii_aiger( std::istream& in, const aiger_reader& reader, diagnostic_engine* diag = nullptr ) +{ + return_code result = return_code::success; + + std::smatch m; + std::string header_line; + detail::getline( in, header_line ); + + uint64_t _m, _i, _l, _o, _a, _b, _c, _j, _f; + + /* header */ + if ( std::regex_search( header_line, m, aig_regex::ascii_header ) ) + { + std::vector header; + for ( const auto& i : m ) + { + if ( i == "" ) + continue; + header.push_back( std::atol( std::string( i ).c_str() ) ); + } + + assert( header.size() >= 6u ); + assert( header.size() <= 10u ); + + _m = header[1u]; + _i = header[2u]; + _l = header[3u]; + _o = header[4u]; + _a = header[5u]; + _b = header.size() > 6 ? header[6u] : 0ul; + _c = header.size() > 7 ? header[7u] : 0ul; + _j = header.size() > 8 ? header[8u] : 0ul; + _f = header.size() > 9 ? header[9u] : 0ul; + reader.on_header( _m, _i, _l, _o, _a, _b, _c, _j, _f ); + } + else + { + if ( diag ) + { + diag->report( diag_id::ERR_AIGER_HEADER ).add_argument( header_line ); + } + return return_code::parse_error; + } + + std::string line; + + /* inputs */ + for ( auto i = 0ul; i < _i; ++i ) + { + detail::getline( in, line ); + const auto index = std::atol( line.c_str() ); + reader.on_input( i, index ); + } + + /* latches */ + for ( auto i = 0ul; i < _l; ++i ) + { + detail::getline( in, line ); + const auto tokens = detail::split( line, " " ); + + if ( !(tokens.size() <= 3u) ) + { + if ( diag ) + { + diag->report( diag_id::ERR_AIGER_LATCH_DECLARATION ).add_argument( line ); + } + return return_code::parse_error; + } + + const auto index = std::atol( std::string(tokens[0u]).c_str() ) / 2u; + const auto next_lit = std::atol( std::string(tokens[1u]).c_str() ); + + aiger_reader::latch_init_value init_value = aiger_reader::latch_init_value::NONDETERMINISTIC; + if ( tokens.size() == 3u ) + { + if ( tokens[2u] == "0" ) + { + init_value = aiger_reader::latch_init_value::ZERO; + } + else if ( tokens[1u] == "1" ) + { + init_value = aiger_reader::latch_init_value::ONE; + } + } + + reader.on_latch( index, next_lit, init_value ); + } + + /* outputs */ + for ( auto i = 0ul; i < _o; ++i ) + { + detail::getline( in, line ); + const auto lit = std::atol( line.c_str() ); + reader.on_output( i, lit ); + } + + /* bad state properties */ + for ( auto i = 0ul; i < _b; ++i ) + { + detail::getline( in, line ); + const auto lit = std::atol( line.c_str() ); + reader.on_bad_state( i, lit ); + } + + /* constraints */ + for ( auto i = 0ul; i < _c; ++i ) + { + detail::getline( in, line ); + const auto lit = std::atol( line.c_str() ); + reader.on_constraint( i, lit ); + } + + /* justice properties */ + std::vector justice_sizes; + for ( auto i = 0ul; i < _j; ++i ) + { + detail::getline( in, line ); + const auto justice_size = std::atol( line.c_str() ); + justice_sizes.emplace_back( justice_size ); + reader.on_justice_header( i, justice_size ); + } + + for ( auto i = 0ul; i < _j; ++i ) + { + std::vector lits; + for ( auto j = 0ul; j < justice_sizes[i]; ++j ) + { + detail::getline( in, line ); + const auto lit = std::atol( line.c_str() ); + lits.emplace_back( lit ); + } + reader.on_justice( i, lits ); + } + + /* fairness */ + for ( auto i = 0ul; i < _f; ++i ) + { + detail::getline( in, line ); + const auto lit = std::atol( line.c_str() ); + reader.on_fairness( i, lit ); + } + + /* ands */ + for ( auto i = 0ul; i < _a; ++i ) + { + detail::getline( in, line ); + const auto tokens = detail::split( line, " " ); + + if ( !(tokens.size() == 3u) ) + { + if ( diag ) + { + diag->report( diag_id::ERR_AIGER_AND_DECLARATION ).add_argument( line ); + } + return return_code::parse_error; + } + + const auto index = std::atol( std::string(tokens[0u]).c_str() )/2u; + const auto left_lit = std::atol( std::string(tokens[1u]).c_str() ); + const auto right_lit = std::atol( std::string(tokens[2u]).c_str() ); + reader.on_and( index, left_lit, right_lit ); + } + + /* parse names and comments */ + while ( detail::getline( in, line ) ) + { + if ( std::regex_search( line, m, aig_regex::input ) ) + { + reader.on_input_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::latch ) ) + { + reader.on_latch_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::output ) ) + { + reader.on_output_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::bad_state ) ) + { + reader.on_bad_state_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::constraint ) ) + { + reader.on_constraint_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::justice ) ) + { + reader.on_justice_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::fairness ) ) + { + reader.on_fairness_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( line == "c" ) + { + std::string comment = ""; + while ( detail::getline( in, line ) ) + { + comment += line; + } + reader.on_comment( comment ); + break; + } + } + + return result; +} + +/*! \brief Reader function for ASCII AIGER format. + * + * Reads ASCII AIGER format from a file and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader An AIGER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_ascii_aiger( const std::string& filename, const aiger_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_ascii_aiger( in, reader, diag ); + in.close(); + return ret; + } +} + +/*! \brief Reader function for binary AIGER format. + * + * Reads binary AIGER format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader An AIGER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_aiger( std::istream& in, const aiger_reader& reader, diagnostic_engine* diag = nullptr ) +{ + return_code result = return_code::success; + + std::smatch m; + std::string header_line; + detail::getline( in, header_line ); + + uint32_t _m, _i, _l, _o, _a, _b, _c, _j, _f; + + /* parse header */ + if ( std::regex_search( header_line, m, aig_regex::header ) ) + { + std::vector header; + for ( const auto& i : m ) + { + if ( i == "" ) + continue; + header.push_back( std::atol( std::string( i ).c_str() ) ); + } + + assert( header.size() >= 6u ); + assert( header.size() <= 10u ); + + _m = header[1u]; + _i = header[2u]; + _l = header[3u]; + _o = header[4u]; + _a = header[5u]; + _b = header.size() > 6 ? header[6u] : 0u; + _c = header.size() > 7 ? header[7u] : 0u; + _j = header.size() > 8 ? header[8u] : 0u; + _f = header.size() > 9 ? header[9u] : 0u; + reader.on_header( _m, _i, _l, _o, _a, _b, _c, _j, _f ); + } + else + { + if ( diag ) + { + diag->report( diag_id::ERR_AIGER_HEADER ).add_argument( header_line ); + } + return return_code::parse_error; + } + + std::string line; + + /* inputs */ + for ( auto i = 0u; i < _i; ++i ) + { + reader.on_input( i, 2u*(i+1) ); + } + + /* latches */ + for ( auto i = 0u; i < _l; ++i ) + { + detail::getline( in, line ); + const auto tokens = detail::split( line, " " ); + const auto next = std::atoi( tokens[0u].c_str() ); + aiger_reader::latch_init_value init_value = aiger_reader::latch_init_value::NONDETERMINISTIC; + if ( tokens.size() == 2u ) + { + if ( tokens[1u] == "0" ) + { + init_value = aiger_reader::latch_init_value::ZERO; + } + else if ( tokens[1u] == "1" ) + { + init_value = aiger_reader::latch_init_value::ONE; + } + } + + reader.on_latch( _i + i + 1u, next, init_value ); + } + + for ( auto i = 0u; i < _o; ++i ) + { + detail::getline( in, line ); + reader.on_output( i, std::atol( line.c_str() ) ); + } + + /* bad state properties */ + for ( auto i = 0u; i < _b; ++i ) + { + detail::getline( in, line ); + reader.on_bad_state( i, std::atol( line.c_str() ) ); + } + + /* constraints */ + for ( auto i = 0u; i < _c; ++i ) + { + detail::getline( in, line ); + reader.on_constraint( i, std::atol( line.c_str() ) ); + } + + /* justice properties */ + std::vector justice_sizes; + for ( auto i = 0u; i < _j; ++i ) + { + detail::getline( in, line ); + const auto justice_size = std::atol( line.c_str() ); + justice_sizes.emplace_back( justice_size ); + reader.on_justice_header( i, justice_size ); + } + + for ( auto i = 0u; i < _j; ++i ) + { + std::vector lits; + for ( auto j = 0u; j < justice_sizes[i]; ++j ) + { + detail::getline( in, line ); + const auto lit = std::atol( line.c_str() ); + lits.emplace_back( lit ); + } + reader.on_justice( i, lits ); + } + + /* fairness */ + for ( auto i = 0u; i < _f; ++i ) + { + detail::getline( in, line ); + reader.on_fairness( i, std::atol( line.c_str() ) ); + } + + const auto decode = [&]() { + auto i = 0; + auto res = 0; + while ( true ) + { + auto c = in.get(); + + res |= ( ( c & 0x7f ) << ( 7 * i ) ); + if ( ( c & 0x80 ) == 0 ) + break; + + ++i; + } + return res; + }; + + /* and gates */ + for ( auto i = _i + _l + 1; i < _i + _l + _a + 1; ++i ) + { + const auto d1 = decode(); + const auto d2 = decode(); + const auto g = i << 1; + reader.on_and( i, ( g - d1 ), ( g - d1 - d2 ) ); + } + + /* parse names and comments */ + while ( detail::getline( in, line ) ) + { + if ( std::regex_search( line, m, aig_regex::input ) ) + { + reader.on_input_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::latch ) ) + { + reader.on_latch_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::output ) ) + { + reader.on_output_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::bad_state ) ) + { + reader.on_bad_state_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::constraint ) ) + { + reader.on_constraint_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::justice ) ) + { + reader.on_justice_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( std::regex_search( line, m, aig_regex::fairness ) ) + { + reader.on_fairness_name( std::atol( std::string( m[1u] ).c_str() ), m[2u] ); + } + else if ( line == "c" ) + { + std::string comment = ""; + while ( detail::getline( in, line ) ) + { + comment += line; + } + reader.on_comment( comment ); + break; + } + } + + return result; +} + +/*! \brief Reader function for binary AIGER format. + * + * Reads binary AIGER format from a file and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader An AIGER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_aiger( const std::string& filename, const aiger_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::binary ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_aiger( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/bench.hpp b/third-party/mockturtle/lib/lorina/lorina/bench.hpp new file mode 100644 index 00000000000..7236e7d75da --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/bench.hpp @@ -0,0 +1,345 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bench.hpp + \brief Implements bench parser + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "common.hpp" +#include "diagnostics.hpp" +#include "detail/utils.hpp" +#include +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for the BENCH format. + * + * Callbacks for the BENCH format. + */ +class bench_reader +{ +public: + /*! \brief Callback method for parsed input. + * + * \param name Name of the input + */ + virtual void on_input( const std::string& name ) const + { + (void)name; + } + + /*! \brief Callback method for parsed output. + * + * \param name Name of the output + */ + virtual void on_output( const std::string& name ) const + { + (void)name; + } + + /*! \brief Callback method for parsed input of DFF (before output is available). + * + * \param input Name of the input + */ + virtual void on_dff_input( const std::string& input ) const + { + (void)input; + } + + /*! \brief Callback method for parsed DFF (when input and output are available). + * + * \param input Name of the input + * \param output Name of the output + */ + virtual void on_dff( const std::string& input, const std::string& output ) const + { + (void)input; + (void)output; + } + + /*! \brief Callback method for parsed gate. + * + * \param inputs A list of inputs + * \param output An output + * \param type Either a name that specified the gate or a hexadecimal number that describes the logic function of the gate. + */ + virtual void on_gate( const std::vector& inputs, const std::string& output, const std::string& type ) const + { + (void)inputs; + (void)output; + (void)type; + } + + /*! \brief Callback method for parsed gate assignment. + * + * \param input An input + * \param output An output + */ + virtual void on_assign( const std::string& input, const std::string& output ) const + { + (void)input; + (void)output; + } +}; /* bench_reader */ + +/*! \brief A BENCH reader for prettyprinting BENCH. + * + * Callbacks for prettyprinting of BENCH. + * + */ +class bench_pretty_printer : public bench_reader +{ +public: + /*! \brief Constructor of the BENCH pretty printer. + * + * \param os Output stream + */ + bench_pretty_printer( std::ostream& os = std::cout ) + : _os( os ) + { + } + + virtual void on_input( const std::string& name ) const override + { + _os << fmt::format( "INPUT({0})", name ) << std::endl; + } + + virtual void on_output( const std::string& name ) const override + { + _os << fmt::format( "OUTPUT({0})", name ) << std::endl; + } + + virtual void on_gate( const std::vector& inputs, const std::string& output, const std::string& type ) const override + { + _os << fmt::format( "{0} = {1}({2})", output, type, detail::join( inputs, "," ) ) << std::endl; + } + + virtual void on_assign( const std::string& input, const std::string& output ) const override + { + _os << fmt::format( "{0} = {1}", output, input ) << std::endl; + } + + std::ostream& _os; /*!< Output stream */ +}; /* bench_pretty_printer */ + +namespace bench_regex +{ +static std::regex input( R"(INPUT\((.*)\))" ); +static std::regex output( R"(OUTPUT\((.*)\))" ); +static std::regex gate( R"((.*)\s+=\s+(.*)\((.*)\))" ); +static std::regex dff( R"((.*)\s+=\s+DFF\((.+)\))" ); +static std::regex lut( R"((.*)\s+=\s+LUT\s+(.*)\((.*)\))" ); +static std::regex gate_asgn( R"((.*)\s+=\s+(.*))" ); +} // namespace bench_regex + +/*! \brief Reader function for the BENCH format. + * + * Reads BENCH format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader A BENCH reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_bench( std::istream& in, const bench_reader& reader, diagnostic_engine* diag = nullptr ) +{ + return_code result = return_code::success; + + /* Function signature */ + using GateFn = detail::Func< + std::vector, + std::string, + std::string + >; + + /* Parameter maps */ + using GateParamMap = detail::ParamPackMap< + /* Key */ + std::string, + /* Params */ + std::vector, + std::string, + std::string + >; + + constexpr static const int GATE_FN{0}; + + using ParamMaps = detail::ParamPackMapN; + using PackedFns = detail::FuncPackN; + + detail::call_in_topological_order + on_action( PackedFns( GateFn( [&]( std::vector inputs, + std::string output, + std::string type ) + { + if ( type == "" ) + { + reader.on_assign( inputs.front(), output ); + } + else if ( type == "DFF" ) + { + reader.on_dff( inputs.front(), output ); + } + else + { + reader.on_gate( inputs, output, type ); + } + } ) ) ); + on_action.declare_known( "vdd" ); + on_action.declare_known( "gnd" ); + + std::smatch m; + detail::foreach_line_in_file_escape( in, [&]( const std::string& line ) { + /* empty line or comment */ + if ( line.empty() || line[0] == '#' ) + return true; + + /* INPUT() */ + if ( std::regex_search( line, m, bench_regex::input ) ) + { + const auto s = detail::trim_copy( m[1] ); + on_action.declare_known( s ); + reader.on_input( s ); + return true; + } + + /* OUTPUT() */ + if ( std::regex_search( line, m, bench_regex::output ) ) + { + reader.on_output( detail::trim_copy( m[1] ) ); + return true; + } + + /* ( = LUT ()) */ + if ( std::regex_search( line, m, bench_regex::lut ) ) + { + const auto output = detail::trim_copy( m[1] ); + const auto type = detail::trim_copy( m[2] ); + const auto args = detail::trim_copy( m[3] ); + const auto inputs = detail::split( args, "," ); + on_action.call_deferred( inputs, { output }, std::make_tuple( inputs, output, type ) ); + return true; + } + + /* ( = DFF()) */ + if ( std::regex_search( line, m, bench_regex::dff ) ) + { + const auto output = detail::trim_copy( m[1] ); + const auto arg = detail::trim_copy( m[2] ); + reader.on_dff_input( output ); + on_action.declare_known( output ); + on_action.call_deferred( { arg }, { output }, std::make_tuple( std::vector{ arg }, output, "DFF" ) ); + return true; + } + + /* ( = ()) */ + if ( std::regex_search( line, m, bench_regex::gate ) ) + { + const auto output = detail::trim_copy( m[1] ); + const auto type = detail::trim_copy( m[2] ); + const auto args = detail::trim_copy( m[3] ); + const auto inputs = detail::split( args, "," ); + on_action.call_deferred( inputs, { output }, std::make_tuple( inputs, output, type ) ); + return true; + } + + /* ( = */ + if ( std::regex_search( line, m, bench_regex::gate_asgn ) ) + { + const auto output = detail::trim_copy( m[1] ); + const auto input = detail::trim_copy( m[2] ); + on_action.call_deferred( { input }, { output }, std::make_tuple( std::vector{ input }, output, "" ) ); + return true; + } + + if ( diag ) + { + diag->report( diag_id::ERR_PARSE_LINE ).add_argument( line ); + } + + result = return_code::parse_error; + return true; + } ); + + /* check dangling objects */ + const auto& deps = on_action.unresolved_dependencies(); + if ( deps.size() > 0 ) + { + result = return_code::parse_error; + } + + for ( const auto& r : deps ) + { + if ( diag ) + { + diag->report( diag_id::WRN_UNRESOLVED_DEPENDENCY ) + .add_argument( r.first ) + .add_argument( r.second ); + } + } + + return result; +} + +/*! \brief Reader function for BENCH format. + * + * Reads BENCH format from a file and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader A BENCH reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_bench( const std::string& filename, const bench_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_bench( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/blif.hpp b/third-party/mockturtle/lib/lorina/lorina/blif.hpp new file mode 100644 index 00000000000..be496954737 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/blif.hpp @@ -0,0 +1,504 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file blif.hpp + \brief Implements BLIF parser + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "common.hpp" +#include "diagnostics.hpp" +#include "detail/utils.hpp" +#include +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for the BLIF format. + * + * Callbacks for the BLIF (Berkeley Logic Interchange Format) format. + */ +class blif_reader +{ +public: + /*! \brief Type of the output cover as truth table. */ + using output_cover_t = std::vector>; + + /*! Latch input values */ + enum latch_init_value + { + ZERO = 0 /*!< Initialized with 0 */ + , ONE /*!< Initialized with 1 */ + , NONDETERMINISTIC /*!< Not initialized (non-deterministic value) */ + , UNKNOWN + }; + + enum latch_type + { + FALLING = 0 + , RISING + , ACTIVE_HIGH + , ACTIVE_LOW + , ASYNC + , NONE + }; + +public: + static std::optional latch_init_value_from_string( std::string const& s ) + { + if ( s == "0" ) + { + return blif_reader::latch_init_value::ZERO; + } + else if ( s == "1" ) + { + return blif_reader::latch_init_value::ONE; + } + else if ( s == "2" ) + { + return blif_reader::latch_init_value::NONDETERMINISTIC; + } + else + { + return blif_reader::latch_init_value::UNKNOWN; + } + } + + static std::optional latch_type_from_string( std::string const& s ) + { + if ( s == "fe" ) + { + return blif_reader::latch_type::FALLING; + } + else if ( s == "re" ) + { + return blif_reader::latch_type::RISING; + } + else if ( s == "ah" ) + { + return blif_reader::latch_type::ACTIVE_HIGH; + } + else if ( s == "al" ) + { + return blif_reader::latch_type::ACTIVE_LOW; + } + else + { + return blif_reader::latch_type::ASYNC; + } + } + +public: + /*! \brief Callback method for parsed model. + * + * \param model_name Name of the model + */ + virtual void on_model( const std::string& model_name ) const + { + (void)model_name; + } + + /*! \brief Callback method for parsed input. + * + * \param name Input name + */ + virtual void on_input( const std::string& name ) const + { + (void)name; + } + + /*! \brief Callback method for parsed output. + * + * \param name Output name + */ + virtual void on_output( const std::string& name ) const + { + (void)name; + } + + /*! \brief Callback method for parsed latch. + * + * \param input Input name + * \param output Output name + * \param type Optional type of the latch + * \param control Optional name of control signal + * \param reset Optional initial value of the latch + */ + virtual void on_latch( const std::string& input, const std::string& output, const std::optional& type, const std::optional& control, const std::optional& init_value ) const + { + (void)input; + (void)output; + (void)type; + (void)control; + (void)init_value; + } + + /*! \brief Callback method for parsed gate. + * + * \param inputs A list of input names + * \param output Name of output of the gate + * \param cover N-input, 1-output PLA description of the logic function corresponding to the logic gate + */ + virtual void on_gate( const std::vector& inputs, const std::string& output, const output_cover_t& cover ) const + { + (void)inputs; + (void)output; + (void)cover; + } + + /*! \brief Callback method for parsed end. + * + */ + virtual void on_end() const {} + + /*! \brief Callback method for parsed comment. + * + * \param comment Comment + */ + virtual void on_comment( const std::string& comment ) const + { + (void)comment; + } +}; /* blif_reader */ + +/*! \brief A BLIF reader for prettyprinting BLIF. + * + * Callbacks for prettyprinting of BLIF. + * + */ +class blif_pretty_printer : public blif_reader +{ +public: + /*! \brief Constructor of the BLIF pretty printer. + * + * \param os Output stream + */ + blif_pretty_printer( std::ostream& os = std::cout ) + : _os( os ) + { + } + + virtual void on_model( const std::string& model_name ) const + { + _os << ".model " << model_name << std::endl; + } + + virtual void on_input( const std::string& name ) const + { + if ( first_input ) _os << std::endl << ".inputs "; + _os << name << " "; + first_input = false; + } + + virtual void on_output( const std::string& name ) const + { + if ( first_output ) _os << std::endl << ".output "; + _os << name << " "; + first_output = false; + } + + virtual void on_latch( const std::string& input, const std::string& output, const std::optional& type, const std::optional& control, const std::optional& init_value ) const + { + _os << fmt::format( "\n.latch {0} {1} {2} {3} {4}", input, output, + ( type ? *type : latch_type::NONE ), ( control ? *control : "" ), ( init_value ? *init_value : latch_init_value::UNKNOWN ) ) << std::endl; + } + + virtual void on_gate( const std::vector& inputs, const std::string& output, const output_cover_t& cover ) const + { + _os << std::endl << fmt::format( ".names {0} {1}", detail::join( inputs, "," ), output ) << std::endl; + for ( const auto& c : cover ) + { + _os << c.first << ' ' << c.second << std::endl; + } + } + + virtual void on_end() const + { + _os << std::endl << ".end" << std::endl; + } + + virtual void on_comment( const std::string& comment ) const + { + _os << std::endl << "# " << comment << std::endl; + } + + mutable bool first_input = true; /*!< Predicate that is true until the first input was parsed */ + mutable bool first_output = true; /*!< Predicate that is true until the first output was parsed */ + std::ostream& _os; /*!< Output stream */ +}; /* blif_pretty_printer */ + +namespace blif_regex +{ +static std::regex model( R"(.model\s+(.*))" ); +static std::regex names( R"(.names\s+(.*))" ); +static std::regex line_of_truthtable( R"(([01\-]*)\s*([01\-]))" ); +static std::regex end( R"(.end)" ); +} // namespace blif_regex + +/*! \brief Reader function for the BLIF format. + * + * Reads BLIF format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader A BLIF reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_blif( std::istream& in, const blif_reader& reader, diagnostic_engine* diag = nullptr ) +{ + return_code result = return_code::success; + + /* Function signature */ + using GateFn = detail::Func< + std::vector, + std::string, + std::vector> + >; + + /* Parameter maps */ + using GateParamMap = detail::ParamPackMap< + /* Key */ + std::string, + /* Params */ + std::vector, + std::string, + std::vector> + >; + + constexpr static const int GATE_FN{0}; + + using ParamMaps = detail::ParamPackMapN; + using PackedFns = detail::FuncPackN; + + detail::call_in_topological_order + on_action( PackedFns( GateFn( [&]( std::vector inputs, + std::string output, + std::vector> tt ) + { + /* ignore latches */ + if ( output == "" ) + { + assert( inputs.size() == 0u ); + return; + } + + reader.on_gate( inputs, output, tt ); + } ) ) ); + + std::smatch m; + detail::foreach_line_in_file_escape( in, [&]( std::string line ) { + redo: + /* empty line or comment */ + if ( line.empty() || line[0] == '#' ) + return true; + + /* names */ + if ( std::regex_search( line, m, blif_regex::names ) ) + { + auto args = detail::split( detail::trim_copy( m[1] ), " " ); + const auto output = *( args.rbegin() ); + args.pop_back(); + + std::vector> tt; + std::string copy_line; + detail::foreach_line_in_file_escape( in, [&]( const std::string& line ) { + copy_line = line; + + /* terminate when the next '.' declaration starts */ + if ( line[0] == '.' ) + return false; + + /* empty line or comment */ + if ( line.empty() || line[0] == '#' ) + return true; + + if ( std::regex_search( line, m, blif_regex::line_of_truthtable ) ) + { + tt.push_back( std::make_pair( detail::trim_copy( m[1] ), detail::trim_copy( m[2] ) ) ); + return true; + } + + return false; + } ); + + on_action.call_deferred( args, { output }, std::tuple( args, output, tt ) ); + + if ( in.eof() ) + { + return true; + } + else + { + /* restart parsing with the line of the subparser */ + line = copy_line; + goto redo; + } + } + + /* .model */ + if ( std::regex_search( line, m, blif_regex::model ) ) + { + reader.on_model( detail::trim_copy( m[1] ) ); + return true; + } + + /* .inputs */ + if ( detail::starts_with( line, ".inputs" ) ) + { + std::string const input_declaration = line.substr( 7 ); + for ( const auto& input : detail::split( detail::trim_copy( input_declaration ), " " ) ) + { + auto const s = detail::trim_copy( input ); + on_action.declare_known( s ); + reader.on_input( s ); + } + return true; + } + + /* .latch */ + if ( detail::starts_with( line, ".latch" ) ) + { + std::string const latch_declaration = line.substr( 6 ); + + std::vector latch_elements = detail::split( detail::trim_copy( latch_declaration ), " " ); + if ( latch_elements.size() >= 2 ) + { + std::string const& input = latch_elements.at( 0 ); + std::string const& output = latch_elements.at( 1 ); + std::optional init_value; + std::optional type; + std::optional control; + + if ( latch_elements.size() == 3u ) + { + init_value = blif_reader::latch_init_value_from_string( latch_elements.at( 2 ) ); + } + else if ( latch_elements.size() == 4u ) + { + type = blif_reader::latch_type_from_string( latch_elements.at( 2 ) ); + control = latch_elements.at( 3 ); + } + else if ( latch_elements.size() == 5u ) + { + type = blif_reader::latch_type_from_string( latch_elements.at( 2 ) ); + control = latch_elements.at( 3 ); + init_value = blif_reader::latch_init_value_from_string( latch_elements.at( 4 ) ); + } + + on_action.declare_known( output ); + reader.on_latch( input, output, type, control, init_value ); + on_action.compute_dependencies( { output } ); + + return true; + } + + diag->report( diag_id::ERR_BLIF_LATCH_FORMAT ).add_argument( line ); + + result = return_code::parse_error; + } + + /* .outputs */ + if ( detail::starts_with( line, ".outputs" ) ) + { + std::string const output_declaration = line.substr( 8 ); + for ( const auto& output : detail::split( detail::trim_copy( output_declaration ), " " ) ) + { + reader.on_output( detail::trim_copy( output ) ); + } + return true; + } + + /* .end */ + if ( std::regex_search( line, m, blif_regex::end ) ) + { + reader.on_end(); + return true; + } + + if ( diag ) + { + diag->report( diag_id::ERR_PARSE_LINE ).add_argument( line ); + } + + result = return_code::parse_error; + return true; + } ); + + /* check dangling objects */ + auto const& deps = on_action.unresolved_dependencies(); + if ( deps.size() > 0 ) + result = return_code::parse_error; + + for ( const auto& r : deps ) + { + if ( diag ) + { + diag->report( diag_id::WRN_UNRESOLVED_DEPENDENCY ) + .add_argument( r.first ) + .add_argument( r.second ); + } + } + + return result; +} + +/*! \brief Reader function for BLIF format. + * + * Reads binary BLIF format from a file and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader A BLIF reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_blif( const std::string& filename, const blif_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_blif( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/bristol.hpp b/third-party/mockturtle/lib/lorina/lorina/bristol.hpp new file mode 100644 index 00000000000..f53a6b42ce9 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/bristol.hpp @@ -0,0 +1,217 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bristol.hpp + \brief Implements a parser for Bristol fashion + + Reference: https://homes.esat.kuleuven.be/~nsmart/MPC/ + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "common.hpp" +#include "diagnostics.hpp" +#include "detail/utils.hpp" + +#include +#include +#include +#include +#include +#include + +namespace lorina +{ + +class bristol_reader +{ +public: + explicit bristol_reader() {} + + virtual ~bristol_reader() {} + + virtual void on_header( uint32_t num_gates, uint32_t num_wires, uint32_t num_inputs, std::vector const& num_wires_per_input, uint32_t num_outputs, std::vector const& num_wires_per_output ) const + { + (void)num_gates; + (void)num_wires; + (void)num_inputs; + (void)num_wires_per_input; + (void)num_outputs; + (void)num_wires_per_output; + } + + virtual void on_gate( std::vector const& in, uint32_t out, std::string const& gate ) const + { + (void)in; + (void)out; + (void)gate; + } +}; + +class bristol_parser +{ +public: + explicit bristol_parser( std::istream& is, bristol_reader const& reader ) + : is( is ) + , reader( reader ) + { + } + + return_code run() + { + std::vector tokens; + + /* read header */ + if ( !get_tokens_of_next_line( tokens ) || tokens.size() != 2u ) + return return_code::parse_error; + + uint32_t num_gates = static_cast( std::stoul( tokens[0u] ) ); + uint32_t num_wires = static_cast( std::stoul( tokens[1u] ) ); + + if ( !get_tokens_of_next_line( tokens ) || tokens.empty() ) + return return_code::parse_error; + uint32_t num_inputs = static_cast( std::stoul( tokens[0u] ) ); + if ( tokens.size() != num_inputs + 1 ) + return return_code::parse_error; + std::vector num_inputs_per_wire( num_inputs ); + std::transform( tokens.begin() + 1, tokens.end(), num_inputs_per_wire.begin(), [&]( std::string const& s ) { return static_cast( std::stoul( s ) ); } ); + + if ( !get_tokens_of_next_line( tokens ) || tokens.empty() ) + return return_code::parse_error; + uint32_t num_outputs = static_cast( std::stoul( tokens[0u] ) ); + if ( tokens.size() != num_outputs + 1 ) + return return_code::parse_error; + std::vector num_outputs_per_wire( num_outputs ); + std::transform( tokens.begin() + 1, tokens.end(), num_outputs_per_wire.begin(), [&]( std::string const& s ) { return static_cast( std::stoul( s ) ); } ); + + reader.on_header( num_gates, num_wires, num_inputs, num_inputs_per_wire, num_outputs, num_outputs_per_wire ); + + /* parse gates */ + while ( get_tokens_of_next_line( tokens ) ) + { + uint32_t num_input_wires = std::atoi( tokens[0u].c_str() ); + uint32_t num_output_wires = std::atoi( tokens[1u].c_str() ); + + assert( tokens.size() == num_input_wires + num_output_wires + 3u ); + assert( num_output_wires == 1u ); + (void)num_output_wires; + + std::vector inputs; + for ( uint32_t i = 0; i < num_input_wires; ++i ) + { + inputs.push_back( std::atoi( tokens[ 2u + i ].c_str() ) ); + } + + uint32_t const output = std::atoi( tokens[ 2u + num_input_wires ].c_str() ); + std::string const& gate = tokens[tokens.size() - 1]; + + reader.on_gate( inputs, output, gate ); + } + + return return_code::success; + } + +private: + bool get_tokens_of_next_line( std::vector& tokens ) + { + tokens.clear(); + + std::string line; + do + { + if ( is.eof() ) + return false; + + std::getline( is, line ); + detail::trim( line ); + } while ( line == "" ); + + std::istringstream iss( line ); + std::string token; + while ( !iss.eof() ) + { + iss >> token; + tokens.push_back( token ); + } + + return true; + } + +private: + std::istream& is; + bristol_reader const& reader; +}; + +/*! \brief Reads a Bristol fashion circuit from a stream + * + * A callback method of the reader visitor is invoked for each + * primitive object matched in the specification. + * + * \param in Input stream + * \param reader A BRISTOL reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_bristol( std::istream& is, const bristol_reader& reader, diagnostic_engine* diag = nullptr ) +{ + (void)diag; + return bristol_parser( is, reader ).run(); +} + +/*! \brief Reads a Bristol fashion circuit from a file + * + * A callback method of the reader visitor is invoked for each + * primitive object matched in the specification. + * + * \param filename Name of the file + * \param reader A BRISTOL reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_bristol( const std::string& filename, const bristol_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( filename, std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_bristol( in, reader ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/common.hpp b/third-party/mockturtle/lib/lorina/lorina/common.hpp new file mode 100644 index 00000000000..3dd1272b4c5 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/common.hpp @@ -0,0 +1,44 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file common.hpp + \brief Common data structures. + + \author Heinz Riener +*/ + +#pragma once + +namespace lorina +{ + +enum class return_code +{ + success = 0, + parse_error, +}; + +} // namespace lorina \ No newline at end of file diff --git a/third-party/mockturtle/lib/lorina/lorina/detail/call_in_topological_order.hpp b/third-party/mockturtle/lib/lorina/lorina/detail/call_in_topological_order.hpp new file mode 100644 index 00000000000..cdf7645e968 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/detail/call_in_topological_order.hpp @@ -0,0 +1,408 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! \cond PRIVATE */ + +#pragma once + +#include + +namespace lorina +{ + +namespace detail +{ + +/* std::apply in C++14 taken from https://stackoverflow.com/a/36656413 */ +template +auto apply( Function f, Tuple t, std::index_sequence ) +{ + return f( std::get(t)... ); +} + +template +auto apply( Function f, Tuple t ) +{ + static constexpr auto size = std::tuple_size::value; + return apply( f, t, std::make_index_sequence{} ); +} + +/* \brief Parameter pack + * + * A pack of parameters of different types + * + * Example: + * using PackedParams = ParamPack; + * + * PackedParams params; + * params.set( 10, "a", 10.5f ); + */ +template +class ParamPack +{ +public: + using Tuple = std::tuple; + +public: + explicit ParamPack() {} + + explicit ParamPack( Args... params ) + : params_( std::make_tuple( params... ) ) + {} + + explicit ParamPack( const std::tuple& tup ) + : params_( tup ) + {} + + void set( Args... params ) + { + params_ = std::make_tuple( params... ); + } + + auto get() const + { + return params_; + } + +private: + std::tuple params_; +}; // ParamPack + +/* \brief Multple parameter packs + * + * A vector of N parameter packs + * + * Example: + * using PackedParamsA = ParamPack; + * using PackedParamsB = ParamPack; + * + * PackedParamsA params_a( 10, "a", 4 ); + * PackedParamsB params_b( 2.0, 3 ); + * ParamPackN + * params( params_a, params_b ); + */ +template +class ParamPackN +{ +public: + explicit ParamPackN() + {} + + explicit ParamPackN( ParamPacks... packs ) + : packs_( std::make_tuple( packs... ) ) + {} + + template + void set( typename std::tuple_element>::type const& pack ) + { + static_assert( I < sizeof...( ParamPacks ) ); + std::get( packs_ ) = pack; + } + + template + auto get() const + { + static_assert( I < sizeof...( ParamPacks ) ); + return std::get( packs_ ); + } + +private: + std::tuple packs_; +}; // ParamPackN + +/* \brief Parameter pack map + * + * A map from a KeyType to a parameter pack + * + * Example: + * ParamPackMap param_map; + * param_map["a"] = ParamPack( 10, "a", 10 ); + * param_map["b"] = ParamPack( 10, "b", 10 ); + */ +template +class ParamPackMap +{ +public: + using ValueType = typename detail::ParamPack::Tuple; + +public: + explicit ParamPackMap() {} + + auto& operator[]( const KeyType& key ) + { + return map_[key]; + } + + const auto& operator[]( const KeyType& key ) const + { + return map_[key]; + } + + void insert( const KeyType& key, const std::tuple& tup ) + { + map_[key] = ParamPack( tup ); + } + + void insert( const KeyType& key, Args... args ) + { + map_[key] = ParamPack( std::make_tuple( args... ) ); + } + +private: + std::unordered_map> map_; +}; + +/* \brief Multiple parameter pack maps + * + * A vector of N parameter pack maps + * + * Example: + * using StringToStringMap = ParamPackMap; + * using StringToIntMap = ParamPackMap; + * + * ParamPackMapN maps; + * maps.get<0>()["a"] = ParamPack( std::string{"a"} ); + * maps.get<1>()["b"] = ParamPack( 10 ); + */ +template +class ParamPackMapN +{ +public: + explicit ParamPackMapN() {} + + template + auto& get() + { + static_assert( I < sizeof...( ParamMaps ) ); + return std::get( maps_ ); + } + + template + auto& get() const + { + static_assert( I < sizeof...( ParamMaps ) ); + return std::get( maps_ ); + } + +private: + std::tuple maps_; +}; + +/* \brief A callable function + * + * Example: + * Func f; + * f( std::tuple( 10, "a" ) ); + * f( std::tuple( 12, "b" ) ); + */ +template +class Func +{ +public: + using Tuple = std::tuple; + +public: + Func( std::function fn ) + : fn_( fn ) + {} + + void operator()( Tuple const& tup ) + { + detail::apply( fn_, tup ); + } + +private: + std::function fn_; +}; // FuncPack + +/* \brief Multiple packed functions + * + * A vector of N packed functions + * + * Example: + * using Fn_A = Func; + * using Fn_B = Func; + * + * FuncPackN functions( + * Fn_A( []( int a, std::string b ){ std::cout << a << ' ' << b << std::endl; } ), + * Fn_B( []( double a, int b ){ std::cout << a << ' ' << b << std::endl; } ) + * ); + * + * functions.apply<0>( std::tuple( 10, "foo" ) ); + * functions.apply<0>( std::tuple( 12, "bar" ) ); + * functions.apply<1>( std::tuple( 3.41, 0 ) ); + * functions.apply<1>( std::tuple( 2.58, 1 ) ); + */ +template +class FuncPackN +{ +public: + explicit FuncPackN( std::tuple fns ) + : fns_( fns ) + {} + + explicit FuncPackN( Fns... fns ) + : fns_( std::make_tuple( fns... ) ) + {} + + template + void apply( typename std::tuple_element>::type::Tuple const& tup ) + { + std::get( fns_ )( tup ); + } + +private: + std::tuple fns_; +}; // FuncPackN + +template +struct call_in_topological_order; + +template +struct call_in_topological_order, ParamPackMapN> +{ +public: + using dependency_type = std::pair; + +public: + explicit call_in_topological_order( FuncPackN fns ) + : fns_( fns ) + {} + + void declare_known( const std::string& name ) + { + known_.emplace( name ); + } + + template + void call_deferred( const std::vector& inputs, + const std::vector& outputs, + const typename std::tuple_element>::type::ValueType& tup ) + { + /* do we have all inputs */ + std::unordered_set unknown; + for ( const auto& input : inputs ) + { + if ( known_.find( input ) != std::end( known_ ) ) + continue; + + const auto it = waits_for_.find( input ); + if ( it == std::end( waits_for_ ) || !it->second.empty() ) + { + unknown.insert( input ); + } + } + + /* store the parameters */ + for ( const auto& output : outputs ) + { + param_maps_.template get()[output] = ParamPack( tup ); + } + + if ( !unknown.empty() ) + { + /* defer computation */ + for ( const auto& input : unknown ) + { + for ( const auto& output : outputs ) + { + triggers_[input].insert( output ); + waits_for_[output].insert( input ); + } + } + return; + } + + /* trigger dependency computation */ + for ( const auto& output : outputs ) + { + compute_dependencies( output ); + } + } + + template + void compute_dependencies( const std::string& output ) + { + /* init empty, makes sure nothing is waiting for this output */ + waits_for_[output]; + + std::stack computed; + computed.push( output ); + + while ( !computed.empty() ) + { + auto const next = computed.top(); + computed.pop(); + + // C++17: std::apply( f, _stored_params[next] ); + // detail::apply( f_, stored_params_[next] ); + + fns_.template apply( param_maps_.template get()[next].get() ); + + /* activate all the triggers */ + for ( const auto& other : triggers_[next] ) + { + waits_for_[other].erase( next ); + if ( waits_for_[other].empty() ) + { + computed.push( other ); + } + } + triggers_[next].clear(); + } + } + + std::vector unresolved_dependencies() + { + std::vector deps; + for ( const auto& item : waits_for_ ) + { + auto const& key = item.first; + auto const& wait_list = item.second; + + if ( wait_list.empty() ) + continue; + + /* collect all keys that are still waiting for an item */ + for ( const auto& entry : wait_list ) + { + deps.emplace_back( key, entry ); + } + } + return deps; + } + +private: + FuncPackN fns_; + ParamPackMapN param_maps_; + + std::unordered_set known_; + std::unordered_map> waits_for_; + std::unordered_map> triggers_; +}; // call_in_topological_order + +} // namespace detail + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/detail/tokenizer.hpp b/third-party/mockturtle/lib/lorina/lorina/detail/tokenizer.hpp new file mode 100644 index 00000000000..ddec8135091 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/detail/tokenizer.hpp @@ -0,0 +1,148 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +namespace lorina +{ + +namespace detail +{ + +enum class tokenizer_return_code +{ + invalid = 0 +, valid = 1 +, comment = 2 +}; + +class tokenizer +{ +public: + explicit tokenizer( std::istream& is ) + : _is( is ) + {} + + bool get_char( char& c ) + { + if ( !lookahead.empty() ) + { + c = *lookahead.rbegin(); + lookahead.pop_back(); + return true; + } + else + { + return bool(_is.get( c )); + } + } + + tokenizer_return_code get_token_internal( std::string& token ) + { + if ( _done ) + { + return tokenizer_return_code::invalid; + } + token = ""; + + char c; + while ( get_char( c ) ) + { + if ( c == '\n' && _comment_mode ) + { + _comment_mode = false; + return tokenizer_return_code::comment; + } + else if ( !_comment_mode ) + { + if ( ( c == ' ' || c == '\\' || c == '\n' ) && !_quote_mode ) + { + return tokenizer_return_code::valid; + } + if ( ( c == '(' || c == ')' || c == '{' || c == '}' || c == ';' || c == ':' || c == ',' || c == '~' || c == '&' || c == '|' || c == '^' || c == '#' || c == '[' || c == ']' ) && !_quote_mode ) + { + if ( token.empty() ) + { + token = std::string() + c; + } + else + { + lookahead += c; + } + return tokenizer_return_code::valid; + } + + if ( c == '\"' ) + { + _quote_mode = !_quote_mode; + } + } + + token += c; + } + + _done = true; + return tokenizer_return_code::valid; + } + + bool get_token( std::string& token ) + { + tokenizer_return_code result; + do + { + result = get_token_internal( token ); + detail::trim( token ); + + /* keep parsing if token is empty */ + } while ( token == "" && result == tokenizer_return_code::valid ); + + return ( result == tokenizer_return_code::valid ); + } + + void set_comment_mode( bool value = true ) + { + _comment_mode = value; + } + + bool get_comment_mode() const + { + return _comment_mode; + } + +protected: + bool _done = false; + bool _quote_mode = false; + bool _comment_mode = false; + std::istream& _is; + std::string lookahead; +}; /* tokenizer */ + +} // namespace detail + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/detail/utils.hpp b/third-party/mockturtle/lib/lorina/lorina/detail/utils.hpp new file mode 100644 index 00000000000..15e9b7ad45a --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/detail/utils.hpp @@ -0,0 +1,242 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! \cond PRIVATE */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "call_in_topological_order.hpp" + +namespace lorina +{ + +namespace detail +{ + +/* https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf */ +inline std::istream& getline( std::istream& is, std::string& t ) +{ + t.clear(); + + /* The characters in the stream are read one-by-one using a std::streambuf. + * That is faster than reading them one-by-one using the std::istream. + * Code that uses streambuf this way must be guarded by a sentry object. + * The sentry object performs various tasks, + * such as thread synchronization and updating the stream state. + */ + + std::istream::sentry se( is, true ); + std::streambuf* sb = is.rdbuf(); + + for ( ;; ) + { + int const c = sb->sbumpc(); + switch ( c ) + { + /* deal with file endings */ + case '\n': + return is; + case '\r': + if ( sb->sgetc() == '\n' ) + { + sb->sbumpc(); + } + return is; + + /* handle the case when the last line has no line ending */ + case std::streambuf::traits_type::eof(): + if ( t.empty() ) + { + is.setstate( std::ios::eofbit ); + } + return is; + + default: + t += (char)c; + } + } +} + +template +inline std::string join( const T& t, const std::string& sep ) +{ + return std::accumulate( + std::next( t.begin() ), t.end(), std::string( t[0] ), + [&]( const std::string& s, const typename T::value_type& v ) { return s + sep + std::string( v ); } ); +} + +/* string utils are from https://stackoverflow.com/a/217605 */ + +inline void ltrim( std::string& s ) +{ + s.erase( s.begin(), std::find_if( s.begin(), s.end(), []( int ch ) { + return !std::isspace( ch ); + } ) ); +} + +inline void rtrim( std::string& s ) +{ + s.erase( std::find_if( s.rbegin(), s.rend(), []( int ch ) { + return !std::isspace( ch ); + } ) + .base(), + s.end() ); +} + +inline void trim( std::string& s ) +{ + ltrim( s ); + rtrim( s ); +} + +inline std::string trim_copy( std::string s ) +{ + trim( s ); + return s; +} + +inline void foreach_line_in_file_escape( std::istream& in, const std::function& f ) +{ + std::string line, line2; + + while ( !getline( in, line ).eof() ) + { + trim( line ); + + while ( line.back() == '\\' ) + { + line.pop_back(); + trim( line ); + + /* check if failbit has been set */ + if ( !getline( in, line2 ) ) + { + assert( false ); + std::abort(); + } + line += line2; + } + + if ( !f( line ) ) + { + break; + } + } +} + +// https://stackoverflow.com/a/14266139 +inline std::vector split( const std::string& str, const std::string& sep ) +{ + std::vector result; + + size_t last = 0; + size_t next = 0; + std::string substring; + while ( ( next = str.find( sep, last ) ) != std::string::npos ) + { + substring = str.substr( last, next - last ); + if ( substring.length() > 0 ) + { + std::string sub = str.substr( last, next - last ); + sub.erase( std::remove( sub.begin(), sub.end(), ' ' ), sub.end() ); + result.push_back( sub ); + } + last = next + 1; + } + + substring = str.substr( last ); + substring.erase( std::remove( substring.begin(), substring.end(), ' ' ), substring.end() ); + result.push_back( substring ); + + return result; +} + +#ifndef _WIN32 +inline std::string word_exp_filename( const std::string& filename ) +{ + std::string result; + + wordexp_t p; + wordexp( filename.c_str(), &p, 0 ); + + for ( auto i = 0u; i < p.we_wordc; ++i ) + { + if ( !result.empty() ) + { + result += " "; + } + result += std::string( p.we_wordv[i] ); + } + + wordfree( &p ); + + return result; +} +#else +inline const std::string& word_exp_filename( const std::string& filename ) +{ + return filename; +} +#endif + +#ifndef _WIN32 +inline std::string basename( const std::string& filepath ) +{ + return std::string( ::basename( const_cast( filepath.c_str() ) ) ); +} +#endif + +inline bool starts_with( std::string const& s, std::string const& match ) +{ + return ( s.substr( 0, match.size() ) == match ); +} + +} // namespace detail +} // namespace lorina + +/*! \endcond */ diff --git a/third-party/mockturtle/lib/lorina/lorina/diagnostics.hpp b/third-party/mockturtle/lib/lorina/lorina/diagnostics.hpp new file mode 100644 index 00000000000..2baf5f96598 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/diagnostics.hpp @@ -0,0 +1,322 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file diagnostics.hpp + \brief Implements diagnostics. + + \author Heinz Riener +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace lorina +{ + +class diagnostic_builder; +class diagnostic_consumer; +class diagnostic_engine; + +enum class diagnostic_level +{ + ignore = 1, + note = 2, + remark = 3, + warning = 4, + error = 5, + fatal = 6, +}; + +#include "diagnostics.inc" + +/*! \brief A diagnostic engine. */ +class diagnostic_engine +{ +public: + using desc_type = std::pair; + +public: + /*! \brief Constructs a diagnostic engine. + * + * \param client Diagnostic client + */ + explicit diagnostic_engine( diagnostic_consumer *client ); + + /*! \brief Creates a diagnostic builder. + * + * \param id ID of diagnostic + */ + virtual inline diagnostic_builder report( diag_id id ); + + /*! \brief Emits a diagnostic message. + * + * \param id ID of diagnostic + * \param args Arguments + */ + virtual inline void emit( diag_id id, std::vector const& args = {} ) const; + + /*! \brief Create custom diagnostic. + * + * \param level Severity level + * \param message Diagnostic message + */ + diag_id create_id( diagnostic_level level, std::string const& message ); + + /*! \brief Return the number of emitted diagnostics. */ + uint64_t get_num_diagnostics() const; + +private: + /*! \brief Emit diagnostics with static ID. + * + * \param id ID of diagnostic + * \param args Arguments + */ + void emit_static_diagnostic( diag_id id, std::vector const& args ) const; + + /*! \brief Emit diagnostics with custom ID. + * + * \param id ID of diagnostic + * \param args Arguments + */ + void emit_custom_diagnostic( diag_id id, std::vector const& args ) const; + +protected: + diagnostic_consumer *client_ = nullptr; /*!< Diagnostic client. */ + std::vector custom_diag_info; /*!< Custom diagnostics. */ + std::map custom_diag_ids; /*!< Map from custom ID to diagnostic. */ + mutable uint64_t num_diagnostics{0}; +}; /* diagnostic_engine */ + +/*! \brief A builder for diagnostics. + * + * An object that encapsulates a diagnostic. The diagnostic may take + * additional parameters and is finally issued at the end of its + * life time. + */ +class diagnostic_builder +{ +public: + /*! \brief Constructs a diagnostic builder. + * + * \param engine Diagnostic engine + * \param id ID of diagnostic + */ + inline explicit diagnostic_builder( diagnostic_engine& engine, diag_id id ); + + /*! \brief Destructs the diagnostic builder and issues the diagnostic. */ + inline ~diagnostic_builder(); + + /*! \brief Add argument. + * + * \param s String argument + */ + inline diagnostic_builder& add_argument( std::string const& s ); + +private: + diagnostic_engine& engine_; /*!< diagnostic engine */ + diag_id id_; /*!< id of diagnostic */ + std::vector args_; /*!< arguments for diagnostic */ +}; /* diagnostic_builder */ + +/*! \brief A consumer for diagnostics. */ +class diagnostic_consumer +{ +public: + diagnostic_consumer() = default; + virtual ~diagnostic_consumer() = default; + + /*! \brief Handle diagnostic. + * + * \param level Severity level + * \param message Diagnostic message + */ + virtual void handle_diagnostic( diagnostic_level level, std::string const& message ) const + { + (void)level; + (void)message; + } +}; + +/*! \brief A consumer for diagnostics. */ +class text_diagnostics : public diagnostic_consumer +{ +public: + text_diagnostics() = default; + virtual ~text_diagnostics() = default; + + /*! \brief Handle diagnostic. + * + * \param level Severity level + * \param message Diagnostic message + */ + void handle_diagnostic( diagnostic_level level, std::string const& message ) const override + { + switch ( level ) + { + case diagnostic_level::ignore: + break; + case diagnostic_level::note: + { + fmt::print( stdout, fmt::emphasis::bold | fg( fmt::terminal_color::bright_green ), "[i]" ); + fmt::print( stdout, fmt::emphasis::bold | fg( fmt::color::white ), " {}\n", message ); + } + break; + case diagnostic_level::remark: + { + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::terminal_color::bright_green ), "[I]" ); + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::color::white ), " {}\n", message ); + } + break; + case diagnostic_level::warning: + { + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::terminal_color::bright_magenta ),"[w]" ); + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::color::white ), " {}\n", message ); + } + break; + case diagnostic_level::error: + { + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::terminal_color::bright_red ), "[e]" ); + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::color::white ), " {}\n", message ); + } + break; + case diagnostic_level::fatal: + default: + { + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::terminal_color::bright_red ), "[E]" ); + fmt::print( stderr, fmt::emphasis::bold | fg( fmt::color::white ), " {}\n", message ); + } + break; + } + } +}; + +inline diagnostic_builder::diagnostic_builder( diagnostic_engine& engine, diag_id id ) + : engine_( engine ), id_( id ) +{ +} + +inline diagnostic_builder::~diagnostic_builder() +{ + engine_.emit( id_, args_ ); +} + +inline diagnostic_builder& diagnostic_builder::add_argument( std::string const& s ) +{ + args_.emplace_back( s ); + return *this; +} + +inline diagnostic_engine::diagnostic_engine( diagnostic_consumer *client ) + : client_( client ) +{ +} + +inline diag_id diagnostic_engine::create_id( diagnostic_level level, std::string const& message ) +{ + desc_type desc{level, message}; + diag_id id{diag_id( custom_diag_info.size() )}; + custom_diag_ids.emplace( desc, id ); + custom_diag_info.emplace_back( desc ); + return diag_id( uint32_t( diag_id::NUM_STATIC_ERROR_IDS ) + uint32_t( id ) ); +} + +inline uint64_t diagnostic_engine::get_num_diagnostics() const +{ + return num_diagnostics; +} + +inline diagnostic_builder diagnostic_engine::report( diag_id id ) +{ + return diagnostic_builder( *this, id ); +} + +inline void diagnostic_engine::emit_static_diagnostic( diag_id id, std::vector const& args ) const +{ + assert( id < diag_id::NUM_STATIC_ERROR_IDS ); + diagnostic_level const level = diag_info[uint32_t( id )].first; + std::string const message = diag_info[uint32_t( id )].second; + switch ( args.size() ) + { + case 1: + client_->handle_diagnostic( level, fmt::format( fmt::runtime(message), args[0] ) ); + break; + case 2: + client_->handle_diagnostic( level, fmt::format( fmt::runtime(message), args[0], args[1] ) ); + break; + case 3: + client_->handle_diagnostic( level, fmt::format( fmt::runtime(message), args[0], args[1], args[2] ) ); + break; + default: + case 0: + assert( args.size() == 0 ); + client_->handle_diagnostic( level, message ); + } +} + +inline void diagnostic_engine::emit_custom_diagnostic( diag_id id, std::vector const& args ) const +{ + uint32_t const custom_id = uint32_t( id ) - uint32_t( diag_id::NUM_STATIC_ERROR_IDS ); + assert( uint32_t( custom_id ) < custom_diag_info.size() ); + diagnostic_level const level = custom_diag_info[custom_id].first; + std::string const message = custom_diag_info[custom_id].second; + switch ( args.size() ) + { + case 1: + client_->handle_diagnostic( level, fmt::format( fmt::runtime(message), args[0] ) ); + break; + case 2: + client_->handle_diagnostic( level, fmt::format( fmt::runtime(message), args[0], args[1] ) ); + break; + case 3: + client_->handle_diagnostic( level, fmt::format( fmt::runtime(message), args[0], args[1], args[2] ) ); + break; + default: + case 0: + assert( args.size() == 0 ); + client_->handle_diagnostic( level, message ); + } +} + +inline void diagnostic_engine::emit( diag_id id, std::vector const& args ) const +{ + assert( client_ != nullptr ); + ++num_diagnostics; + if ( id < diag_id::NUM_STATIC_ERROR_IDS ) + { + emit_static_diagnostic( id, args ); + } + else + { + emit_custom_diagnostic( id, args ); + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/diagnostics.inc b/third-party/mockturtle/lib/lorina/lorina/diagnostics.inc new file mode 100644 index 00000000000..0c96e2d5967 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/diagnostics.inc @@ -0,0 +1,91 @@ +/*! pre-defined static diagnostic IDs */ +enum class diag_id +{ + /* general */ + ERR_FILE_OPEN, + ERR_PARSE_LINE, + WRN_UNRESOLVED_DEPENDENCY, + ERR_UNSUPPORTED_KEYWORD, + + /* AIGER */ + ERR_AIGER_HEADER, + ERR_AIGER_LATCH_DECLARATION, + ERR_AIGER_AND_DECLARATION, + + /* BLIF */ + ERR_BLIF_LATCH_FORMAT, + + /* VERILOG */ + ERR_VERILOG_MODULE_HEADER, + ERR_VERILOG_INPUT_DECLARATION, + ERR_VERILOG_OUTPUT_DECLARATION, + ERR_VERILOG_WIRE_DECLARATION, + ERR_VERILOG_ASSIGNMENT, + ERR_VERILOG_MODULE_INSTANTIATION_STATEMENT, + ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_MODULE, + ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_PIN, + ERR_VERILOG_ASSIGNMENT_RHS, + + /* DIMACS */ + ERR_DIMACS_MISSING_SPEC, + + /* GENLIB */ + ERR_GENLIB_UNEXPECTED_STRUCTURE, + ERR_GENLIB_GATE, + ERR_GENLIB_EXPRESSION, + ERR_GENLIB_PIN, + ERR_GENLIB_PIN_PHASE, + ERR_GENLIB_FAILED, + + /* SUPER */ + ERR_SUPER_INFO, + ERR_SUPER_UNEXPECTED_STRUCTURE, + ERR_SUPER_GATE, + + /* sentinel element to mark the end */ + NUM_STATIC_ERROR_IDS, +}; + + +/*! pre-defined static diagnostics */ +static std::pair const diag_info[] = { + { diagnostic_level::fatal, "could not open file `{}`" }, + { diagnostic_level::fatal, "could not parse line `{}`" }, + { diagnostic_level::warning, "unresolved dependencies: `{}` requires `{}`" }, + { diagnostic_level::fatal, "unsupported keyword `{}`" }, + + /* AIGER */ + { diagnostic_level::fatal, "could not parse AIGER header `{}`" }, + { diagnostic_level::fatal, "could not parse declaration of LATCH `{}`" }, + { diagnostic_level::fatal, "could not parse declaration of AND gate `{}`" }, + + /* BLIF */ + { diagnostic_level::fatal, "latch format not supported `{}`" }, + + /* VERILOG */ + { diagnostic_level::fatal, "cannot parse module header" }, + { diagnostic_level::fatal, "cannot parse input declaration" }, + { diagnostic_level::fatal, "cannot parse output declaration" }, + { diagnostic_level::fatal, "cannot parse wire declaration" }, + { diagnostic_level::fatal, "cannot parse assign statement" }, + { diagnostic_level::fatal, "cannot parse module instantiation statement" }, + { diagnostic_level::fatal, "cannot instantiate module: module `{}` has not been declared (ensure that the module has been parsed before)" }, + { diagnostic_level::fatal, "cannot instantiate module: pin `{}` has not been declared in module `{}`" }, + { diagnostic_level::fatal, "cannot parse expression on right-hand side of assign `{}`" }, + + /* DIMACS */ + { diagnostic_level::fatal, "missing problem specification line" }, + + /* GENLIB */ + { diagnostic_level::fatal, "line `{}` has unexpected structure (expected `GATE ;`)`" }, + { diagnostic_level::fatal, "line `{}` does not start with keyword `GATE`" }, + { diagnostic_level::fatal, "expression `{}` is not immediately terminated with `;`" }, + { diagnostic_level::fatal, "unexpected `{}` token (expected `PIN`)" }, + { diagnostic_level::fatal, "unknown PIN phase type `{}` (expected `INV`, `NONINV`, or `UNKNOWN`)" }, + { diagnostic_level::fatal, "parsing failed at token `{}`" }, + + /* SUPER */ + { diagnostic_level::fatal, "line `{}` has unexpected structure (expected only one data)" }, + { diagnostic_level::fatal, "line `{}` has a wrong structure (too few fields or exceeding the limit)" }, + { diagnostic_level::fatal, "gate `{}` has a wrong number of fanin (zero or exceeding the limit)" }, +}; diff --git a/third-party/mockturtle/lib/lorina/lorina/dimacs.hpp b/third-party/mockturtle/lib/lorina/lorina/dimacs.hpp new file mode 100644 index 00000000000..dc44b9a1949 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/dimacs.hpp @@ -0,0 +1,221 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dimacs.hpp + \brief Implements DIMACS parser + + \author Bruno Schmitt + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "common.hpp" +#include "diagnostics.hpp" +#include "detail/utils.hpp" +#include +#include +#include +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for the DIMACS format. + * + * Callbacks for reading the DIMACS format. + */ +class dimacs_reader +{ +public: + /*! \brief Callback method for parsed format. + * + * \param format The format (cnf or dnf) + */ + virtual void on_format( const std::string& format ) const + { + (void)format; + } + + /*! \brief Callback method for parsed number of variables. + * + * \param number_of_variables Number of variables + */ + virtual void on_number_of_variables( uint64_t number_of_variables ) const + { + (void)number_of_variables; + } + + /*! \brief Callback method for parsed number of clauses. + * + * \param number_of_clauses Number of clauses + */ + virtual void on_number_of_clauses( uint64_t number_of_clauses ) const + { + (void)number_of_clauses; + } + + /*! \brief Callback method for parsed clause. + * + * \param clause A clause of a logic function + */ + virtual void on_clause( const std::vector& clause ) const + { + (void)clause; + } + + /*! \brief Callback method for parse end. + * + */ + virtual void on_end() const {} + +}; /* dimacs_reader */ + +namespace dimacs_regex +{ +static std::regex problem_spec( R"(^p\s+([cd]nf)\s+([0-9]+)\s+([0-9]+)$)" ); +static std::regex clause( R"(((-?[1-9]+)+ +)+0)" ); + +} // namespace dimacs_regex + +/*! \brief Reader function for the DIMACS format. + * + * Reads DIMACS format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader A DIMACS reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_dimacs( std::istream& in, const dimacs_reader& reader, diagnostic_engine* diag = nullptr ) +{ + auto loc = 0ul; + auto errors = 0ul; + bool found_spec = false; + + std::smatch m; + detail::foreach_line_in_file_escape( in, [&]( const std::string& line ) { + ++loc; + + /* empty line or comment */ + if ( line.empty() || line[0u] == 'c' ) + return true; + + if ( std::regex_search( line, m, dimacs_regex::problem_spec ) ) + { + reader.on_format( std::string( m[1] ) ); + reader.on_number_of_variables( std::atol( std::string( m[2] ).c_str() ) ); + reader.on_number_of_clauses( std::atol( std::string( m[3] ).c_str() ) ); + found_spec = true; + return true; + } + + if ( found_spec == false ) + { + if ( diag ) + { + diag->report( diag_id::ERR_DIMACS_MISSING_SPEC ); + } + ++errors; + return false; + } + + auto clauses_begin = std::sregex_iterator( line.begin(), line.end(), dimacs_regex::clause ); + auto clauses_end = std::sregex_iterator(); + + if ( std::distance( clauses_begin, clauses_end ) == 0u ) + { + if ( diag ) + { + diag->report( diag_id::ERR_PARSE_LINE ).add_argument( line ); + } + ++errors; + return false; + } + + for ( std::sregex_iterator i = clauses_begin; i != clauses_end; ++i ) + { + std::smatch match = *i; + std::stringstream ss( match[0].str() ); + std::string lit_str; + std::vector clause; + while ( std::getline( ss, lit_str, ' ' ) ) + { + int const lit = std::atol( lit_str.c_str() ); + if ( lit != 0 ) + { + clause.push_back( lit ); + } + } + reader.on_clause( clause ); + } + return true; + } ); + + if ( errors > 0 ) + { + return return_code::parse_error; + } + else + { + reader.on_end(); + return return_code::success; + } +} + +/*! \brief Reader function for DIMACS format. + * + * Reads DIMACS format from a file and invokes a callback method for each + * parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader A DIMACS reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_dimacs( const std::string& filename, const dimacs_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_dimacs( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/genlib.hpp b/third-party/mockturtle/lib/lorina/lorina/genlib.hpp new file mode 100644 index 00000000000..32583baf98a --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/genlib.hpp @@ -0,0 +1,383 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file genlib.hpp + \brief Implements GENLIB parser + + \author Heinz Riener + \author Shubham Rai + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "common.hpp" +#include "detail/utils.hpp" +#include "diagnostics.hpp" +#include +#include +#include +#include +#include + +namespace lorina +{ + +enum class phase_type : uint8_t +{ + INV = 0, + NONINV = 1, + UNKNOWN = 2, +}; + +// PIN +struct pin_spec +{ + std::string name; + phase_type phase; + double input_load; + double max_load; + double rise_block_delay; + double rise_fanout_delay; + double fall_block_delay; + double fall_fanout_delay; +}; /* pin_spec */ + +/*! \brief A reader visitor for a GENLIB format. + * + * Callbacks for the GENLIB format. + */ +class genlib_reader +{ +public: + virtual void on_gate( std::string const& name, std::string const& expression, double area, std::vector const& pins, std::string const& output_pin ) const + { + (void)name; + (void)expression; + (void)area; + (void)pins; + (void)output_pin; + } +}; /* genlib_reader */ + +/*! \brief Parse for the GENLIB format. + * + */ +class genlib_parser +{ +public: + explicit genlib_parser( std::istream& in, genlib_reader const& reader, diagnostic_engine* diag ) + : in( in ) + , reader( reader ) + , diag( diag ) + {} + +public: + bool run() + { + std::string line; + std::string entry; + bool new_line; + + while ( true ) + { + new_line = std::getline( in, line ) ? true : false; + + if ( new_line == false ) + { + break; + } + + /* remove whitespaces */ + detail::trim( line ); + + /* skip comments and empty lines */ + if ( line[0] != '#' && !line.empty() ) + { + entry = line + " "; + break; + } + } + + while ( new_line ) + { + new_line = std::getline( in, line ) ? true : false; + + if ( new_line == false ) + { + if ( !parse_gate_definition( entry ) ) + { + return false; + } + break; + } + + /* remove whitespaces */ + detail::trim( line ); + + /* skip comments and empty lines */ + if ( line[0] == '#' || line.empty() ) + { + continue; + } + + if ( line.find( "GATE" ) != std::string::npos ) + { + if ( !parse_gate_definition( entry ) ) + { + return false; + } + entry = line + " "; + } + else + { + entry.append( line + " " ); + } + } + + return true; + } + +private: + bool parse_gate_definition( std::string const& line ) + { + std::stringstream ss( line ); + std::string const deliminators{" \t\r\n"}; + + std::string token; + bool in_equation = false; + + std::vector tokens; + while ( std::getline( ss, token ) ) + { + std::size_t prev = 0, pos, eq_pos; + while ( ( pos = line.find_first_of( deliminators, prev ) ) != std::string::npos ) + { + /* equation */ + if ( tokens.size() == 3 && !in_equation ) + { + /* equation is not finished */ + if ( token.substr( prev, pos - prev ).find_first_of( ";" ) == std::string::npos ) + { + in_equation = true; + eq_pos = prev; + prev = pos + 1; + continue; + } + } + if ( in_equation ) + { + if ( token.substr( prev, pos - prev ).find_first_of( ";" ) != std::string::npos ) + { + in_equation = false; + prev = eq_pos; + } + else + { + prev = pos + 1; + continue; + } + } + if ( pos > prev ) + { + tokens.emplace_back( token.substr( prev, pos - prev ) ); + } + prev = pos + 1; + } + + if ( prev < line.length() ) + { + tokens.emplace_back( token.substr( prev, std::string::npos ) ); + } + } + + if ( tokens.size() < 4u ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_UNEXPECTED_STRUCTURE ).add_argument( line ); + } + return false; + } + + if ( tokens[0] != "GATE" ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_GATE ).add_argument( line ); + } + return false; + } + auto const beg = tokens[3].find_first_of( "=" ); + auto const end = tokens[3].find_first_of( ";" ); + if ( beg == std::string::npos || end == std::string::npos ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_EXPRESSION ).add_argument( tokens[3] ); + } + return false; + } + + std::string const& name = tokens[1]; + std::string const& expression = tokens[3].substr( beg + 1, end - beg - 1 ); + std::string const& output_name = detail::trim_copy( tokens[3].substr( 0, beg ) ); + double const area = std::stod( tokens[2] ); + + std::vector pins; + + bool generic_pin{false}; + uint64_t i{4}; + for ( ; i+8 < tokens.size(); i += 9 ) + { + /* check PIN specification */ + if ( tokens[i] != "PIN" ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_PIN ).add_argument( tokens[i] ); + } + return false; + } + + std::string const& name = tokens[i+1]; + if ( tokens[i+1] == "*" ) + { + generic_pin = true; + } + phase_type phase{phase_type::UNKNOWN}; + if ( tokens[i+2] == "INV" ) + { + phase = phase_type::INV; + } + else if ( tokens[i+2] == "NONINV" ) + { + phase = phase_type::NONINV; + } + else + { + if ( tokens[i+2] != "UNKNOWN" ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_PIN_PHASE ).add_argument( tokens[i+1] ); + } + } + } + + double const input_load = std::stod( tokens[i+3] ); + double const max_load = std::stod( tokens[i+4] ); + double const rise_block_delay = std::stod( tokens[i+5] ); + double const rise_fanout_delay = std::stod( tokens[i+6] ); + double const fall_block_delay = std::stod( tokens[i+7] ); + double const fall_fanout_delay = std::stod( tokens[i+8] ); + + pins.emplace_back( pin_spec{name,phase,input_load,max_load,rise_block_delay,rise_fanout_delay,fall_block_delay,fall_fanout_delay} ); + } + + if ( pins.size() > 1 && generic_pin ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_PIN ).add_argument( line ); + } + return false; + } + + if ( i != tokens.size() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_FAILED ).add_argument( tokens[i] ); + } + return false; + } + + reader.on_gate( name, expression, area, pins, output_name ); + return true; + } + +protected: + std::istream& in; + genlib_reader const& reader; + diagnostic_engine* diag; +}; /* genlib_parser */ + + +/*! \brief Reader function for the GENLIB format. + * + * Reads GENLIB format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader GENLIB reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_genlib( std::istream& in, const genlib_reader& reader, diagnostic_engine* diag = nullptr ) +{ + genlib_parser parser( in, reader, diag ); + auto result = parser.run(); + if ( !result ) + { + return return_code::parse_error; + } + else + { + return return_code::success; + } +} + +/*! \brief Reader function for the GENLIB format. + * + * Reads GENLIB format from a file and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader GENLIB reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_genlib( const std::string& filename, const genlib_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_genlib( in, reader, diag ); + in.close(); + return ret; + } +} + +} /* lorina */ diff --git a/third-party/mockturtle/lib/lorina/lorina/lorina.hpp b/third-party/mockturtle/lib/lorina/lorina/lorina.hpp new file mode 100644 index 00000000000..7445031f788 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/lorina.hpp @@ -0,0 +1,42 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file lorina.hpp + \brief Main header for lorina + + \author Heinz Riener +*/ + +#pragma once + +#include "aiger.hpp" +#include "bench.hpp" +#include "blif.hpp" +#include "bristol.hpp" +#include "dimacs.hpp" +#include "genlib.hpp" +#include "pla.hpp" +#include "verilog.hpp" \ No newline at end of file diff --git a/third-party/mockturtle/lib/lorina/lorina/pla.hpp b/third-party/mockturtle/lib/lorina/lorina/pla.hpp new file mode 100644 index 00000000000..c8a96565f0c --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/pla.hpp @@ -0,0 +1,360 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file pla.hpp + \brief Implements pla parser + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "common.hpp" +#include "diagnostics.hpp" +#include "detail/utils.hpp" +#include +#include +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for the PLA format. + * + * Callbacks for reading the PLA format. + */ +class pla_reader +{ +public: + /*! \brief Callback method for parsed number of inputs. + * + * \param number_of_inputs Number of inputs + */ + virtual void on_number_of_inputs( uint64_t number_of_inputs ) const + { + (void)number_of_inputs; + } + + /*! \brief Callback method for parsed number of outputs. + * + * \param number_of_outputs Number of outputs + */ + virtual void on_number_of_outputs( uint64_t number_of_outputs ) const + { + (void)number_of_outputs; + } + + /*! \brief Callback method for parsed number of terms. + * + * \param number_of_terms Number of terms + */ + virtual void on_number_of_terms( uint64_t number_of_terms ) const + { + (void)number_of_terms; + } + + /*! \brief Callback method for parsed keyword-value pair. + * + * \param keyword Parsed keyword + * \param value Parsed value + * \return true if keyword is recognized and handled, false if keyword is not supported + */ + virtual bool on_keyword( const std::string& keyword, const std::string& value ) const + { + (void)keyword; + (void)value; + return false; + } + + /*! \brief Callback method for parsed end. + * + */ + virtual void on_end() const {} + + /*! \brief Callback method for parsed term. + * + * \param term A term of a logic function + * \param out Output bit of term + */ + virtual void on_term( const std::string& term, const std::string& out ) const + { + (void)term; + (void)out; + } +}; /* pla_reader */ + +/*! \brief A writer for the PLA format. + * + * Callbacks for writing the PLA format. + * + */ +class pla_writer +{ +public: + /*! \brief Constructs a PLA writer. + * + * \param os Output stream + */ + explicit pla_writer( std::ostream& os ) + : _os( os ) + {} + + /*! \brief Callback method for writing number of inputs. + * + * \param number_of_inputs Number of inputs + */ + virtual void on_number_of_inputs( uint64_t number_of_inputs ) const + { + _os << fmt::format( ".i {}\n", number_of_inputs ); + } + + /*! \brief Callback method for writing number of outputs. + * + * \param number_of_outputs Number of outputs + */ + virtual void on_number_of_outputs( uint64_t number_of_outputs ) const + { + _os << fmt::format( ".o {}\n", number_of_outputs ); + } + + /*! \brief Callback method for writing number of terms. + * + * \param number_of_terms Number of terms + */ + virtual void on_number_of_terms( uint64_t number_of_terms ) const + { + _os << fmt::format( ".p {}\n", number_of_terms ); + } + + /*! \brief Callback method for writing keyword-value pair. + * + * \param keyword Keyword + * \param value Value + */ + virtual void on_keyword( const std::string& keyword, const std::string& value ) const + { + _os << fmt::format( ".{} {}\n", keyword, value ); + } + + /*! \brief Callback method for writing end. + * + */ + virtual void on_end() const + { + _os << ".e\n"; + } + + /*! \brief Callback method for writing term. + * + * \param term A term of a logic function + * \param out Output bit of term + */ + virtual void on_term( const std::string& term, const std::string& out ) const + { + _os << term << ' ' << out << '\n'; + } + +protected: + std::ostream& _os; /*!< Output stream */ +}; /* pla_writer */ + +/*! \brief A PLA reader for prettyprinting PLA. + * + * Callbacks for prettyprinting of PLA. + * + */ +class pla_pretty_printer : public pla_reader +{ +public: + /*! \brief Constructor of the PLA pretty printer. + * + * \param os Output stream + */ + pla_pretty_printer( std::ostream& os = std::cout ) + : _os( os ) + { + } + + virtual void on_number_of_inputs( uint64_t number_of_inputs ) const override + { + _os << ".i " << number_of_inputs << std::endl; + } + + virtual void on_number_of_outputs( uint64_t number_of_outputs ) const override + { + _os << ".o " << number_of_outputs << std::endl; + } + + virtual void on_number_of_terms( uint64_t number_of_terms ) const override + { + _os << ".p " << number_of_terms << std::endl; + } + + virtual bool on_keyword( const std::string& keyword, const std::string& value ) const override + { + _os << fmt::format( ".{} {}", keyword, value ) << std::endl; + return true; + } + + virtual void on_end() const override + { + _os << ".e" << std::endl; + } + + virtual void on_term( const std::string& term, const std::string& out ) const override + { + _os << term << ' ' << out << std::endl; + } + + std::ostream& _os; /*!< Output stream */ +}; /* pla_pretty_printer */ + +namespace pla_regex +{ +static std::regex keyword( R"(^\.([^\s]*)(?:\s+(.+))?$)" ); +static std::regex term( R"(^([01\-]+)\s+([01\-]+)$)" ); + +} // namespace pla_regex + +/*! \brief Reader function for the PLA format. + * + * Reads PLA format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader A PLA reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_pla( std::istream& in, const pla_reader& reader, diagnostic_engine* diag = nullptr ) +{ + auto loc = 0ul; + auto errors = 0ul; + + std::smatch m; + detail::foreach_line_in_file_escape( in, [&]( const std::string& line ) { + ++loc; + + /* empty line or comment */ + if ( line.empty() || line[0u] == '#' ) + return true; + + if ( std::regex_search( line, m, pla_regex::keyword ) ) + { + if ( m[1] == "i" ) + { + reader.on_number_of_inputs( std::atol( std::string( m[2] ).c_str() ) ); + return true; + } + else if ( m[1] == "o" ) + { + reader.on_number_of_outputs( std::atol( std::string( m[2] ).c_str() ) ); + return true; + } + else if ( m[1] == "p" ) + { + reader.on_number_of_terms( std::atol( std::string( m[2] ).c_str() ) ); + return true; + } + else if ( m[1] == "e" ) + { + reader.on_end(); + return true; + } + else + { + if ( reader.on_keyword( std::string( m[1] ), std::string( m[2] ) ) ) + { + return true; + } + + if ( diag ) + { + diag->report( diag_id::ERR_UNSUPPORTED_KEYWORD ).add_argument( line ); + } + ++errors; + return true; /* understood error case, go on parsing */ + } + } + + /* [01-]+ [01-]+ */ + if ( std::regex_match( line, m, pla_regex::term ) ) + { + std::string out = m[2]; + reader.on_term( m[1], m[2] ); + return true; + } + + if ( diag ) + { + diag->report( diag_id::ERR_PARSE_LINE ).add_argument( line ); + } + ++errors; + return true; /* understood error case, go on parsing */ + } ); + + if ( errors > 0 ) + { + return return_code::parse_error; + } + else + { + return return_code::success; + } +} + +/*! \brief Reader function for PLA format. + * + * Reads PLA format from a file and invokes a callback method for each + * parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader A PLA reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_pla( const std::string& filename, const pla_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_pla( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/super.hpp b/third-party/mockturtle/lib/lorina/lorina/super.hpp new file mode 100644 index 00000000000..7cd0f44eda8 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/super.hpp @@ -0,0 +1,287 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file super.hpp + \brief Implements SUPER parser for files generated by ABC + + \author Alessandro Tempia Calvino + \author Shubham Rai +*/ + +#pragma once + +#include "common.hpp" +#include "detail/utils.hpp" +#include "diagnostics.hpp" +#include +#include +#include +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for a super format. + * + * Callbacks for the super format. + */ +class super_reader +{ +public: + virtual void on_super_info( std::string const& genlib_name, uint32_t max_num_vars, uint32_t max_supergates, uint32_t num_lines ) const + { + (void) genlib_name; + (void) max_num_vars; + (void) max_supergates; + (void) num_lines; + } + + virtual void on_supergate( std::string const& name, bool const& is_super, std::vector const& fanins_id ) const + { + (void)name; + (void)is_super; + (void)fanins_id; + } +}; /* super_reader */ + +/*! \brief Parse for the SUPER format. + * + */ +class super_parser +{ +public: + explicit super_parser( std::istream& in, super_reader const& reader, diagnostic_engine* diag ) + : in( in ) + , reader( reader ) + , diag( diag ) + { + } + +public: + bool run() + { + std::string line; + uint32_t info_lines = 0u; + uint32_t max_num_vars = 0u; + + std::vector info_vec; + + while ( std::getline( in, line ) ) + { + /* remove whitespaces */ + detail::trim( line ); + + /* skip comments and empty lines */ + if ( line[0] == '#' || line[0] == '\0' || line.empty() ) + { + continue; + } + + if ( info_lines < 4 ) + { + if ( !parse_file_info( line, info_vec ) ) + { + return false; + } + if ( info_lines == 1 ) + { + max_num_vars = std::stod( info_vec[1] ); + } + ++info_lines; + } + else + { + if ( !parse_gate_definition( line, max_num_vars ) ) + { + return false; + } + } + } + + return true; + } + +private: + bool parse_file_info( std::string const& line, std::vector& info_vec ) + { + std::stringstream ss( line ); + std::string token; + + std::vector tokens; + + while ( std::getline( ss, token ) ) + { + tokens.emplace_back( token ); + info_vec.emplace_back( token ); + } + + if ( line.find_first_of( " " ) != std::string::npos ) + { + if ( diag ) + { + diag->report( diag_id::ERR_SUPER_INFO ).add_argument( line ); + } + return false; + } + + if ( info_vec.size() == 4 ) + { + reader.on_super_info( info_vec[0], std::stoi( info_vec[1] ), std::stoi( info_vec[2] ), std::stoi( info_vec[3] ) ); + } + + return true; + } + + bool parse_gate_definition( std::string const& line, uint32_t const& max_num_vars ) + { + std::stringstream ss( line ); + std::string const deliminators{ " \t\r\n" }; + + std::string token; + std::vector tokens; + + std::string name; + std::vector fanins_id; + + while ( std::getline( ss, token ) ) + { + std::size_t prev = 0, pos; + while ( ( pos = line.find_first_of( deliminators, prev ) ) != std::string::npos ) + { + if ( pos > prev ) + { + tokens.emplace_back( token.substr( prev, pos - prev ) ); + } + prev = pos + 1; + } + + if ( prev < line.length() ) + { + tokens.emplace_back( token.substr( prev, std::string::npos ) ); + } + } + + if ( tokens.size() < 2 || tokens.size() > max_num_vars + 2 ) + { + if ( diag ) + { + diag->report( diag_id::ERR_SUPER_UNEXPECTED_STRUCTURE ).add_argument( line ); + } + return false; + } + + bool is_super = false; + uint64_t i{2}; + if ( tokens[0] == "*" ) + { + is_super = true; + name = tokens[1]; + } + else + { + name = tokens[0]; + i = 1u; + } + + for ( auto j = i; j < tokens.size(); ++j ) + { + fanins_id.emplace_back( std::stod( tokens[j] ) ); + } + + if ( fanins_id.size() == 0 ) + { + if ( diag ) + { + diag->report( diag_id::ERR_SUPER_GATE ).add_argument( line ); + } + return false; + } + + reader.on_supergate( name, is_super, fanins_id ); + return true; + } + +protected: + std::istream& in; + super_reader const& reader; + diagnostic_engine* diag; +}; /* super_parser */ + +/*! \brief Reader function for the SUPER format. + * + * Reads SUPER format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader SUPER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_super( std::istream& in, const super_reader& reader, diagnostic_engine* diag = nullptr ) +{ + super_parser parser( in, reader, diag ); + auto result = parser.run(); + if ( !result ) + { + return return_code::parse_error; + } + else + { + return return_code::success; + } +} + +/*! \brief Reader function for the SUPER format. + * + * Reads SUPER format from a .super file generated by ABC and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader SUPER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_super( const std::string& filename, const super_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_super( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/verilog.hpp b/third-party/mockturtle/lib/lorina/lorina/verilog.hpp new file mode 100644 index 00000000000..1e34ea56888 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/verilog.hpp @@ -0,0 +1,1774 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file verilog.hpp + \brief Implements simplistic Verilog parser + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "common.hpp" +#include "detail/tokenizer.hpp" +#include "detail/utils.hpp" +#include "diagnostics.hpp" +#include "verilog_regex.hpp" +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for a simplistic VERILOG format. + * + * Callbacks for the VERILOG format. + */ +class verilog_reader +{ +public: + /*! \brief Callback method for parsed module. + * + * \param module_name Name of the module + * \param inouts Container for input and output names + */ + virtual void on_module_header( const std::string& module_name, const std::vector& inouts ) const + { + (void)module_name; + (void)inouts; + } + + /*! \brief Callback method for parsed inputs. + * + * \param inputs Input names + * \param size Size modifier + */ + virtual void on_inputs( const std::vector& inputs, std::string const& size = "" ) const + { + (void)inputs; + (void)size; + } + + /*! \brief Callback method for parsed outputs. + * + * \param outputs Output names + * \param size Size modifier + */ + virtual void on_outputs( const std::vector& outputs, std::string const& size = "" ) const + { + (void)outputs; + (void)size; + } + + /*! \brief Callback method for parsed wires. + * + * \param wires Wire names + * \param size Size modifier + */ + virtual void on_wires( const std::vector& wires, std::string const& size = "" ) const + { + (void)wires; + (void)size; + } + + /*! \brief Callback method for parsed parameter definition of form ` parameter M = 10;`. + * + * \param name Name of the parameter + * \param value Value of the parameter + */ + virtual void on_parameter( const std::string& name, const std::string& value ) const + { + (void)name; + (void)value; + } + + /*! \brief Callback method for parsed immediate assignment of form `LHS = RHS ;`. + * + * \param lhs Left-hand side of assignment + * \param rhs Right-hand side of assignment + */ + virtual void on_assign( const std::string& lhs, const std::pair& rhs ) const + { + (void)lhs; + (void)rhs; + } + + /*! \brief Callback method for parsed module instantiation of form `NAME #(P1,P2) NAME(.SIGNAL(SIGANL), ..., .SIGNAL(SIGNAL));` + * + * \param module_name Name of the module + * \param params List of parameters + * \param inst_name Name of the instantiation + * \param args List (a_1,b_1), ..., (a_n,b_n) of name pairs, where + * a_i is a name of a signals in module_name and b_i is a name of a + * signal in inst_name. + */ + virtual void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, + std::vector> const& args ) const + { + (void)module_name; + (void)params; + (void)inst_name; + (void)args; + } + + /*! \brief Callback method for parsed AND-gate with 2 operands `LHS = OP1 & OP2 ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + */ + virtual void on_and( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const + { + (void)lhs; + (void)op1; + (void)op2; + } + + /*! \brief Callback method for parsed NAND-gate with 2 operands `LHS = ~(OP1 & OP2) ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + */ + virtual void on_nand( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const + { + (void)lhs; + (void)op1; + (void)op2; + } + + /*! \brief Callback method for parsed OR-gate with 2 operands `LHS = OP1 | OP2 ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + */ + virtual void on_or( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const + { + (void)lhs; + (void)op1; + (void)op2; + } + + /*! \brief Callback method for parsed NOR-gate with 2 operands `LHS = ~(OP1 | OP2) ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + */ + virtual void on_nor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const + { + (void)lhs; + (void)op1; + (void)op2; + } + + /*! \brief Callback method for parsed XOR-gate with 2 operands `LHS = OP1 ^ OP2 ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + */ + virtual void on_xor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const + { + (void)lhs; + (void)op1; + (void)op2; + } + + /*! \brief Callback method for parsed XOR-gate with 2 operands `LHS = ~(OP1 ^ OP2) ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + */ + virtual void on_xnor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const + { + (void)lhs; + (void)op1; + (void)op2; + } + + /*! \brief Callback method for parsed AND-gate with 3 operands `LHS = OP1 & OP2 & OP3 ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + * \param op3 operand3 of assignment + */ + virtual void on_and3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const + { + (void)lhs; + (void)op1; + (void)op2; + (void)op3; + } + + /*! \brief Callback method for parsed OR-gate with 3 operands `LHS = OP1 | OP2 | OP3 ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + * \param op3 operand3 of assignment + */ + virtual void on_or3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const + { + (void)lhs; + (void)op1; + (void)op2; + (void)op3; + } + + /*! \brief Callback method for parsed XOR-gate with 3 operands `LHS = OP1 ^ OP2 ^ OP3 ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + * \param op3 operand3 of assignment + */ + virtual void on_xor3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const + { + (void)lhs; + (void)op1; + (void)op2; + (void)op3; + } + + /*! \brief Callback method for parsed majority-of-3 gate `LHS = ( OP1 & OP2 ) | ( OP1 & OP3 ) | ( OP2 & OP3 ) ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + * \param op3 operand3 of assignment + */ + virtual void on_maj3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const + { + (void)lhs; + (void)op1; + (void)op2; + (void)op3; + } + + /*! \brief Callback method for parsed 2-to-1 multiplexer gate `LHS = OP1 ? OP2 : OP3 ;`. + * + * \param lhs Left-hand side of assignment + * \param op1 operand1 of assignment + * \param op2 operand2 of assignment + * \param op3 operand3 of assignment + */ + virtual void on_mux21( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const + { + (void)lhs; + (void)op1; + (void)op2; + (void)op3; + } + + /*! \brief Callback method for parsed comments `// comment string`. + * + * \param comment Comment string + */ + virtual void on_comment( std::string const& comment ) const + { + (void)comment; + } + + /*! \brief Callback method for parsed endmodule. + * + */ + virtual void on_endmodule() const {} +}; /* verilog_reader */ + +/*! \brief A VERILOG reader for prettyprinting a simplistic VERILOG format. + * + * Callbacks for prettyprinting of BLIF. + * + */ +class verilog_pretty_printer : public verilog_reader +{ +public: + /*! \brief Constructor of the VERILOG pretty printer. + * + * \param os Output stream + */ + verilog_pretty_printer( std::ostream& os = std::cout ) + : _os( os ) + { + } + + void on_module_header( const std::string& module_name, const std::vector& inouts ) const override + { + std::string params; + if ( inouts.size() > 0 ) + { + params = inouts[0]; + for ( auto i = 1u; i < inouts.size(); ++i ) + { + params += " , "; + params += inouts[i]; + } + } + _os << fmt::format( "module {}( {} ) ;\n", module_name, params ); + } + + void on_inputs( const std::vector& inputs, std::string const& size = "" ) const override + { + if ( inputs.size() == 0 ) + return; + _os << "input "; + if ( size != "" ) + _os << "[" << size << "] "; + + _os << inputs[0]; + for ( auto i = 1u; i < inputs.size(); ++i ) + { + _os << " , "; + _os << inputs[i]; + } + _os << " ;\n"; + } + + void on_outputs( const std::vector& outputs, std::string const& size = "" ) const override + { + if ( outputs.size() == 0 ) + return; + _os << "output "; + if ( size != "" ) + _os << "[" << size << "] "; + + _os << outputs[0]; + for ( auto i = 1u; i < outputs.size(); ++i ) + { + _os << " , "; + _os << outputs[i]; + } + _os << " ;\n"; + } + + void on_wires( const std::vector& wires, std::string const& size = "" ) const override + { + if ( wires.size() == 0 ) + return; + _os << "wire "; + if ( size != "" ) + _os << "[" << size << "] "; + + _os << wires[0]; + for ( auto i = 1u; i < wires.size(); ++i ) + { + _os << " , "; + _os << wires[i]; + } + _os << " ;\n"; + } + + void on_parameter( const std::string& name, const std::string& value ) const override + { + _os << "parameter " << name << " = " << value << ";\n"; + } + + void on_assign( const std::string& lhs, const std::pair& rhs ) const override + { + const std::string param = rhs.second ? fmt::format( "~{}", rhs.first ) : rhs.first; + _os << fmt::format( "assign {} = {} ;\n", lhs, param ); + } + + virtual void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, + std::vector> const& args ) const override + { + _os << module_name << " "; + if ( params.size() > 0u ) + { + _os << "#("; + for ( auto i = 0u; i < params.size(); ++i ) + { + _os << params.at( i ); + if ( i + 1 < params.size() ) + _os << ", "; + } + _os << ")"; + } + + _os << " " << inst_name << "("; + for ( auto i = 0u; i < args.size(); ++i ) + { + _os << args.at( i ).first << "(" << args.at( i ).second << ")"; + if ( i + 1 < args.size() ) + _os << ", "; + } + _os << ")"; + + _os << ";\n"; + } + + void on_and( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + _os << fmt::format( "assign {} = {} & {} ;\n", lhs, p1, p2 ); + } + + void on_nand( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + _os << fmt::format( "assign {} = ~({} & {}) ;\n", lhs, p1, p2 ); + } + + void on_or( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + _os << fmt::format( "assign {} = {} | {} ;\n", lhs, p1, p2 ); + } + + void on_nor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + _os << fmt::format( "assign {} = ~({} | {}) ;\n", lhs, p1, p2 ); + } + + void on_xor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + _os << fmt::format( "assign {} = {} ^ {} ;\n", lhs, p1, p2 ); + } + + void on_xnor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + _os << fmt::format( "assign {} = ~({} ^ {}) ;\n", lhs, p1, p2 ); + } + + void on_and3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; + _os << fmt::format( "assign {} = {} & {} & {} ;\n", lhs, p1, p2, p3 ); + } + + void on_or3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; + _os << fmt::format( "assign {} = {} | {} | {} ;\n", lhs, p1, p2, p3 ); + } + + void on_xor3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; + _os << fmt::format( "assign {} = {} ^ {} ^ {} ;\n", lhs, p1, p2, p3 ); + } + + void on_maj3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + const std::string p1 = op1.second ? fmt::format( "~{}", op1.first ) : op1.first; + const std::string p2 = op2.second ? fmt::format( "~{}", op2.first ) : op2.first; + const std::string p3 = op3.second ? fmt::format( "~{}", op3.first ) : op3.first; + _os << fmt::format( "assign {0} = ( {1} & {2} ) | ( {1} & {3} ) | ( {2} & {3} ) ;\n", lhs, p1, p2, p3 ); + } + + void on_endmodule() const override + { + _os << "endmodule\n" + << std::endl; + } + + void on_comment( const std::string& comment ) const override + { + _os << "// " << comment << std::endl; + } + + std::ostream& _os; /*!< Output stream */ +}; /* verilog_pretty_printer */ + +/*! \brief A writer for a simplistic VERILOG format. + * + * Callbacks for writing the VERILOG format. + * + */ +class verilog_writer +{ +public: + /*! \brief Constructs a VERILOG writer. + * + * \param os Output stream + */ + explicit verilog_writer( std::ostream& os ) + : _os( os ) + { + } + + /*! \brief Callback method for writing begin of a module declaration. + * + * \param name Module name + * \param xs List of module inputs + * \param ys List of module outputs + */ + virtual void on_module_begin( std::string const& name, std::vector const& xs, std::vector const& ys ) const + { + std::vector names = xs; + std::copy( ys.begin(), ys.end(), std::back_inserter( names ) ); + + _os << fmt::format( "module {}( {} );\n", name, fmt::join( names, " , " ) ); + } + + /*! \brief Callback method for writing single 1-bit input. + * + * \param name Input name + */ + virtual void on_input( std::string const& name ) const + { + _os << fmt::format( " input {} ;\n", name ); + } + + /*! \brief Callback method for writing input register. + * + * \param width Register size + * \param name Input name + */ + virtual void on_input( uint32_t width, std::string const& name ) const + { + _os << fmt::format( " input [{}:0] {} ;\n", width - 1, name ); + } + + /*! \brief Callback method for writing multiple single 1-bit input. + * + * \param names Input names + */ + virtual void on_input( std::vector const& names ) const + { + _os << fmt::format( " input {} ;\n", fmt::join( names, " , " ) ); + } + + /*! \brief Callback method for writing multiple input registers. + * + * \param width Register size + * \param names Input names + */ + virtual void on_input( uint32_t width, std::vector const& names ) const + { + _os << fmt::format( " input [{}:0] {} ;\n", width - 1, fmt::join( names, " , " ) ); + } + + /*! \brief Callback method for writing single 1-bit output. + * + * \param name Output name + */ + virtual void on_output( std::string const& name ) const + { + _os << fmt::format( " output {} ;\n", name ); + } + + /*! \brief Callback method for writing output register. + * + * \param width Register size + * \param name Output name + */ + virtual void on_output( uint32_t width, std::string const& name ) const + { + _os << fmt::format( " output [{}:0] {} ;\n", width - 1, name ); + } + + /*! \brief Callback method for writing multiple single 1-bit output. + * + * \param names Output names + */ + virtual void on_output( std::vector const& names ) const + { + _os << fmt::format( " output {} ;\n", fmt::join( names, " , " ) ); + } + + /*! \brief Callback method for writing multiple output registers. + * + * \param width Register size + * \param names Output names + */ + virtual void on_output( uint32_t width, std::vector const& names ) const + { + _os << fmt::format( " output [{}:0] {} ;\n", width - 1, fmt::join( names, " , " ) ); + } + + /*! \brief Callback method for writing single 1-bit wire. + * + * \param name Wire name + */ + virtual void on_wire( std::string const& name ) const + { + _os << fmt::format( " wire {} ;\n", name ); + } + + /*! \brief Callback method for writing wire register. + * + * \param width Register size + * \param name Wire name + */ + virtual void on_wire( uint32_t width, std::string const& name ) const + { + _os << fmt::format( " wire [{}:0] {} ;\n", width - 1, name ); + } + + /*! \brief Callback method for writing multiple single 1-bit wire. + * + * \param names Wire names + */ + virtual void on_wire( std::vector const& names ) const + { + _os << fmt::format( " wire {} ;\n", fmt::join( names, " , " ) ); + } + + /*! \brief Callback method for writing multiple wire registers. + * + * \param width Register size + * \param names Wire names + */ + virtual void on_wire( uint32_t width, std::vector const& names ) const + { + _os << fmt::format( " wire [{}:0] {} ;\n", width - 1, fmt::join( names, " , " ) ); + } + + /*! \brief Callback method for writing end of a module declaration. */ + virtual void on_module_end() const + { + _os << "endmodule" << std::endl; + } + + /*! \brief Callback method for writing a module instantiation. + * + * \param module_name Module name + * \param params List of parameters + * \param inst_name Instance name + * \param args List of arguments (first: I/O pin name, second: wire name) + */ + virtual void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, + std::vector> const& args ) const + { + _os << fmt::format( " {} ", module_name ); + if ( params.size() > 0u ) + { + _os << "#("; + for ( auto i = 0u; i < params.size(); ++i ) + { + _os << params.at( i ); + if ( i + 1 < params.size() ) + _os << ", "; + } + _os << ") "; + } + + _os << fmt::format( "{}( ", inst_name ); + for ( auto i = 0u; i < args.size(); ++i ) + { + _os << fmt::format( ".{} ({})", args.at( i ).first, args.at( i ).second ); + if ( i + 1 < args.size() ) + _os << ", "; + } + _os << " );\n"; + } + + /*! \brief Callback method for writing an assignment statement. + * + * \param out Output signal + * \param ins List of input signals + * \param op Operator + */ + virtual void on_assign( std::string const& out, std::vector> const& ins, std::string const& op ) const + { + std::string args; + + /* assemble arguments */ + for ( auto i = 0u; i < ins.size(); ++i ) + { + args.append( fmt::format( "{}{}", ins.at( i ).first ? "~" : "", ins.at( i ).second ) ); + if ( i != ins.size() - 1 ) + args.append( fmt::format( " {} ", op ) ); + } + + _os << fmt::format( " assign {} = {} ;\n", out, args ); + } + + /*! \brief Callback method for writing a maj3 assignment statement. + * + * \param out Output signal + * \param ins List of three input signals + */ + virtual void on_assign_maj3( std::string const& out, std::vector> const& ins ) const + { + assert( ins.size() == 3u ); + _os << fmt::format( " assign {0} = ( {1}{2} & {3}{4} ) | ( {1}{2} & {5}{6} ) | ( {3}{4} & {5}{6} ) ;\n", + out, + ins.at( 0 ).first ? "~" : "", ins.at( 0 ).second, + ins.at( 1 ).first ? "~" : "", ins.at( 1 ).second, + ins.at( 2 ).first ? "~" : "", ins.at( 2 ).second ); + } + + /*! \brief Callback method for writing a 2-to-1 multiplexer (ITE) assignment statement. + * + * \param out Output signal + * \param ins List of three input signals, in the order (if, then, else) + */ + virtual void on_assign_mux21( std::string const& out, std::vector> const& ins ) const + { + assert( ins.size() == 3u ); + _os << fmt::format( " assign {0} = {1}{2} ? {3}{4} : {5}{6} ;\n", + out, + ins.at( 0 ).first ? "~" : "", ins.at( 0 ).second, + ins.at( 1 ).first ? "~" : "", ins.at( 1 ).second, + ins.at( 2 ).first ? "~" : "", ins.at( 2 ).second ); + } + + /*! \brief Callback method for writing an assignment statement with unknown operator. + * + * \param out Output signal + */ + virtual void on_assign_unknown_gate( std::string const& out ) const + { + _os << fmt::format( " assign {} = unknown gate;\n", out ); + } + + /*! \brief Callback method for writing a maj3 assignment statement. + * + * \param out Output signal + * \param in An input signal + */ + virtual void on_assign_po( std::string const& out, std::pair const& in ) const + { + _os << fmt::format( " assign {} = {}{} ;\n", + out, + in.first ? "~" : "", in.second ); + } + +protected: + std::ostream& _os; /*!< Output stream */ +}; /* verilog_writer */ + +/*! \brief Simple parser for VERILOG format. + * + * Simplistic grammar-oriented parser for a structural VERILOG format. + * + */ +class verilog_parser +{ +public: + struct module_info + { + std::vector inputs; + std::vector outputs; + }; + +public: + /*! \brief Construct a VERILOG parser + * + * \param in Input stream + * \param reader A verilog reader + * \param diag A diagnostic engine + */ + verilog_parser( std::istream& in, + const verilog_reader& reader, + diagnostic_engine* diag = nullptr ) + : tok( in ) + , reader( reader ) + , diag( diag ) + , on_action( PackedFns( GateFn( [&]( const std::vector>& inputs, + const std::string output, + const std::string type ) + { + if ( type == "assign" ) + { + assert( inputs.size() == 1u ); + reader.on_assign( output, inputs[0] ); + } + else if ( type == "and2" ) + { + assert( inputs.size() == 2u ); + reader.on_and( output, inputs[0], inputs[1] ); + } + else if ( type == "nand2" ) + { + assert( inputs.size() == 2u ); + reader.on_nand( output, inputs[0], inputs[1] ); + } + else if ( type == "or2" ) + { + assert( inputs.size() == 2u ); + reader.on_or( output, inputs[0], inputs[1] ); + } + else if ( type == "nor2" ) + { + assert( inputs.size() == 2u ); + reader.on_nor( output, inputs[0], inputs[1] ); + } + else if ( type == "xor2" ) + { + assert( inputs.size() == 2u ); + reader.on_xor( output, inputs[0], inputs[1] ); + } + else if ( type == "xnor2" ) + { + assert( inputs.size() == 2u ); + reader.on_xnor( output, inputs[0], inputs[1] ); + } + else if ( type == "and3" ) + { + assert( inputs.size() == 3u ); + reader.on_and3( output, inputs[0], inputs[1], inputs[2] ); + } + else if ( type == "or3" ) + { + assert( inputs.size() == 3u ); + reader.on_or3( output, inputs[0], inputs[1], inputs[2] ); + } + else if ( type == "xor3" ) + { + assert( inputs.size() == 3u ); + reader.on_xor3( output, inputs[0], inputs[1], inputs[2] ); + } + else if ( type == "maj3" ) + { + assert( inputs.size() == 3u ); + reader.on_maj3( output, inputs[0], inputs[1], inputs[2] ); + } + else if ( type == "mux21" ) + { + assert( inputs.size() == 3u ); + reader.on_mux21( output, inputs[0], inputs[1], inputs[2] ); + } + else + { + assert( false && "unknown gate function" ); + std::cerr << "unknown gate function" << std::endl; + std::abort(); + } + } ), + ModuleInstFn( [&]( const std::string module_name, + const std::vector& params, + const std::string instance_name, + const std::vector>& pin_to_pin ) + { + reader.on_module_instantiation( module_name, params, instance_name, pin_to_pin ); + } ) + ) ) + { + on_action.declare_known( "0" ); + on_action.declare_known( "1" ); + on_action.declare_known( "1'b0" ); + on_action.declare_known( "1'b1" ); + } + + bool get_token( std::string& token ) + { + detail::tokenizer_return_code result; + do + { + if ( tokens.empty() ) + { + result = tok.get_token_internal( token ); + detail::trim( token ); + } + else + { + token = tokens.front(); + tokens.pop(); + return true; + } + + /* switch to comment mode */ + if ( token == "//" && result == detail::tokenizer_return_code::valid ) + { + tok.set_comment_mode(); + } + else if ( result == detail::tokenizer_return_code::comment ) + { + reader.on_comment( token ); + } + /* keep parsing if token is empty or if in the middle or at the end of a comment */ + } while ( ( token == "" && result == detail::tokenizer_return_code::valid ) || + tok.get_comment_mode() || + result == detail::tokenizer_return_code::comment ); + + return ( result == detail::tokenizer_return_code::valid ); + } + + void push_token( std::string const& token ) + { + tokens.push( token ); + } + + bool parse_signal_name() + { + valid = get_token( token ); // name + if ( !valid || token == "[" ) + return false; + auto const name = token; + + valid = get_token( token ); + if ( token == "[" ) + { + valid = get_token( token ); // size + if ( !valid ) + return false; + auto const size = token; + + valid = get_token( token ); // size + if ( !valid && token != "]" ) + return false; + token = name + "[" + size + "]"; + + return true; + } + + push_token( token ); + + token = name; + return true; + } + + bool parse_modules() + { + while ( get_token( token ) ) + { + if ( token != "module" ) + { + return false; + } + + if ( !parse_module() ) + { + return false; + } + } + return true; + } + + bool parse_module() + { + bool success = parse_module_header(); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_MODULE_HEADER ); + } + return false; + } + + do + { + valid = get_token( token ); + if ( !valid ) + return false; + + if ( token == "input" ) + { + success = parse_inputs(); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_INPUT_DECLARATION ); + } + return false; + } + } + else if ( token == "output" ) + { + success = parse_outputs(); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_OUTPUT_DECLARATION ); + } + return false; + } + } + else if ( token == "wire" ) + { + success = parse_wires(); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_WIRE_DECLARATION ); + } + return false; + } + } + else if ( token == "parameter" ) + { + success = parse_parameter(); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_WIRE_DECLARATION ); + } + return false; + } + } + else + { + break; + } + } while ( token != "assign" && token != "endmodule" ); + + while ( token != "endmodule" ) + { + if ( token == "assign" ) + { + success = parse_assign(); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_ASSIGNMENT ); + } + return false; + } + + valid = get_token( token ); + if ( !valid ) + return false; + } + else + { + success = parse_module_instantiation(); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_MODULE_INSTANTIATION_STATEMENT ); + } + return false; + } + + valid = get_token( token ); + if ( !valid ) + return false; + } + } + + /* check dangling objects */ + bool result = true; + const auto& deps = on_action.unresolved_dependencies(); + if ( deps.size() > 0 ) + result = false; + + for ( const auto& r : deps ) + { + if ( diag ) + { + diag->report( diag_id::WRN_UNRESOLVED_DEPENDENCY ) + .add_argument( r.first ) + .add_argument( r.second ); + } + } + + if ( !result ) + return false; + + if ( token == "endmodule" ) + { + /* callback */ + reader.on_endmodule(); + + return true; + } + else + { + return false; + } + } + + bool parse_module_header() + { + if ( token != "module" ) + return false; + + valid = get_token( token ); + if ( !valid ) + return false; + + module_name = token; + + valid = get_token( token ); + if ( !valid || token != "(" ) + return false; + + std::vector inouts; + do + { + if ( !parse_signal_name() ) + return false; + inouts.emplace_back( token ); + + valid = get_token( token ); // , or ) + if ( !valid || ( token != "," && token != ")" ) ) + return false; + } while ( valid && token != ")" ); + + valid = get_token( token ); + if ( !valid || token != ";" ) + return false; + + /* callback */ + reader.on_module_header( module_name, inouts ); + + return true; + } + + bool parse_inputs() + { + std::vector inputs; + if ( token != "input" ) + return false; + + std::string size = ""; + if ( !parse_signal_name() && token == "[" ) + { + do + { + valid = get_token( token ); + if ( !valid ) + return false; + + if ( token != "]" ) + size += token; + } while ( valid && token != "]" ); + + if ( !parse_signal_name() ) + return false; + } + inputs.emplace_back( token ); + + while ( true ) + { + valid = get_token( token ); + + if ( !valid || ( token != "," && token != ";" ) ) + return false; + + if ( token == ";" ) + break; + + if ( !parse_signal_name() ) + return false; + + inputs.emplace_back( token ); + } + + /* callback */ + reader.on_inputs( inputs, size ); + modules[module_name].inputs = inputs; + + for ( const auto& i : inputs ) + { + on_action.declare_known( i ); + } + + if ( std::smatch m; std::regex_match( size, m, verilog_regex::const_size_range ) ) + { + const auto a = std::stoul( m[1].str() ); + const auto b = std::stoul( m[2].str() ); + for ( auto j = std::min( a, b ); j <= std::max( a, b ); ++j ) + { + for ( const auto& i : inputs ) + { + on_action.declare_known( fmt::format( "{}[{}]", i, j ) ); + } + } + } + + return true; + } + + bool parse_outputs() + { + std::vector outputs; + if ( token != "output" ) + return false; + + std::string size = ""; + if ( !parse_signal_name() && token == "[" ) + { + do + { + valid = get_token( token ); + if ( !valid ) + return false; + + if ( token != "]" ) + size += token; + } while ( valid && token != "]" ); + + if ( !parse_signal_name() ) + return false; + } + outputs.emplace_back( token ); + + while ( true ) + { + valid = get_token( token ); + + if ( !valid || ( token != "," && token != ";" ) ) + return false; + + if ( token == ";" ) + break; + + if ( !parse_signal_name() ) + return false; + + outputs.emplace_back( token ); + } + + /* callback */ + reader.on_outputs( outputs, size ); + modules[module_name].outputs = outputs; + + return true; + } + + bool parse_wires() + { + std::vector wires; + if ( token != "wire" ) + return false; + + std::string size = ""; + if ( !parse_signal_name() && token == "[" ) + { + do + { + valid = get_token( token ); + if ( !valid ) + return false; + + if ( token != "]" ) + size += token; + } while ( valid && token != "]" ); + + if ( !parse_signal_name() ) + return false; + } + wires.emplace_back( token ); + + while ( true ) + { + valid = get_token( token ); + + if ( !valid || ( token != "," && token != ";" ) ) + return false; + + if ( token == ";" ) + break; + + if ( !parse_signal_name() ) + return false; + + wires.emplace_back( token ); + } + + /* callback */ + reader.on_wires( wires, size ); + + return true; + } + + bool parse_parameter() + { + if ( token != "parameter" ) + return false; + + valid = get_token( token ); + if ( !valid ) + return false; + auto const name = token; + + valid = get_token( token ); + if ( !valid || ( token != "=" ) ) + return false; + + valid = get_token( token ); + if ( !valid ) + return false; + auto const value = token; + + valid = get_token( token ); + if ( !valid || ( token != ";" ) ) + return false; + + /* callback */ + reader.on_parameter( name, value ); + + return true; + } + + bool parse_assign() + { + if ( token != "assign" ) + return false; + + if ( !parse_signal_name() ) + return false; + + const std::string lhs = token; + valid = get_token( token ); + if ( !valid || token != "=" ) + return false; + + /* expression */ + bool success = parse_rhs_expression( lhs ); + if ( !success ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_ASSIGNMENT_RHS ) + .add_argument( lhs ); + } + return false; + } + + if ( token != ";" ) + return false; + return true; + } + + bool parse_module_instantiation() + { + bool success = true; + std::string const module_name{token}; // name of module + + auto const it = modules.find( module_name ); + if ( it == std::end( modules ) ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_MODULE ) + .add_argument( module_name ); + } + return false; + } + + /* get module info */ + auto const& info = modules[module_name]; + + valid = get_token( token ); + if ( !valid ) + return false; + + std::vector params; + if ( token == "#" ) + { + valid = get_token( token ); // ( + if ( !valid || token != "(" ) + return false; + + do + { + valid = get_token( token ); // param + if ( !valid ) + return false; + params.emplace_back( token ); + + valid = get_token( token ); // , + if ( !valid ) + return false; + } while ( valid && token == "," ); + + if ( !valid || token != ")" ) + return false; + + valid = get_token( token ); + if ( !valid ) + return false; + } + + std::string const inst_name = token; // name of instantiation + + valid = get_token( token ); + if ( !valid || token != "(" ) + return false; + + std::vector> args; + do + { + valid = get_token( token ); + + if ( !valid ) + return false; // refers to signal + std::string const arg0{token}; + + /* check if a signal with this name exists in the module declaration */ + if ( ( std::find( std::begin( info.inputs ), std::end( info.inputs ), arg0.substr( 1, arg0.size() ) ) == std::end( info.inputs ) ) && + ( std::find( std::begin( info.outputs ), std::end( info.outputs ), arg0.substr( 1, arg0.size() ) ) == std::end( info.outputs ) ) ) + { + if ( diag ) + { + diag->report( diag_id::ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_PIN ) + .add_argument( arg0.substr( 1, arg0.size() ) ) + .add_argument( module_name ); + } + + success = false; + } + + valid = get_token( token ); + if ( !valid || token != "(" ) + return false; // ( + + valid = get_token( token ); + if ( !valid ) + return false; // signal name + auto const arg1 = token; + + valid = get_token( token ); + if ( !valid || token != ")" ) + return false; // ) + + valid = get_token( token ); + if ( !valid ) + return false; + + args.emplace_back( std::make_pair( arg0, arg1 ) ); + } while ( token == "," ); + + if ( !valid || token != ")" ) + return false; + + valid = get_token( token ); + if ( !valid || token != ";" ) + return false; + + std::vector inputs; + for ( const auto& input : modules[module_name].inputs ) + { + for ( const auto& a : args ) + { + if ( a.first.substr( 1, a.first.length() - 1 ) == input ) + { + inputs.emplace_back( a.second ); + } + } + } + + std::vector outputs; + for ( const auto& output : modules[module_name].outputs ) + { + for ( const auto& a : args ) + { + if ( a.first.substr( 1, a.first.length() - 1 ) == output ) + { + outputs.emplace_back( a.second ); + } + } + } + + /* callback */ + on_action.call_deferred( inputs, outputs, + std::make_tuple( module_name, params, inst_name, args ) ); + + return success; + } + + bool parse_rhs_expression( const std::string& lhs ) + { + std::string s; + do + { + valid = get_token( token ); + if ( !valid ) + return false; + + if ( token == ";" || token == "assign" || token == "endmodule" ) + break; + s.append( token ); + } while ( token != ";" && token != "assign" && token != "endmodule" ); + + std::smatch sm; + if ( std::regex_match( s, sm, verilog_regex::immediate_assign ) ) + { + assert( sm.size() == 3u ); + std::vector> args{{sm[2], sm[1] == "~"}}; + + on_action.call_deferred( /* dependencies */ { sm[2] }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "assign" ) + ); + } + else if ( std::regex_match( s, sm, verilog_regex::binary_expression ) ) + { + assert( sm.size() == 6u ); + std::pair arg0 = {sm[2], sm[1] == "~"}; + std::pair arg1 = {sm[5], sm[4] == "~"}; + std::vector> args{arg0, arg1}; + + auto op = sm[3]; + + if ( op == "&" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "and2" ) + ); + } + else if ( op == "|" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "or2" ) + ); + } + else if ( op == "^" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "xor2" ) + ); + } + else + { + return false; + } + } + else if ( std::regex_match( s, sm, verilog_regex::negated_binary_expression ) ) + { + assert( sm.size() == 6u ); + std::pair arg0 = {sm[2], sm[1] == "~"}; + std::pair arg1 = {sm[5], sm[4] == "~"}; + std::vector> args{arg0, arg1}; + + auto op = sm[3]; + if ( op == "&" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "nand2" ) + ); + } + else if ( op == "|" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "nor2" ) + ); + } + else if ( op == "^" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "xnor2" ) + ); + } + else + { + return false; + } + } + else if ( std::regex_match( s, sm, verilog_regex::ternary_expression ) ) + { + assert( sm.size() == 9u ); + std::pair arg0 = {sm[2], sm[1] == "~"}; + std::pair arg1 = {sm[5], sm[4] == "~"}; + std::pair arg2 = {sm[8], sm[7] == "~"}; + std::vector> args{arg0, arg1, arg2}; + + auto op = sm[3]; + if ( sm[6] != op ) + { + if ( sm[3] == "?" && sm[6] == ":" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first, arg2.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "mux21" ) + ); + return true; + } + return false; + } + + if ( op == "&" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first, arg2.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "and3" ) + ); + } + else if ( op == "|" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first, arg2.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "or3" ) + ); + } + else if ( op == "^" ) + { + on_action.call_deferred( /* dependencies */ { arg0.first, arg1.first, arg2.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "xor3" ) + ); + } + else + { + return false; + } + } + else if ( std::regex_match( s, sm, verilog_regex::maj3_expression ) ) + { + assert( sm.size() == 13u ); + std::pair a0 = {sm[2], sm[1] == "~"}; + std::pair b0 = {sm[4], sm[3] == "~"}; + std::pair a1 = {sm[6], sm[5] == "~"}; + std::pair c0 = {sm[8], sm[7] == "~"}; + std::pair b1 = {sm[10], sm[9] == "~"}; + std::pair c1 = {sm[12], sm[11] == "~"}; + + if ( a0 != a1 || b0 != b1 || c0 != c1 ) + return false; + + std::vector> args; + args.push_back( a0 ); + args.push_back( b0 ); + args.push_back( c0 ); + + on_action.call_deferred( /* dependencies */ { a0.first, b0.first, c0.first }, { lhs }, + /* gate-function params */ std::make_tuple( args, lhs, "maj3" ) + ); + } + else + { + return false; + } + + return true; + } + +private: + /* Function signatures */ + using GateFn = detail::Func< + std::vector>, + std::string, + std::string + >; + using ModuleInstFn = detail::Func< + std::string, + std::vector, + std::string, + std::vector> + >; + + /* Parameter maps */ + using GateParamMap = detail::ParamPackMap< + /* Key */ + std::string, + /* Params */ + std::vector>, + std::string, + std::string + >; + using ModuleInstParamMap = detail::ParamPackMap< + /* Key */ + std::string, + /* Param */ + std::string, + std::vector, + std::string, + std::vector> + >; + + constexpr static const int GATE_FN{0}; + constexpr static const int MODULE_INST_FN{1}; + + using ParamMaps = detail::ParamPackMapN; + using PackedFns = detail::FuncPackN; + +private: + detail::tokenizer tok; + const verilog_reader& reader; + diagnostic_engine* diag; + + std::string token; + std::queue tokens; /* lookahead */ + std::string module_name; + + bool valid = false; + + detail::call_in_topological_order on_action; + std::unordered_map modules; +}; /* verilog_parser */ + +/*! \brief Reader function for VERILOG format. + * + * Reads a simplistic VERILOG format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader A VERILOG reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_verilog( std::istream& in, const verilog_reader& reader, diagnostic_engine* diag = nullptr ) +{ + verilog_parser parser( in, reader, diag ); + auto result = parser.parse_modules(); + if ( !result ) + { + return return_code::parse_error; + } + else + { + return return_code::success; + } +} + +/*! \brief Reader function for VERILOG format. + * + * Reads a simplistic VERILOG format from a file and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader A VERILOG reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_verilog( const std::string& filename, const verilog_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_verilog( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/verilog_regex.hpp b/third-party/mockturtle/lib/lorina/lorina/verilog_regex.hpp new file mode 100644 index 00000000000..19347130db3 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/verilog_regex.hpp @@ -0,0 +1,51 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file verilog_regex.hpp + \brief Regular expressions used by the Verilog parser. + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +namespace lorina +{ + +namespace verilog_regex +{ +static std::regex immediate_assign( R"(^(~)?\(?([[:alnum:]\[\]_']+)\)?$)" ); +static std::regex binary_expression( R"(^(~)?([[:alnum:]\[\]_']+)([&|^])(~)?([[:alnum:]\[\]_']+)$)" ); +static std::regex ternary_expression( R"(^(~)?([[:alnum:]\[\]_']+)([&|^?])(~)?([[:alnum:]\[\]_']+)([&|^:])(~)?([[:alnum:]\[\]_']+)$)" ); +static std::regex maj3_expression( R"(^\((~)?([[:alnum:]\[\]_']+)&(~)?([[:alnum:]\[\]_']+)\)\|\((~)?([[:alnum:]\[\]_']+)&(~)?([[:alnum:]\[\]_']+)\)\|\((~)?([[:alnum:]\[\]_']+)&(~)?([[:alnum:]\[\]_']+)\)$)" ); +static std::regex negated_binary_expression( R"(^~\((~)?([[:alnum:]\[\]_']+)([&|^])(~)?([[:alnum:]\[\]_']+)\)$)" ); +static std::regex const_size_range( R"(^(\d+):(\d+)$)" ); +} // namespace verilog_regex + +} // namespace lorina \ No newline at end of file diff --git a/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/btree.h b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/btree.h new file mode 100644 index 00000000000..7c73162d1c5 --- /dev/null +++ b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/btree.h @@ -0,0 +1,4050 @@ +// --------------------------------------------------------------------------- +// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp) +// with modifications. +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// --------------------------------------------------------------------------- + +#ifndef PHMAP_BTREE_BTREE_CONTAINER_H_ +#define PHMAP_BTREE_BTREE_CONTAINER_H_ + +#ifdef _MSC_VER + #pragma warning(push) + + #pragma warning(disable : 4127) // conditional expression is constant + #pragma warning(disable : 4324) // structure was padded due to alignment specifier + #pragma warning(disable : 4355) // 'this': used in base member initializer list + #pragma warning(disable : 4365) // conversion from 'int' to 'const unsigned __int64', signed/unsigned mismatch + #pragma warning(disable : 4514) // unreferenced inline function has been removed + #pragma warning(disable : 4623) // default constructor was implicitly defined as deleted + #pragma warning(disable : 4625) // copy constructor was implicitly defined as deleted + #pragma warning(disable : 4626) // assignment operator was implicitly defined as deleted + #pragma warning(disable : 4710) // function not inlined + #pragma warning(disable : 4711) // selected for automatic inline expansion + #pragma warning(disable : 4820) // '6' bytes padding added after data member + #pragma warning(disable : 4868) // compiler may not enforce left-to-right evaluation order in braced initializer list + #pragma warning(disable : 5026) // move constructor was implicitly defined as deleted + #pragma warning(disable : 5027) // move assignment operator was implicitly defined as deleted + #pragma warning(disable : 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +#endif + + +#include +#include +#include +#include +#include + +#include "phmap_fwd_decl.h" +#include "phmap_base.h" + +#if PHMAP_HAVE_STD_STRING_VIEW + #include +#endif + +// MSVC constructibility traits do not detect destructor properties and so our +// implementations should not use them as a source-of-truth. +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) + #define PHMAP_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 +#endif + +namespace phmap { + + // Defined and documented later on in this file. + template + struct is_trivially_destructible; + + // Defined and documented later on in this file. + template + struct is_trivially_move_assignable; + + namespace type_traits_internal { + + // Silence MSVC warnings about the destructor being defined as deleted. +#if defined(_MSC_VER) && !defined(__GNUC__) + #pragma warning(push) + #pragma warning(disable : 4624) +#endif // defined(_MSC_VER) && !defined(__GNUC__) + + template + union SingleMemberUnion { + T t; + }; + + // Restore the state of the destructor warning that was silenced above. +#if defined(_MSC_VER) && !defined(__GNUC__) + #pragma warning(pop) +#endif // defined(_MSC_VER) && !defined(__GNUC__) + + template + struct IsTriviallyMoveConstructibleObject + : std::integral_constant< + bool, std::is_move_constructible< + type_traits_internal::SingleMemberUnion>::value && + phmap::is_trivially_destructible::value> {}; + + template + struct IsTriviallyCopyConstructibleObject + : std::integral_constant< + bool, std::is_copy_constructible< + type_traits_internal::SingleMemberUnion>::value && + phmap::is_trivially_destructible::value> {}; + + template + struct IsTriviallyMoveAssignableReference : std::false_type {}; + + template + struct IsTriviallyMoveAssignableReference + : phmap::is_trivially_move_assignable::type {}; + + template + struct IsTriviallyMoveAssignableReference + : phmap::is_trivially_move_assignable::type {}; + + } // namespace type_traits_internal + + + template + using void_t = typename type_traits_internal::VoidTImpl::type; + + + template + struct is_function + : std::integral_constant< + bool, !(std::is_reference::value || + std::is_const::type>::value)> {}; + + + namespace type_traits_internal { + + template + class is_trivially_copyable_impl { + using ExtentsRemoved = typename std::remove_all_extents::type; + static constexpr bool kIsCopyOrMoveConstructible = + std::is_copy_constructible::value || + std::is_move_constructible::value; + static constexpr bool kIsCopyOrMoveAssignable = + phmap::is_copy_assignable::value || + phmap::is_move_assignable::value; + + public: + static constexpr bool kValue = + (__has_trivial_copy(ExtentsRemoved) || !kIsCopyOrMoveConstructible) && + (__has_trivial_assign(ExtentsRemoved) || !kIsCopyOrMoveAssignable) && + (kIsCopyOrMoveConstructible || kIsCopyOrMoveAssignable) && + is_trivially_destructible::value && + // We need to check for this explicitly because otherwise we'll say + // references are trivial copyable when compiled by MSVC. + !std::is_reference::value; + }; + + template + struct is_trivially_copyable + : std::integral_constant< + bool, type_traits_internal::is_trivially_copyable_impl::kValue> {}; + } // namespace type_traits_internal + + namespace swap_internal { + + // Necessary for the traits. + using std::swap; + + // This declaration prevents global `swap` and `phmap::swap` overloads from being + // considered unless ADL picks them up. + void swap(); + + template + using IsSwappableImpl = decltype(swap(std::declval(), std::declval())); + + // NOTE: This dance with the default template parameter is for MSVC. + template (), std::declval()))>> + using IsNothrowSwappableImpl = typename std::enable_if::type; + + template + struct IsSwappable + : phmap::type_traits_internal::is_detected {}; + + template + struct IsNothrowSwappable + : phmap::type_traits_internal::is_detected {}; + + template ::value, int> = 0> + void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable::value) { + swap(lhs, rhs); + } + + using StdSwapIsUnconstrained = IsSwappable; + + } // namespace swap_internal + + namespace type_traits_internal { + + // Make the swap-related traits/function accessible from this namespace. + using swap_internal::IsNothrowSwappable; + using swap_internal::IsSwappable; + using swap_internal::Swap; + using swap_internal::StdSwapIsUnconstrained; + + } // namespace type_traits_internal + + namespace compare_internal { + + using value_type = int8_t; + + template + struct Fail { + static_assert(sizeof(T) < 0, "Only literal `0` is allowed."); + }; + + template + struct OnlyLiteralZero { + constexpr OnlyLiteralZero(NullPtrT) noexcept {} // NOLINT + + template < + typename T, + typename = typename std::enable_if< + std::is_same::value || + (std::is_integral::value && !std::is_same::value)>::type, + typename = typename Fail::type> + OnlyLiteralZero(T); // NOLINT + }; + + enum class eq : value_type { + equal = 0, + equivalent = equal, + nonequal = 1, + nonequivalent = nonequal, + }; + + enum class ord : value_type { less = -1, greater = 1 }; + + enum class ncmp : value_type { unordered = -127 }; + +#ifdef __cpp_inline_variables + +#define PHMAP_COMPARE_INLINE_BASECLASS_DECL(name) + +#define PHMAP_COMPARE_INLINE_SUBCLASS_DECL(type, name) \ + static const type name; + +#define PHMAP_COMPARE_INLINE_INIT(type, name, init) \ + inline constexpr type type::name(init) + +#else // __cpp_inline_variables + +#define PHMAP_COMPARE_INLINE_BASECLASS_DECL(name) \ + static const T name; + +#define PHMAP_COMPARE_INLINE_SUBCLASS_DECL(type, name) + +#define PHMAP_COMPARE_INLINE_INIT(type, name, init) \ + template \ + const T compare_internal::type##_base::name(init) + +#endif // __cpp_inline_variables + + // These template base classes allow for defining the values of the constants + // in the header file (for performance) without using inline variables (which + // aren't available in C++11). + template + struct weak_equality_base { + PHMAP_COMPARE_INLINE_BASECLASS_DECL(equivalent) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(nonequivalent) + }; + + template + struct strong_equality_base { + PHMAP_COMPARE_INLINE_BASECLASS_DECL(equal) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(nonequal) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(equivalent) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(nonequivalent) + }; + + template + struct partial_ordering_base { + PHMAP_COMPARE_INLINE_BASECLASS_DECL(less) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(equivalent) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(greater) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(unordered) + }; + + template + struct weak_ordering_base { + PHMAP_COMPARE_INLINE_BASECLASS_DECL(less) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(equivalent) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(greater) + }; + + template + struct strong_ordering_base { + PHMAP_COMPARE_INLINE_BASECLASS_DECL(less) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(equal) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(equivalent) + PHMAP_COMPARE_INLINE_BASECLASS_DECL(greater) + }; + + } // namespace compare_internal + + class weak_equality + : public compare_internal::weak_equality_base { + explicit constexpr weak_equality(compare_internal::eq v) noexcept + : value_(static_cast(v)) {} + friend struct compare_internal::weak_equality_base; + + public: + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, equivalent) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, nonequivalent) + + // Comparisons + friend constexpr bool operator==( + weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + weak_equality v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + weak_equality v) noexcept { + return 0 != v.value_; + } + + private: + compare_internal::value_type value_; + }; + PHMAP_COMPARE_INLINE_INIT(weak_equality, equivalent, + compare_internal::eq::equivalent); + PHMAP_COMPARE_INLINE_INIT(weak_equality, nonequivalent, + compare_internal::eq::nonequivalent); + + class strong_equality + : public compare_internal::strong_equality_base { + explicit constexpr strong_equality(compare_internal::eq v) noexcept + : value_(static_cast(v)) {} + friend struct compare_internal::strong_equality_base; + + public: + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equal) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequal) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equivalent) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequivalent) + + // Conversion + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + // Comparisons + friend constexpr bool operator==( + strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + strong_equality v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + strong_equality v) noexcept { + return 0 != v.value_; + } + + private: + compare_internal::value_type value_; + }; + + PHMAP_COMPARE_INLINE_INIT(strong_equality, equal, compare_internal::eq::equal); + PHMAP_COMPARE_INLINE_INIT(strong_equality, nonequal, + compare_internal::eq::nonequal); + PHMAP_COMPARE_INLINE_INIT(strong_equality, equivalent, + compare_internal::eq::equivalent); + PHMAP_COMPARE_INLINE_INIT(strong_equality, nonequivalent, + compare_internal::eq::nonequivalent); + + class partial_ordering + : public compare_internal::partial_ordering_base { + explicit constexpr partial_ordering(compare_internal::eq v) noexcept + : value_(static_cast(v)) {} + explicit constexpr partial_ordering(compare_internal::ord v) noexcept + : value_(static_cast(v)) {} + explicit constexpr partial_ordering(compare_internal::ncmp v) noexcept + : value_(static_cast(v)) {} + friend struct compare_internal::partial_ordering_base; + + constexpr bool is_ordered() const noexcept { + return value_ != + compare_internal::value_type(compare_internal::ncmp::unordered); + } + + public: + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, less) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, equivalent) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered) + + // Conversion + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + // Comparisons + friend constexpr bool operator==( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ == 0; + } + friend constexpr bool operator!=( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return !v.is_ordered() || v.value_ != 0; + } + friend constexpr bool operator<( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ < 0; + } + friend constexpr bool operator<=( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ <= 0; + } + friend constexpr bool operator>( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ > 0; + } + friend constexpr bool operator>=( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ >= 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return !v.is_ordered() || 0 != v.value_; + } + friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 < v.value_; + } + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 <= v.value_; + } + friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 > v.value_; + } + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 >= v.value_; + } + + private: + compare_internal::value_type value_; + }; + + PHMAP_COMPARE_INLINE_INIT(partial_ordering, less, compare_internal::ord::less); + PHMAP_COMPARE_INLINE_INIT(partial_ordering, equivalent, + compare_internal::eq::equivalent); + PHMAP_COMPARE_INLINE_INIT(partial_ordering, greater, + compare_internal::ord::greater); + PHMAP_COMPARE_INLINE_INIT(partial_ordering, unordered, + compare_internal::ncmp::unordered); + + class weak_ordering + : public compare_internal::weak_ordering_base { + explicit constexpr weak_ordering(compare_internal::eq v) noexcept + : value_(static_cast(v)) {} + explicit constexpr weak_ordering(compare_internal::ord v) noexcept + : value_(static_cast(v)) {} + friend struct compare_internal::weak_ordering_base; + + public: + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, less) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, equivalent) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater) + + // Conversions + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + constexpr operator partial_ordering() const noexcept { // NOLINT + return value_ == 0 ? partial_ordering::equivalent + : (value_ < 0 ? partial_ordering::less + : partial_ordering::greater); + } + // Comparisons + friend constexpr bool operator==( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator<( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ < 0; + } + friend constexpr bool operator<=( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ <= 0; + } + friend constexpr bool operator>( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ > 0; + } + friend constexpr bool operator>=( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ >= 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 != v.value_; + } + friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 < v.value_; + } + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 <= v.value_; + } + friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 > v.value_; + } + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 >= v.value_; + } + + private: + compare_internal::value_type value_; + }; + + PHMAP_COMPARE_INLINE_INIT(weak_ordering, less, compare_internal::ord::less); + PHMAP_COMPARE_INLINE_INIT(weak_ordering, equivalent, + compare_internal::eq::equivalent); + PHMAP_COMPARE_INLINE_INIT(weak_ordering, greater, + compare_internal::ord::greater); + + class strong_ordering + : public compare_internal::strong_ordering_base { + explicit constexpr strong_ordering(compare_internal::eq v) noexcept + : value_(static_cast(v)) {} + explicit constexpr strong_ordering(compare_internal::ord v) noexcept + : value_(static_cast(v)) {} + friend struct compare_internal::strong_ordering_base; + + public: + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, less) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equal) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equivalent) + PHMAP_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater) + + // Conversions + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + constexpr operator strong_equality() const noexcept { // NOLINT + return value_ == 0 ? strong_equality::equal : strong_equality::nonequal; + } + constexpr operator partial_ordering() const noexcept { // NOLINT + return value_ == 0 ? partial_ordering::equivalent + : (value_ < 0 ? partial_ordering::less + : partial_ordering::greater); + } + constexpr operator weak_ordering() const noexcept { // NOLINT + return value_ == 0 + ? weak_ordering::equivalent + : (value_ < 0 ? weak_ordering::less : weak_ordering::greater); + } + // Comparisons + friend constexpr bool operator==( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator<( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ < 0; + } + friend constexpr bool operator<=( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ <= 0; + } + friend constexpr bool operator>( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ > 0; + } + friend constexpr bool operator>=( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ >= 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 != v.value_; + } + friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 < v.value_; + } + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 <= v.value_; + } + friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 > v.value_; + } + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 >= v.value_; + } + + private: + compare_internal::value_type value_; + }; + PHMAP_COMPARE_INLINE_INIT(strong_ordering, less, compare_internal::ord::less); + PHMAP_COMPARE_INLINE_INIT(strong_ordering, equal, compare_internal::eq::equal); + PHMAP_COMPARE_INLINE_INIT(strong_ordering, equivalent, + compare_internal::eq::equivalent); + PHMAP_COMPARE_INLINE_INIT(strong_ordering, greater, + compare_internal::ord::greater); + +#undef PHMAP_COMPARE_INLINE_BASECLASS_DECL +#undef PHMAP_COMPARE_INLINE_SUBCLASS_DECL +#undef PHMAP_COMPARE_INLINE_INIT + + namespace compare_internal { + // We also provide these comparator adapter functions for internal phmap use. + + // Helper functions to do a boolean comparison of two keys given a boolean + // or three-way comparator. + // SFINAE prevents implicit conversions to bool (such as from int). + template ::value, int> = 0> + constexpr bool compare_result_as_less_than(const BoolType r) { return r; } + constexpr bool compare_result_as_less_than(const phmap::weak_ordering r) { + return r < 0; + } + + template + constexpr bool do_less_than_comparison(const Compare &compare, const K &x, + const LK &y) { + return compare_result_as_less_than(compare(x, y)); + } + + // Helper functions to do a three-way comparison of two keys given a boolean or + // three-way comparator. + // SFINAE prevents implicit conversions to int (such as from bool). + template ::value, int> = 0> + constexpr phmap::weak_ordering compare_result_as_ordering(const Int c) { + return c < 0 ? phmap::weak_ordering::less + : c == 0 ? phmap::weak_ordering::equivalent + : phmap::weak_ordering::greater; + } + constexpr phmap::weak_ordering compare_result_as_ordering( + const phmap::weak_ordering c) { + return c; + } + + template < + typename Compare, typename K, typename LK, + phmap::enable_if_t>::value, + int> = 0> + constexpr phmap::weak_ordering do_three_way_comparison(const Compare &compare, + const K &x, const LK &y) { + return compare_result_as_ordering(compare(x, y)); + } + template < + typename Compare, typename K, typename LK, + phmap::enable_if_t>::value, + int> = 0> + constexpr phmap::weak_ordering do_three_way_comparison(const Compare &compare, + const K &x, const LK &y) { + return compare(x, y) ? phmap::weak_ordering::less + : compare(y, x) ? phmap::weak_ordering::greater + : phmap::weak_ordering::equivalent; + } + + } // namespace compare_internal +} + + +namespace phmap { + +namespace priv { + + // A helper class that indicates if the Compare parameter is a key-compare-to + // comparator. + template + using btree_is_key_compare_to = + std::is_convertible, + phmap::weak_ordering>; + + struct StringBtreeDefaultLess { + using is_transparent = void; + + StringBtreeDefaultLess() = default; + + // Compatibility constructor. + StringBtreeDefaultLess(std::less) {} // NOLINT +#if PHMAP_HAVE_STD_STRING_VIEW + StringBtreeDefaultLess(std::less) {} // NOLINT + StringBtreeDefaultLess(phmap::Less) {} // NOLINT + + phmap::weak_ordering operator()(std::string_view lhs, + std::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.compare(rhs)); + } +#else + phmap::weak_ordering operator()(std::string lhs, + std::string rhs) const { + return compare_internal::compare_result_as_ordering(lhs.compare(rhs)); + } +#endif + }; + + struct StringBtreeDefaultGreater { + using is_transparent = void; + + StringBtreeDefaultGreater() = default; + + StringBtreeDefaultGreater(std::greater) {} // NOLINT +#if PHMAP_HAVE_STD_STRING_VIEW + StringBtreeDefaultGreater(std::greater) {} // NOLINT + + phmap::weak_ordering operator()(std::string_view lhs, + std::string_view rhs) const { + return compare_internal::compare_result_as_ordering(rhs.compare(lhs)); + } +#else + phmap::weak_ordering operator()(std::string lhs, + std::string rhs) const { + return compare_internal::compare_result_as_ordering(rhs.compare(lhs)); + } +#endif + }; + + // A helper class to convert a boolean comparison into a three-way "compare-to" + // comparison that returns a negative value to indicate less-than, zero to + // indicate equality and a positive value to indicate greater-than. This helper + // class is specialized for less, greater, + // less, and greater. + // + // key_compare_to_adapter is provided so that btree users + // automatically get the more efficient compare-to code when using common + // google string types with common comparison functors. + // These string-like specializations also turn on heterogeneous lookup by + // default. + template + struct key_compare_to_adapter { + using type = Compare; + }; + + template <> + struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; + }; + + template <> + struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; + }; + + template <> + struct key_compare_to_adapter> { + using type = StringBtreeDefaultGreater; + }; + +#if PHMAP_HAVE_STD_STRING_VIEW + template <> + struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; + }; + + template <> + struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; + }; + + template <> + struct key_compare_to_adapter> { + using type = StringBtreeDefaultGreater; + }; +#endif + + template + struct common_params { + // If Compare is a common comparator for a std::string-like type, then we adapt it + // to use heterogeneous lookup and to be a key-compare-to comparator. + using key_compare = typename key_compare_to_adapter::type; + // A type which indicates if we have a key-compare-to functor or a plain old + // key-compare functor. + using is_key_compare_to = btree_is_key_compare_to; + + using allocator_type = Alloc; + using key_type = Key; + using size_type = std::make_signed::type; + using difference_type = ptrdiff_t; + + // True if this is a multiset or multimap. + using is_multi_container = std::integral_constant; + + using slot_policy = SlotPolicy; + using slot_type = typename slot_policy::slot_type; + using value_type = typename slot_policy::value_type; + using init_type = typename slot_policy::mutable_value_type; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + + enum { + kTargetNodeSize = TargetNodeSize, + + // Upper bound for the available space for values. This is largest for leaf + // nodes, which have overhead of at least a pointer + 4 bytes (for storing + // 3 field_types and an enum). + kNodeValueSpace = + TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4), + }; + + // This is an integral type large enough to hold as many + // ValueSize-values as will fit a node of TargetNodeSize bytes. + using node_count_type = + phmap::conditional_t<(kNodeValueSpace / sizeof(value_type) > + (std::numeric_limits::max)()), + uint16_t, uint8_t>; // NOLINT + + // The following methods are necessary for passing this struct as PolicyTraits + // for node_handle and/or are used within btree. + static value_type &element(slot_type *slot) { + return slot_policy::element(slot); + } + static const value_type &element(const slot_type *slot) { + return slot_policy::element(slot); + } + template + static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { + slot_policy::construct(alloc, slot, std::forward(args)...); + } + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + slot_policy::construct(alloc, slot, other); + } + static void destroy(Alloc *alloc, slot_type *slot) { + slot_policy::destroy(alloc, slot); + } + static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { + construct(alloc, new_slot, old_slot); + destroy(alloc, old_slot); + } + static void swap(Alloc *alloc, slot_type *a, slot_type *b) { + slot_policy::swap(alloc, a, b); + } + static void move(Alloc *alloc, slot_type *src, slot_type *dest) { + slot_policy::move(alloc, src, dest); + } + static void move(Alloc *alloc, slot_type *first, slot_type *last, + slot_type *result) { + slot_policy::move(alloc, first, last, result); + } + }; + + // A parameters structure for holding the type parameters for a btree_map. + // Compare and Alloc should be nothrow copy-constructible. + template + struct map_params : common_params> { + using super_type = typename map_params::common_params; + using mapped_type = Data; + // This type allows us to move keys when it is safe to do so. It is safe + // for maps in which value_type and mutable_value_type are layout compatible. + using slot_policy = typename super_type::slot_policy; + using slot_type = typename super_type::slot_type; + using value_type = typename super_type::value_type; + using init_type = typename super_type::init_type; + + using key_compare = typename super_type::key_compare; + // Inherit from key_compare for empty base class optimization. + struct value_compare : private key_compare { + value_compare() = default; + explicit value_compare(const key_compare &cmp) : key_compare(cmp) {} + + template + auto operator()(const T &left, const U &right) const + -> decltype(std::declval()(left.first, right.first)) { + return key_compare::operator()(left.first, right.first); + } + }; + using is_map_container = std::true_type; + + static const Key &key(const value_type &x) { return x.first; } + static const Key &key(const init_type &x) { return x.first; } + static const Key &key(const slot_type *x) { return slot_policy::key(x); } + static mapped_type &value(value_type *value) { return value->second; } + }; + + // This type implements the necessary functions from the + // btree::priv::slot_type interface. + template + struct set_slot_policy { + using slot_type = Key; + using value_type = Key; + using mutable_value_type = Key; + + static value_type &element(slot_type *slot) { return *slot; } + static const value_type &element(const slot_type *slot) { return *slot; } + + template + static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { + phmap::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + phmap::allocator_traits::construct(*alloc, slot, std::move(*other)); + } + + template + static void destroy(Alloc *alloc, slot_type *slot) { + phmap::allocator_traits::destroy(*alloc, slot); + } + + template + static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { + using std::swap; + swap(*a, *b); + } + + template + static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { + *dest = std::move(*src); + } + + template + static void move(Alloc *alloc, slot_type *first, slot_type *last, + slot_type *result) { + for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) + move(alloc, src, dest); + } + }; + + // A parameters structure for holding the type parameters for a btree_set. + // Compare and Alloc should be nothrow copy-constructible. + template + struct set_params : common_params> { + using value_type = Key; + using slot_type = typename set_params::common_params::slot_type; + using value_compare = typename set_params::common_params::key_compare; + using is_map_container = std::false_type; + + static const Key &key(const value_type &x) { return x; } + static const Key &key(const slot_type *x) { return *x; } + }; + + // An adapter class that converts a lower-bound compare into an upper-bound + // compare. Note: there is no need to make a version of this adapter specialized + // for key-compare-to functors because the upper-bound (the first value greater + // than the input) is never an exact match. + template + struct upper_bound_adapter { + explicit upper_bound_adapter(const Compare &c) : comp(c) {} + template + bool operator()(const K &a, const LK &b) const { + // Returns true when a is not greater than b. + return !phmap::compare_internal::compare_result_as_less_than(comp(b, a)); + } + + private: + Compare comp; + }; + + enum class MatchKind : uint8_t { kEq, kNe }; + + template + struct SearchResult { + V value; + MatchKind match; + + static constexpr bool HasMatch() { return true; } + bool IsEq() const { return match == MatchKind::kEq; } + }; + + // When we don't use CompareTo, `match` is not present. + // This ensures that callers can't use it accidentally when it provides no + // useful information. + template + struct SearchResult { + V value; + + static constexpr bool HasMatch() { return false; } + static constexpr bool IsEq() { return false; } + }; + + // A node in the btree holding. The same node type is used for both internal + // and leaf nodes in the btree, though the nodes are allocated in such a way + // that the children array is only valid in internal nodes. + template + class btree_node { + using is_key_compare_to = typename Params::is_key_compare_to; + using is_multi_container = typename Params::is_multi_container; + using field_type = typename Params::node_count_type; + using allocator_type = typename Params::allocator_type; + using slot_type = typename Params::slot_type; + + public: + using params_type = Params; + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using key_compare = typename Params::key_compare; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + + // Btree decides whether to use linear node search as follows: + // - If the key is arithmetic and the comparator is std::less or + // std::greater, choose linear. + // - Otherwise, choose binary. + // TODO(ezb): Might make sense to add condition(s) based on node-size. + using use_linear_search = std::integral_constant< + bool, + std::is_arithmetic::value && + (std::is_same, key_compare>::value || + std::is_same, key_compare>::value || + std::is_same, key_compare>::value)>; + + + ~btree_node() = default; + btree_node(btree_node const &) = delete; + btree_node &operator=(btree_node const &) = delete; + + // Public for EmptyNodeType. + constexpr static size_type Alignment() { + static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(), + "Alignment of all nodes must be equal."); + return (size_type)InternalLayout().Alignment(); + } + + protected: + btree_node() = default; + + private: + using layout_type = phmap::priv::Layout; + constexpr static size_type SizeWithNValues(size_type n) { + return (size_type)layout_type(/*parent*/ 1, + /*position, start, count, max_count*/ 4, + /*values*/ (size_t)n, + /*children*/ 0) + .AllocSize(); + } + // A lower bound for the overhead of fields other than values in a leaf node. + constexpr static size_type MinimumOverhead() { + return (size_type)(SizeWithNValues(1) - sizeof(value_type)); + } + + // Compute how many values we can fit onto a leaf node taking into account + // padding. + constexpr static size_type NodeTargetValues(const int begin, const int end) { + return begin == end ? begin + : SizeWithNValues((begin + end) / 2 + 1) > + params_type::kTargetNodeSize + ? NodeTargetValues(begin, (begin + end) / 2) + : NodeTargetValues((begin + end) / 2 + 1, end); + } + + enum { + kTargetNodeSize = params_type::kTargetNodeSize, + kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize), + + // We need a minimum of 3 values per internal node in order to perform + // splitting (1 value for the two nodes involved in the split and 1 value + // propagated to the parent as the delimiter for the split). + kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3, + + // The node is internal (i.e. is not a leaf node) if and only if `max_count` + // has this value. + kInternalNodeMaxCount = 0, + }; + + // Leaves can have less than kNodeValues values. + constexpr static layout_type LeafLayout(const int max_values = kNodeValues) { + return layout_type(/*parent*/ 1, + /*position, start, count, max_count*/ 4, + /*values*/ (size_t)max_values, + /*children*/ 0); + } + constexpr static layout_type InternalLayout() { + return layout_type(/*parent*/ 1, + /*position, start, count, max_count*/ 4, + /*values*/ kNodeValues, + /*children*/ kNodeValues + 1); + } + constexpr static size_type LeafSize(const int max_values = kNodeValues) { + return (size_type)LeafLayout(max_values).AllocSize(); + } + constexpr static size_type InternalSize() { + return (size_type)InternalLayout().AllocSize(); + } + + // N is the index of the type in the Layout definition. + // ElementType is the Nth type in the Layout definition. + template + inline typename layout_type::template ElementType *GetField() { + // We assert that we don't read from values that aren't there. + assert(N < 3 || !leaf()); + return InternalLayout().template Pointer(reinterpret_cast(this)); + } + + template + inline const typename layout_type::template ElementType *GetField() const { + assert(N < 3 || !leaf()); + return InternalLayout().template Pointer( + reinterpret_cast(this)); + } + + void set_parent(btree_node *p) { *GetField<0>() = p; } + field_type &mutable_count() { return GetField<1>()[2]; } + slot_type *slot(size_type i) { return &GetField<2>()[i]; } + const slot_type *slot(size_type i) const { return &GetField<2>()[i]; } + void set_position(field_type v) { GetField<1>()[0] = v; } + void set_start(field_type v) { GetField<1>()[1] = v; } + void set_count(field_type v) { GetField<1>()[2] = v; } + void set_max_count(field_type v) { GetField<1>()[3] = v; } + + public: + // Whether this is a leaf node or not. This value doesn't change after the + // node is created. + bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; } + + // Getter for the position of this node in its parent. + field_type position() const { return GetField<1>()[0]; } + + // Getter for the offset of the first value in the `values` array. + field_type start() const { return GetField<1>()[1]; } + + // Getters for the number of values stored in this node. + field_type count() const { return GetField<1>()[2]; } + field_type max_count() const { + // Internal nodes have max_count==kInternalNodeMaxCount. + // Leaf nodes have max_count in [1, kNodeValues]. + const field_type max_count = GetField<1>()[3]; + return max_count == field_type{kInternalNodeMaxCount} + ? field_type{kNodeValues} + : max_count; + } + + // Getter for the parent of this node. + btree_node *parent() const { return *GetField<0>(); } + // Getter for whether the node is the root of the tree. The parent of the + // root of the tree is the leftmost node in the tree which is guaranteed to + // be a leaf. + bool is_root() const { return parent()->leaf(); } + void make_root() { + assert(parent()->is_root()); + set_parent(parent()->parent()); + } + + // Getters for the key/value at position i in the node. + const key_type &key(size_type i) const { return params_type::key(slot(i)); } + reference value(size_type i) { return params_type::element(slot(i)); } + const_reference value(size_type i) const { return params_type::element(slot(i)); } + + // Getters/setter for the child at position i in the node. + btree_node *child(size_type i) const { return GetField<3>()[i]; } + btree_node *&mutable_child(size_type i) { return GetField<3>()[i]; } + void clear_child(size_type i) { + phmap::priv::SanitizerPoisonObject(&mutable_child(i)); + } + void set_child(size_type i, btree_node *c) { + phmap::priv::SanitizerUnpoisonObject(&mutable_child(i)); + mutable_child(i) = c; + c->set_position((field_type)i); + } + void init_child(int i, btree_node *c) { + set_child(i, c); + c->set_parent(this); + } + + // Returns the position of the first value whose key is not less than k. + template + SearchResult lower_bound( + const K &k, const key_compare &comp) const { + return use_linear_search::value ? linear_search(k, comp) + : binary_search(k, comp); + } + // Returns the position of the first value whose key is greater than k. + template + int upper_bound(const K &k, const key_compare &comp) const { + auto upper_compare = upper_bound_adapter(comp); + return use_linear_search::value ? linear_search(k, upper_compare).value + : binary_search(k, upper_compare).value; + } + + template + SearchResult::value> + linear_search(const K &k, const Compare &comp) const { + return linear_search_impl(k, 0, count(), comp, + btree_is_key_compare_to()); + } + + template + SearchResult::value> + binary_search(const K &k, const Compare &comp) const { + return binary_search_impl(k, 0, count(), comp, + btree_is_key_compare_to()); + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using plain compare. + template + SearchResult linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s < e) { + if (!comp(key(s), k)) { + break; + } + ++s; + } + return {s}; + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using compare-to. + template + SearchResult linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::true_type /* IsCompareTo */) const { + while (s < e) { + const phmap::weak_ordering c = comp(key(s), k); + if (c == 0) { + return {s, MatchKind::kEq}; + } else if (c > 0) { + break; + } + ++s; + } + return {s, MatchKind::kNe}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using plain compare. + template + SearchResult binary_search_impl( + const K &k, int s, int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s != e) { + const int mid = (s + e) >> 1; + if (comp(key(mid), k)) { + s = mid + 1; + } else { + e = mid; + } + } + return {s}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using compare-to. + template + SearchResult binary_search_impl( + const K &k, int s, int e, const CompareTo &comp, + std::true_type /* IsCompareTo */) const { + if (is_multi_container::value) { + MatchKind exact_match = MatchKind::kNe; + while (s != e) { + const int mid = (s + e) >> 1; + const phmap::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else { + e = mid; + if (c == 0) { + // Need to return the first value whose key is not less than k, + // which requires continuing the binary search if this is a + // multi-container. + exact_match = MatchKind::kEq; + } + } + } + return {s, exact_match}; + } else { // Not a multi-container. + while (s != e) { + const int mid = (s + e) >> 1; + const phmap::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else if (c > 0) { + e = mid; + } else { + return {mid, MatchKind::kEq}; + } + } + return {s, MatchKind::kNe}; + } + } + + // Emplaces a value at position i, shifting all existing values and + // children at positions >= i to the right by 1. + template + void emplace_value(size_type i, allocator_type *alloc, Args &&... args); + + // Removes the value at position i, shifting all existing values and children + // at positions > i to the left by 1. + void remove_value(int i, allocator_type *alloc); + + // Removes the values at positions [i, i + to_erase), shifting all values + // after that range to the left by to_erase. Does not change children at all. + void remove_values_ignore_children(int i, size_type to_erase, + allocator_type *alloc); + + // Rebalances a node with its right sibling. + void rebalance_right_to_left(int to_move, btree_node *right, + allocator_type *alloc); + void rebalance_left_to_right(int to_move, btree_node *right, + allocator_type *alloc); + + // Splits a node, moving a portion of the node's values to its right sibling. + void split(int insert_position, btree_node *dest, allocator_type *alloc); + + // Merges a node with its right sibling, moving all of the values and the + // delimiting key in the parent node onto itself. + void merge(btree_node *sibling, allocator_type *alloc); + + // Swap the contents of "this" and "src". + void swap(btree_node *src, allocator_type *alloc); + + // Node allocation/deletion routines. + static btree_node *init_leaf(btree_node *n, btree_node *parent, + int max_count) { + n->set_parent(parent); + n->set_position(0); + n->set_start(0); + n->set_count(0); + n->set_max_count((field_type)max_count); + phmap::priv::SanitizerPoisonMemoryRegion( + n->slot(0), max_count * sizeof(slot_type)); + return n; + } + static btree_node *init_internal(btree_node *n, btree_node *parent) { + init_leaf(n, parent, kNodeValues); + // Set `max_count` to a sentinel value to indicate that this node is + // internal. + n->set_max_count(kInternalNodeMaxCount); + phmap::priv::SanitizerPoisonMemoryRegion( + &n->mutable_child(0), (kNodeValues + 1) * sizeof(btree_node *)); + return n; + } + void destroy(allocator_type *alloc) { + for (int i = 0; i < count(); ++i) { + value_destroy(i, alloc); + } + } + + public: + // Exposed only for tests. + static bool testonly_uses_linear_node_search() { + return use_linear_search::value; + } + + private: + template + void value_init(const size_type i, allocator_type *alloc, Args &&... args) { + phmap::priv::SanitizerUnpoisonObject(slot(i)); + params_type::construct(alloc, slot(i), std::forward(args)...); + } + void value_destroy(const size_type i, allocator_type *alloc) { + params_type::destroy(alloc, slot(i)); + phmap::priv::SanitizerPoisonObject(slot(i)); + } + + // Move n values starting at value i in this node into the values starting at + // value j in node x. + void uninitialized_move_n(const size_type n, const size_type i, + const size_type j, btree_node *x, + allocator_type *alloc) { + phmap::priv::SanitizerUnpoisonMemoryRegion( + x->slot(j), n * sizeof(slot_type)); + for (slot_type *src = slot(i), *end = src + n, *dest = x->slot(j); + src != end; ++src, ++dest) { + params_type::construct(alloc, dest, src); + } + } + + // Destroys a range of n values, starting at index i. + void value_destroy_n(const size_type i, const size_type n, + allocator_type *alloc) { + for (int j = 0; j < n; ++j) { + value_destroy(i + j, alloc); + } + } + + template + friend class btree; + template + friend struct btree_iterator; + friend class BtreeNodePeer; + }; + + template + struct btree_iterator { + private: + using key_type = typename Node::key_type; + using size_type = typename Node::size_type; + using params_type = typename Node::params_type; + + using node_type = Node; + using normal_node = typename std::remove_const::type; + using const_node = const Node; + using normal_pointer = typename params_type::pointer; + using normal_reference = typename params_type::reference; + using const_pointer = typename params_type::const_pointer; + using const_reference = typename params_type::const_reference; + using slot_type = typename params_type::slot_type; + + using iterator = + btree_iterator; + using const_iterator = + btree_iterator; + + public: + // These aliases are public for std::iterator_traits. + using difference_type = typename Node::difference_type; + using value_type = typename params_type::value_type; + using pointer = Pointer; + using reference = Reference; + using iterator_category = std::bidirectional_iterator_tag; + + btree_iterator() : node(nullptr), position(-1) {} + btree_iterator(Node *n, int p) : node(n), position(p) {} + + // NOTE: this SFINAE allows for implicit conversions from iterator to + // const_iterator, but it specifically avoids defining copy constructors so + // that btree_iterator can be trivially copyable. This is for performance and + // binary size reasons. + template , iterator>::value && + std::is_same::value, + int> = 0> + btree_iterator(const btree_iterator &x) // NOLINT + : node(x.node), position(x.position) {} + + private: + // This SFINAE allows explicit conversions from const_iterator to + // iterator, but also avoids defining a copy constructor. + // NOTE: the const_cast is safe because this constructor is only called by + // non-const methods and the container owns the nodes. + template , const_iterator>::value && + std::is_same::value, + int> = 0> + explicit btree_iterator(const btree_iterator &x) + : node(const_cast(x.node)), position(x.position) {} + + // Increment/decrement the iterator. + void increment() { + if (node->leaf() && ++position < node->count()) { + return; + } + increment_slow(); + } + void increment_slow(); + + void decrement() { + if (node->leaf() && --position >= 0) { + return; + } + decrement_slow(); + } + void decrement_slow(); + + public: + bool operator==(const const_iterator &x) const { + return node == x.node && position == x.position; + } + bool operator!=(const const_iterator &x) const { + return node != x.node || position != x.position; + } + + // Accessors for the key/value the iterator is pointing at. + reference operator*() const { + return node->value(position); + } + pointer operator->() const { + return &node->value(position); + } + + btree_iterator& operator++() { + increment(); + return *this; + } + btree_iterator& operator--() { + decrement(); + return *this; + } + btree_iterator operator++(int) { + btree_iterator tmp = *this; + ++*this; + return tmp; + } + btree_iterator operator--(int) { + btree_iterator tmp = *this; + --*this; + return tmp; + } + + private: + template + friend class btree; + template + friend class btree_container; + template + friend class btree_set_container; + template + friend class btree_map_container; + template + friend class btree_multiset_container; + template + friend struct btree_iterator; + template + friend class base_checker; + + const key_type &key() const { return node->key(position); } + slot_type *slot() { return node->slot(position); } + + // The node in the tree the iterator is pointing at. + Node *node; + // The position within the node of the tree the iterator is pointing at. + // TODO(ezb): make this a field_type + int position; + }; + + template + class btree { + using node_type = btree_node; + using is_key_compare_to = typename Params::is_key_compare_to; + + // We use a static empty node for the root/leftmost/rightmost of empty btrees + // in order to avoid branching in begin()/end(). + struct alignas(node_type::Alignment()) EmptyNodeType : node_type { + using field_type = typename node_type::field_type; + node_type *parent; + field_type position = 0; + field_type start = 0; + field_type count = 0; + // max_count must be != kInternalNodeMaxCount (so that this node is regarded + // as a leaf node). max_count() is never called when the tree is empty. + field_type max_count = node_type::kInternalNodeMaxCount + 1; + +#ifdef _MSC_VER + // MSVC has constexpr code generations bugs here. + EmptyNodeType() : parent(this) {} +#else + constexpr EmptyNodeType(node_type *p) : parent(p) {} +#endif + }; + + static node_type *EmptyNode() { +#ifdef _MSC_VER + static EmptyNodeType* empty_node = new EmptyNodeType; + // This assert fails on some other construction methods. + assert(empty_node->parent == empty_node); + return empty_node; +#else + static constexpr EmptyNodeType empty_node( + const_cast(&empty_node)); + return const_cast(&empty_node); +#endif + } + + enum { + kNodeValues = node_type::kNodeValues, + kMinNodeValues = kNodeValues / 2, + }; + + struct node_stats { + using size_type = typename Params::size_type; + + node_stats(size_type l, size_type i) + : leaf_nodes(l), + internal_nodes(i) { + } + + node_stats& operator+=(const node_stats &x) { + leaf_nodes += x.leaf_nodes; + internal_nodes += x.internal_nodes; + return *this; + } + + size_type leaf_nodes; + size_type internal_nodes; + }; + + public: + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + using key_compare = typename Params::key_compare; + using value_compare = typename Params::value_compare; + using allocator_type = typename Params::allocator_type; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using iterator = btree_iterator; + using const_iterator = typename iterator::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using node_handle_type = node_handle; + + // Internal types made public for use by btree_container types. + using params_type = Params; + using slot_type = typename Params::slot_type; + + private: + // For use in copy_or_move_values_in_order. + const value_type &maybe_move_from_iterator(const_iterator x) { return *x; } + value_type &&maybe_move_from_iterator(iterator x) { return std::move(*x); } + + // Copies or moves (depending on the template parameter) the values in + // x into this btree in their order in x. This btree must be empty before this + // method is called. This method is used in copy construction, copy + // assignment, and move assignment. + template + void copy_or_move_values_in_order(Btree *x); + + // Validates that various assumptions/requirements are true at compile time. + constexpr static bool static_assert_validation(); + + public: + btree(const key_compare &comp, const allocator_type &alloc); + + btree(const btree &x); + btree(btree &&x) noexcept + : root_(std::move(x.root_)), + rightmost_(phmap::exchange(x.rightmost_, EmptyNode())), + size_(phmap::exchange(x.size_, 0)) { + x.mutable_root() = EmptyNode(); + } + + ~btree() { + // Put static_asserts in destructor to avoid triggering them before the type + // is complete. + static_assert(static_assert_validation(), "This call must be elided."); + clear(); + } + + // Assign the contents of x to *this. + btree &operator=(const btree &x); + btree &operator=(btree &&x) noexcept; + + iterator begin() { + return iterator(leftmost(), 0); + } + const_iterator begin() const { + return const_iterator(leftmost(), 0); + } + iterator end() { return iterator(rightmost_, rightmost_->count()); } + const_iterator end() const { + return const_iterator(rightmost_, rightmost_->count()); + } + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { + return reverse_iterator(begin()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Finds the first element whose key is not less than key. + template + iterator lower_bound(const K &key) { + return internal_end(internal_lower_bound(key)); + } + template + const_iterator lower_bound(const K &key) const { + return internal_end(internal_lower_bound(key)); + } + + // Finds the first element whose key is greater than key. + template + iterator upper_bound(const K &key) { + return internal_end(internal_upper_bound(key)); + } + template + const_iterator upper_bound(const K &key) const { + return internal_end(internal_upper_bound(key)); + } + + // Finds the range of values which compare equal to key. The first member of + // the returned pair is equal to lower_bound(key). The second member pair of + // the pair is equal to upper_bound(key). + template + std::pair equal_range(const K &key) { + return {lower_bound(key), upper_bound(key)}; + } + template + std::pair equal_range(const K &key) const { + return {lower_bound(key), upper_bound(key)}; + } + + // Inserts a value into the btree only if it does not already exist. The + // boolean return value indicates whether insertion succeeded or failed. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_unique(const key_type &key, Args &&... args); + + // Inserts with hint. Checks to see if the value should be placed immediately + // before `position` in the tree. If so, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_unique() were made. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_hint_unique(iterator position, + const key_type &key, + Args &&... args); + + // Insert a range of values into the btree. + template + void insert_iterator_unique(InputIterator b, InputIterator e); + + // Inserts a value into the btree. + template + iterator insert_multi(const key_type &key, ValueType &&v); + + // Inserts a value into the btree. + template + iterator insert_multi(ValueType &&v) { + return insert_multi(params_type::key(v), std::forward(v)); + } + + // Insert with hint. Check to see if the value should be placed immediately + // before position in the tree. If it does, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_multi(v) were made. + template + iterator insert_hint_multi(iterator position, ValueType &&v); + + // Insert a range of values into the btree. + template + void insert_iterator_multi(InputIterator b, InputIterator e); + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + // Requirement: does not read the value at `*iter`. + iterator erase(iterator iter); + + // Erases range. Returns the number of keys erased and an iterator pointing + // to the element after the last erased element. + std::pair erase(iterator begin, iterator end); + + // Erases the specified key from the btree. Returns 1 if an element was + // erased and 0 otherwise. + template + size_type erase_unique(const K &key); + + // Erases all of the entries matching the specified key from the + // btree. Returns the number of elements erased. + template + size_type erase_multi(const K &key); + + // Finds the iterator corresponding to a key or returns end() if the key is + // not present. + template + iterator find(const K &key) { + return internal_end(internal_find(key)); + } + template + const_iterator find(const K &key) const { + return internal_end(internal_find(key)); + } + + // Returns a count of the number of times the key appears in the btree. + template + size_type count_unique(const K &key) const { + const iterator begin = internal_find(key); + if (begin.node == nullptr) { + // The key doesn't exist in the tree. + return 0; + } + return 1; + } + // Returns a count of the number of times the key appears in the btree. + template + size_type count_multi(const K &key) const { + const auto range = equal_range(key); + return std::distance(range.first, range.second); + } + + // Clear the btree, deleting all of the values it contains. + void clear(); + + // Swap the contents of *this and x. + void swap(btree &x); + + const key_compare &key_comp() const noexcept { + return root_.template get<0>(); + } + template + bool compare_keys(const K &x, const LK &y) const { + return compare_internal::compare_result_as_less_than(key_comp()(x, y)); + } + + value_compare value_comp() const { return value_compare(key_comp()); } + + // Verifies the structure of the btree. + void verify() const; + + // Size routines. + size_type size() const { return size_; } + size_type max_size() const { return (std::numeric_limits::max)(); } + bool empty() const { return size_ == 0; } + + // The height of the btree. An empty tree will have height 0. + size_type height() const { + size_type h = 0; + if (!empty()) { + // Count the length of the chain from the leftmost node up to the + // root. We actually count from the root back around to the level below + // the root, but the calculation is the same because of the circularity + // of that traversal. + const node_type *n = root(); + do { + ++h; + n = n->parent(); + } while (n != root()); + } + return h; + } + + // The number of internal, leaf and total nodes used by the btree. + size_type leaf_nodes() const { + return internal_stats(root()).leaf_nodes; + } + size_type internal_nodes() const { + return internal_stats(root()).internal_nodes; + } + size_type nodes() const { + node_stats stats = internal_stats(root()); + return stats.leaf_nodes + stats.internal_nodes; + } + + // The total number of bytes used by the btree. + size_type bytes_used() const { + node_stats stats = internal_stats(root()); + if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { + return sizeof(*this) + + node_type::LeafSize(root()->max_count()); + } else { + return sizeof(*this) + + stats.leaf_nodes * node_type::LeafSize() + + stats.internal_nodes * node_type::InternalSize(); + } + } + + // The average number of bytes used per value stored in the btree. + static double average_bytes_per_value() { + // Returns the number of bytes per value on a leaf node that is 75% + // full. Experimentally, this matches up nicely with the computed number of + // bytes per value in trees that had their values inserted in random order. + return node_type::LeafSize() / (kNodeValues * 0.75); + } + + // The fullness of the btree. Computed as the number of elements in the btree + // divided by the maximum number of elements a tree with the current number + // of nodes could hold. A value of 1 indicates perfect space + // utilization. Smaller values indicate space wastage. + // Returns 0 for empty trees. + double fullness() const { + if (empty()) return 0.0; + return static_cast(size()) / (nodes() * kNodeValues); + } + // The overhead of the btree structure in bytes per node. Computed as the + // total number of bytes used by the btree minus the number of bytes used for + // storing elements divided by the number of elements. + // Returns 0 for empty trees. + double overhead() const { + if (empty()) return 0.0; + return (bytes_used() - size() * sizeof(value_type)) / + static_cast(size()); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { + return allocator(); + } + + private: + // Internal accessor routines. + node_type *root() { return root_.template get<2>(); } + const node_type *root() const { return root_.template get<2>(); } + node_type *&mutable_root() noexcept { return root_.template get<2>(); } + key_compare *mutable_key_comp() noexcept { return &root_.template get<0>(); } + + // The leftmost node is stored as the parent of the root node. + node_type *leftmost() { return root()->parent(); } + const node_type *leftmost() const { return root()->parent(); } + + // Allocator routines. + allocator_type *mutable_allocator() noexcept { + return &root_.template get<1>(); + } + const allocator_type &allocator() const noexcept { + return root_.template get<1>(); + } + + // Allocates a correctly aligned node of at least size bytes using the + // allocator. + node_type *allocate(const size_type size) { + return reinterpret_cast( + phmap::priv::Allocate( + mutable_allocator(), (size_t)size)); + } + + // Node creation/deletion routines. + node_type* new_internal_node(node_type *parent) { + node_type *p = allocate(node_type::InternalSize()); + return node_type::init_internal(p, parent); + } + node_type* new_leaf_node(node_type *parent) { + node_type *p = allocate(node_type::LeafSize()); + return node_type::init_leaf(p, parent, kNodeValues); + } + node_type *new_leaf_root_node(const int max_count) { + node_type *p = allocate(node_type::LeafSize(max_count)); + return node_type::init_leaf(p, p, max_count); + } + + // Deletion helper routines. + void erase_same_node(iterator begin, iterator end); + iterator erase_from_leaf_node(iterator begin, size_type to_erase); + iterator rebalance_after_delete(iterator iter); + + // Deallocates a node of a certain size in bytes using the allocator. + void deallocate(const size_type size, node_type *node) { + phmap::priv::Deallocate( + mutable_allocator(), node, (size_t)size); + } + + void delete_internal_node(node_type *node) { + node->destroy(mutable_allocator()); + deallocate(node_type::InternalSize(), node); + } + void delete_leaf_node(node_type *node) { + node->destroy(mutable_allocator()); + deallocate(node_type::LeafSize(node->max_count()), node); + } + + // Rebalances or splits the node iter points to. + void rebalance_or_split(iterator *iter); + + // Merges the values of left, right and the delimiting key on their parent + // onto left, removing the delimiting key and deleting right. + void merge_nodes(node_type *left, node_type *right); + + // Tries to merge node with its left or right sibling, and failing that, + // rebalance with its left or right sibling. Returns true if a merge + // occurred, at which point it is no longer valid to access node. Returns + // false if no merging took place. + bool try_merge_or_rebalance(iterator *iter); + + // Tries to shrink the height of the tree by 1. + void try_shrink(); + + iterator internal_end(iterator iter) { + return iter.node != nullptr ? iter : end(); + } + const_iterator internal_end(const_iterator iter) const { + return iter.node != nullptr ? iter : end(); + } + + // Emplaces a value into the btree immediately before iter. Requires that + // key(v) <= iter.key() and (--iter).key() <= key(v). + template + iterator internal_emplace(iterator iter, Args &&... args); + + // Returns an iterator pointing to the first value >= the value "iter" is + // pointing at. Note that "iter" might be pointing to an invalid location as + // iter.position == iter.node->count(). This routine simply moves iter up in + // the tree to a valid location. + // Requires: iter.node is non-null. + template + static IterType internal_last(IterType iter); + + // Returns an iterator pointing to the leaf position at which key would + // reside in the tree. We provide 2 versions of internal_locate. The first + // version uses a less-than comparator and is incapable of distinguishing when + // there is an exact match. The second version is for the key-compare-to + // specialization and distinguishes exact matches. The key-compare-to + // specialization allows the caller to avoid a subsequent comparison to + // determine if an exact match was made, which is important for keys with + // expensive comparison, such as strings. + template + SearchResult internal_locate( + const K &key) const; + + template + SearchResult internal_locate_impl( + const K &key, std::false_type /* IsCompareTo */) const; + + template + SearchResult internal_locate_impl( + const K &key, std::true_type /* IsCompareTo */) const; + + // Internal routine which implements lower_bound(). + template + iterator internal_lower_bound(const K &key) const; + + // Internal routine which implements upper_bound(). + template + iterator internal_upper_bound(const K &key) const; + + // Internal routine which implements find(). + template + iterator internal_find(const K &key) const; + + // Deletes a node and all of its children. + void internal_clear(node_type *node); + + // Verifies the tree structure of node. + int internal_verify(const node_type *node, + const key_type *lo, const key_type *hi) const; + + node_stats internal_stats(const node_type *node) const { + // The root can be a static empty node. + if (node == nullptr || (node == root() && empty())) { + return node_stats(0, 0); + } + if (node->leaf()) { + return node_stats(1, 0); + } + node_stats res(0, 1); + for (int i = 0; i <= node->count(); ++i) { + res += internal_stats(node->child(i)); + } + return res; + } + + public: + // Exposed only for tests. + static bool testonly_uses_linear_node_search() { + return node_type::testonly_uses_linear_node_search(); + } + + private: + // We use compressed tuple in order to save space because key_compare and + // allocator_type are usually empty. + phmap::priv::CompressedTuple + root_; + + // A pointer to the rightmost node. Note that the leftmost node is stored as + // the root's parent. + node_type *rightmost_; + + // Number of values. + size_type size_; + }; + + //// + // btree_node methods + template + template + inline void btree_node

::emplace_value(const size_type i, + allocator_type *alloc, + Args &&... args) { + assert(i <= count()); + // Shift old values to create space for new value and then construct it in + // place. + if (i < count()) { + value_init(count(), alloc, slot(count() - 1)); + for (size_type j = count() - 1; j > i; --j) + params_type::move(alloc, slot(j - 1), slot(j)); + value_destroy(i, alloc); + } + value_init(i, alloc, std::forward(args)...); + set_count((field_type)(count() + 1)); + + if (!leaf() && count() > i + 1) { + for (int j = count(); j > i + 1; --j) { + set_child(j, child(j - 1)); + } + clear_child(i + 1); + } + } + + template + inline void btree_node

::remove_value(const int i, allocator_type *alloc) { + if (!leaf() && count() > i + 1) { + assert(child(i + 1)->count() == 0); + for (size_type j = i + 1; j < count(); ++j) { + set_child(j, child(j + 1)); + } + clear_child(count()); + } + + remove_values_ignore_children(i, /*to_erase=*/1, alloc); + } + + template + inline void btree_node

::remove_values_ignore_children( + int i, size_type to_erase, allocator_type *alloc) { + params_type::move(alloc, slot(i + to_erase), slot(count()), slot(i)); + value_destroy_n(count() - to_erase, to_erase, alloc); + set_count((field_type)(count() - to_erase)); + } + + template + void btree_node

::rebalance_right_to_left(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(right->count() >= count()); + assert(to_move >= 1); + assert(to_move <= right->count()); + + // 1) Move the delimiting value in the parent to the left node. + value_init(count(), alloc, parent()->slot(position())); + + // 2) Move the (to_move - 1) values from the right node to the left node. + right->uninitialized_move_n(to_move - 1, 0, count() + 1, this, alloc); + + // 3) Move the new delimiting value to the parent from the right node. + params_type::move(alloc, right->slot(to_move - 1), + parent()->slot(position())); + + // 4) Shift the values in the right node to their correct position. + params_type::move(alloc, right->slot(to_move), right->slot(right->count()), + right->slot(0)); + + // 5) Destroy the now-empty to_move entries in the right node. + right->value_destroy_n(right->count() - to_move, to_move, alloc); + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = 0; i < to_move; ++i) { + init_child(count() + i + 1, right->child(i)); + } + for (int i = 0; i <= right->count() - to_move; ++i) { + assert(i + to_move <= right->max_count()); + right->init_child(i, right->child(i + to_move)); + right->clear_child(i + to_move); + } + } + + // Fixup the counts on the left and right nodes. + set_count((field_type)(count() + to_move)); + right->set_count((field_type)(right->count() - to_move)); + } + + template + void btree_node

::rebalance_left_to_right(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(count() >= right->count()); + assert(to_move >= 1); + assert(to_move <= count()); + + // Values in the right node are shifted to the right to make room for the + // new to_move values. Then, the delimiting value in the parent and the + // other (to_move - 1) values in the left node are moved into the right node. + // Lastly, a new delimiting value is moved from the left node into the + // parent, and the remaining empty left node entries are destroyed. + + if (right->count() >= to_move) { + // The original location of the right->count() values are sufficient to hold + // the new to_move entries from the parent and left node. + + // 1) Shift existing values in the right node to their correct positions. + right->uninitialized_move_n(to_move, right->count() - to_move, + right->count(), right, alloc); + for (slot_type *src = right->slot(right->count() - to_move - 1), + *dest = right->slot(right->count() - 1), + *end = right->slot(0); + src >= end; --src, --dest) { + params_type::move(alloc, src, dest); + } + + // 2) Move the delimiting value in the parent to the right node. + params_type::move(alloc, parent()->slot(position()), + right->slot(to_move - 1)); + + // 3) Move the (to_move - 1) values from the left node to the right node. + params_type::move(alloc, slot(count() - (to_move - 1)), slot(count()), + right->slot(0)); + } else { + // The right node does not have enough initialized space to hold the new + // to_move entries, so part of them will move to uninitialized space. + + // 1) Shift existing values in the right node to their correct positions. + right->uninitialized_move_n(right->count(), 0, to_move, right, alloc); + + // 2) Move the delimiting value in the parent to the right node. + right->value_init(to_move - 1, alloc, parent()->slot(position())); + + // 3) Move the (to_move - 1) values from the left node to the right node. + const size_type uninitialized_remaining = to_move - right->count() - 1; + uninitialized_move_n(uninitialized_remaining, + count() - uninitialized_remaining, right->count(), + right, alloc); + params_type::move(alloc, slot(count() - (to_move - 1)), + slot(count() - uninitialized_remaining), right->slot(0)); + } + + // 4) Move the new delimiting value to the parent from the left node. + params_type::move(alloc, slot(count() - to_move), parent()->slot(position())); + + // 5) Destroy the now-empty to_move entries in the left node. + value_destroy_n(count() - to_move, to_move, alloc); + + if (!leaf()) { + // Move the child pointers from the left to the right node. + for (int i = right->count(); i >= 0; --i) { + right->init_child(i + to_move, right->child(i)); + right->clear_child(i); + } + for (int i = 1; i <= to_move; ++i) { + right->init_child(i - 1, child(count() - to_move + i)); + clear_child(count() - to_move + i); + } + } + + // Fixup the counts on the left and right nodes. + set_count((field_type)(count() - to_move)); + right->set_count((field_type)(right->count() + to_move)); + } + + template + void btree_node

::split(const int insert_position, btree_node *dest, + allocator_type *alloc) { + assert(dest->count() == 0); + assert(max_count() == kNodeValues); + + // We bias the split based on the position being inserted. If we're + // inserting at the beginning of the left node then bias the split to put + // more values on the right node. If we're inserting at the end of the + // right node then bias the split to put more values on the left node. + if (insert_position == 0) { + dest->set_count((field_type)(count() - 1)); + } else if (insert_position == kNodeValues) { + dest->set_count(0); + } else { + dest->set_count((field_type)(count() / 2)); + } + set_count((field_type)(count() - dest->count())); + assert(count() >= 1); + + // Move values from the left sibling to the right sibling. + uninitialized_move_n(dest->count(), count(), 0, dest, alloc); + + // Destroy the now-empty entries in the left node. + value_destroy_n(count(), dest->count(), alloc); + + // The split key is the largest value in the left sibling. + set_count((field_type)(count() - 1)); + parent()->emplace_value(position(), alloc, slot(count())); + value_destroy(count(), alloc); + parent()->init_child(position() + 1, dest); + + if (!leaf()) { + for (int i = 0; i <= dest->count(); ++i) { + assert(child(count() + i + 1) != nullptr); + dest->init_child(i, child(count() + i + 1)); + clear_child(count() + i + 1); + } + } + } + + template + void btree_node

::merge(btree_node *src, allocator_type *alloc) { + assert(parent() == src->parent()); + assert(position() + 1 == src->position()); + + // Move the delimiting value to the left node. + value_init(count(), alloc, parent()->slot(position())); + + // Move the values from the right to the left node. + src->uninitialized_move_n(src->count(), 0, count() + 1, this, alloc); + + // Destroy the now-empty entries in the right node. + src->value_destroy_n(0, src->count(), alloc); + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = 0; i <= src->count(); ++i) { + init_child(count() + i + 1, src->child(i)); + src->clear_child(i); + } + } + + // Fixup the counts on the src and dest nodes. + set_count((field_type)(1 + count() + src->count())); + src->set_count(0); + + // Remove the value on the parent node. + parent()->remove_value(position(), alloc); + } + + template + void btree_node

::swap(btree_node *x, allocator_type *alloc) { + using std::swap; + assert(leaf() == x->leaf()); + + // Determine which is the smaller/larger node. + btree_node *smaller = this, *larger = x; + if (smaller->count() > larger->count()) { + swap(smaller, larger); + } + + // Swap the values. + for (slot_type *a = smaller->slot(0), *b = larger->slot(0), + *end = a + smaller->count(); + a != end; ++a, ++b) { + params_type::swap(alloc, a, b); + } + + // Move values that can't be swapped. + const size_type to_move = larger->count() - smaller->count(); + larger->uninitialized_move_n(to_move, smaller->count(), smaller->count(), + smaller, alloc); + larger->value_destroy_n(smaller->count(), to_move, alloc); + + if (!leaf()) { + // Swap the child pointers. + std::swap_ranges(&smaller->mutable_child(0), + &smaller->mutable_child(smaller->count() + 1), + &larger->mutable_child(0)); + // Update swapped children's parent pointers. + int i = 0; + for (; i <= smaller->count(); ++i) { + smaller->child(i)->set_parent(smaller); + larger->child(i)->set_parent(larger); + } + // Move the child pointers that couldn't be swapped. + for (; i <= larger->count(); ++i) { + smaller->init_child(i, larger->child(i)); + larger->clear_child(i); + } + } + + // Swap the counts. + swap(mutable_count(), x->mutable_count()); + } + + //// + // btree_iterator methods + template + void btree_iterator::increment_slow() { + if (node->leaf()) { + assert(position >= node->count()); + btree_iterator save(*this); + while (position == node->count() && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position(); + node = node->parent(); + } + if (position == node->count()) { + *this = save; + } + } else { + assert(position < node->count()); + node = node->child(position + 1); + while (!node->leaf()) { + node = node->child(0); + } + position = 0; + } + } + + template + void btree_iterator::decrement_slow() { + if (node->leaf()) { + assert(position <= -1); + btree_iterator save(*this); + while (position < 0 && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position() - 1; + node = node->parent(); + } + if (position < 0) { + *this = save; + } + } else { + assert(position >= 0); + node = node->child(position); + while (!node->leaf()) { + node = node->child(node->count()); + } + position = node->count() - 1; + } + } + + //// + // btree methods + template + template + void btree

::copy_or_move_values_in_order(Btree *x) { + static_assert(std::is_same::value || + std::is_same::value, + "Btree type must be same or const."); + assert(empty()); + + // We can avoid key comparisons because we know the order of the + // values is the same order we'll store them in. + auto iter = x->begin(); + if (iter == x->end()) return; + insert_multi(maybe_move_from_iterator(iter)); + ++iter; + for (; iter != x->end(); ++iter) { + // If the btree is not empty, we can just insert the new value at the end + // of the tree. + internal_emplace(end(), maybe_move_from_iterator(iter)); + } + } + + template + constexpr bool btree

::static_assert_validation() { + static_assert(std::is_nothrow_copy_constructible::value, + "Key comparison must be nothrow copy constructible"); + static_assert(std::is_nothrow_copy_constructible::value, + "Allocator must be nothrow copy constructible"); + static_assert(type_traits_internal::is_trivially_copyable::value, + "iterator not trivially copyable."); + + // Note: We assert that kTargetValues, which is computed from + // Params::kTargetNodeSize, must fit the node_type::field_type. + static_assert( + kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))), + "target node size too large"); + + // Verify that key_compare returns an phmap::{weak,strong}_ordering or bool. + using compare_result_type = + phmap::invoke_result_t; + static_assert( + std::is_same::value || + std::is_convertible::value, + "key comparison function must return phmap::{weak,strong}_ordering or " + "bool."); + + // Test the assumption made in setting kNodeValueSpace. + static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4, + "node space assumption incorrect"); + + return true; + } + + template + btree

::btree(const key_compare &comp, const allocator_type &alloc) + : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {} + + template + btree

::btree(const btree &x) : btree(x.key_comp(), x.allocator()) { + copy_or_move_values_in_order(&x); + } + + template + template + auto btree

::insert_unique(const key_type &key, Args &&... args) + -> std::pair { + if (empty()) { + mutable_root() = rightmost_ = new_leaf_root_node(1); + } + + auto res = internal_locate(key); + iterator &iter = res.value; + + if (res.HasMatch()) { + if (res.IsEq()) { + // The key already exists in the tree, do nothing. + return {iter, false}; + } + } else { + iterator last = internal_last(iter); + if (last.node && !compare_keys(key, last.key())) { + // The key already exists in the tree, do nothing. + return {last, false}; + } + } + return {internal_emplace(iter, std::forward(args)...), true}; + } + + template + template + inline auto btree

::insert_hint_unique(iterator position, const key_type &key, + Args &&... args) + -> std::pair { + if (!empty()) { + if (position == end() || compare_keys(key, position.key())) { + iterator prev = position; + if (position == begin() || compare_keys((--prev).key(), key)) { + // prev.key() < key < position.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else if (compare_keys(position.key(), key)) { + ++position; + if (position == end() || compare_keys(key, position.key())) { + // {original `position`}.key() < key < {current `position`}.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else { + // position.key() == key + return {position, false}; + } + } + return insert_unique(key, std::forward(args)...); + } + + template + template + void btree

::insert_iterator_unique(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_unique(end(), params_type::key(*b), *b); + } + } + + template + template + auto btree

::insert_multi(const key_type &key, ValueType &&v) -> iterator { + if (empty()) { + mutable_root() = rightmost_ = new_leaf_root_node(1); + } + + iterator iter = internal_upper_bound(key); + if (iter.node == nullptr) { + iter = end(); + } + return internal_emplace(iter, std::forward(v)); + } + + template + template + auto btree

::insert_hint_multi(iterator position, ValueType &&v) -> iterator { + if (!empty()) { + const key_type &key = params_type::key(v); + if (position == end() || !compare_keys(position.key(), key)) { + iterator prev = position; + if (position == begin() || !compare_keys(key, (--prev).key())) { + // prev.key() <= key <= position.key() + return internal_emplace(position, std::forward(v)); + } + } else { + iterator next = position; + ++next; + if (next == end() || !compare_keys(next.key(), key)) { + // position.key() < key <= next.key() + return internal_emplace(next, std::forward(v)); + } + } + } + return insert_multi(std::forward(v)); + } + + template + template + void btree

::insert_iterator_multi(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_multi(end(), *b); + } + } + + template + auto btree

::operator=(const btree &x) -> btree & { + if (this != &x) { + clear(); + + *mutable_key_comp() = x.key_comp(); + if (phmap::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + *mutable_allocator() = x.allocator(); + } + + copy_or_move_values_in_order(&x); + } + return *this; + } + + template + auto btree

::operator=(btree &&x) noexcept -> btree & { + if (this != &x) { + clear(); + + using std::swap; + if (phmap::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + // Note: `root_` also contains the allocator and the key comparator. + swap(root_, x.root_); + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); + } else { + if (allocator() == x.allocator()) { + swap(mutable_root(), x.mutable_root()); + swap(*mutable_key_comp(), *x.mutable_key_comp()); + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); + } else { + // We aren't allowed to propagate the allocator and the allocator is + // different so we can't take over its memory. We must move each element + // individually. We need both `x` and `this` to have `x`s key comparator + // while moving the values so we can't swap the key comparators. + *mutable_key_comp() = x.key_comp(); + copy_or_move_values_in_order(&x); + } + } + } + return *this; + } + + template + auto btree

::erase(iterator iter) -> iterator { + bool internal_delete = false; + if (!iter.node->leaf()) { + // Deletion of a value on an internal node. First, move the largest value + // from our left child here, then delete that position (in remove_value() + // below). We can get to the largest value from our left child by + // decrementing iter. + iterator internal_iter(iter); + --iter; + assert(iter.node->leaf()); + params_type::move(mutable_allocator(), iter.node->slot(iter.position), + internal_iter.node->slot(internal_iter.position)); + internal_delete = true; + } + + // Delete the key from the leaf. + iter.node->remove_value(iter.position, mutable_allocator()); + --size_; + + // We want to return the next value after the one we just erased. If we + // erased from an internal node (internal_delete == true), then the next + // value is ++(++iter). If we erased from a leaf node (internal_delete == + // false) then the next value is ++iter. Note that ++iter may point to an + // internal node and the value in the internal node may move to a leaf node + // (iter.node) when rebalancing is performed at the leaf level. + + iterator res = rebalance_after_delete(iter); + + // If we erased from an internal node, advance the iterator. + if (internal_delete) { + ++res; + } + return res; + } + + template + auto btree

::rebalance_after_delete(iterator iter) -> iterator { + // Merge/rebalance as we walk back up the tree. + iterator res(iter); + bool first_iteration = true; + for (;;) { + if (iter.node == root()) { + try_shrink(); + if (empty()) { + return end(); + } + break; + } + if (iter.node->count() >= kMinNodeValues) { + break; + } + bool merged = try_merge_or_rebalance(&iter); + // On the first iteration, we should update `res` with `iter` because `res` + // may have been invalidated. + if (first_iteration) { + res = iter; + first_iteration = false; + } + if (!merged) { + break; + } + iter.position = iter.node->position(); + iter.node = iter.node->parent(); + } + + // Adjust our return value. If we're pointing at the end of a node, advance + // the iterator. + if (res.position == res.node->count()) { + res.position = res.node->count() - 1; + ++res; + } + + return res; + } + + template + auto btree

::erase(iterator begin, iterator end) + -> std::pair { + difference_type count = std::distance(begin, end); + assert(count >= 0); + + if (count == 0) { + return {0, begin}; + } + + if (count == size_) { + clear(); + return {count, this->end()}; + } + + if (begin.node == end.node) { + erase_same_node(begin, end); + size_ -= count; + return {count, rebalance_after_delete(begin)}; + } + + const size_type target_size = size_ - count; + while (size_ > target_size) { + if (begin.node->leaf()) { + const size_type remaining_to_erase = size_ - target_size; + const size_type remaining_in_node = begin.node->count() - begin.position; + begin = erase_from_leaf_node( + begin, (std::min)(remaining_to_erase, remaining_in_node)); + } else { + begin = erase(begin); + } + } + return {count, begin}; + } + + template + void btree

::erase_same_node(iterator begin, iterator end) { + assert(begin.node == end.node); + assert(end.position > begin.position); + + node_type *node = begin.node; + size_type to_erase = end.position - begin.position; + if (!node->leaf()) { + // Delete all children between begin and end. + for (size_type i = 0; i < to_erase; ++i) { + internal_clear(node->child(begin.position + i + 1)); + } + // Rotate children after end into new positions. + for (size_type i = begin.position + to_erase + 1; i <= node->count(); ++i) { + node->set_child(i - to_erase, node->child(i)); + node->clear_child(i); + } + } + node->remove_values_ignore_children(begin.position, to_erase, + mutable_allocator()); + + // Do not need to update rightmost_, because + // * either end == this->end(), and therefore node == rightmost_, and still + // exists + // * or end != this->end(), and therefore rightmost_ hasn't been erased, since + // it wasn't covered in [begin, end) + } + + template + auto btree

::erase_from_leaf_node(iterator begin, size_type to_erase) + -> iterator { + node_type *node = begin.node; + assert(node->leaf()); + assert(node->count() > begin.position); + assert(begin.position + to_erase <= node->count()); + + node->remove_values_ignore_children(begin.position, to_erase, + mutable_allocator()); + + size_ -= to_erase; + + return rebalance_after_delete(begin); + } + + template + template + auto btree

::erase_unique(const K &key) -> size_type { + const iterator iter = internal_find(key); + if (iter.node == nullptr) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + erase(iter); + return 1; + } + + template + template + auto btree

::erase_multi(const K &key) -> size_type { + const iterator begin = internal_lower_bound(key); + if (begin.node == nullptr) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + // Delete all of the keys between begin and upper_bound(key). + const iterator end = internal_end(internal_upper_bound(key)); + return erase(begin, end).first; + } + + template + void btree

::clear() { + if (!empty()) { + internal_clear(root()); + } + mutable_root() = EmptyNode(); + rightmost_ = EmptyNode(); + size_ = 0; + } + + template + void btree

::swap(btree &x) { + using std::swap; + if (phmap::allocator_traits< + allocator_type>::propagate_on_container_swap::value) { + // Note: `root_` also contains the allocator and the key comparator. + swap(root_, x.root_); + } else { + // It's undefined behavior if the allocators are unequal here. + assert(allocator() == x.allocator()); + swap(mutable_root(), x.mutable_root()); + swap(*mutable_key_comp(), *x.mutable_key_comp()); + } + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); + } + + template + void btree

::verify() const { + assert(root() != nullptr); + assert(leftmost() != nullptr); + assert(rightmost_ != nullptr); + assert(empty() || size() == internal_verify(root(), nullptr, nullptr)); + assert(leftmost() == (++const_iterator(root(), -1)).node); + assert(rightmost_ == (--const_iterator(root(), root()->count())).node); + assert(leftmost()->leaf()); + assert(rightmost_->leaf()); + } + + template + void btree

::rebalance_or_split(iterator *iter) { + node_type *&node = iter->node; + int &insert_position = iter->position; + assert(node->count() == node->max_count()); + assert(kNodeValues == node->max_count()); + + // First try to make room on the node by rebalancing. + node_type *parent = node->parent(); + if (node != root()) { + if (node->position() > 0) { + // Try rebalancing with our left sibling. + node_type *left = parent->child(node->position() - 1); + assert(left->max_count() == kNodeValues); + if (left->count() < kNodeValues) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the end of the right node then we bias rebalancing to + // fill up the left node. + int to_move = (kNodeValues - left->count()) / + (1 + (insert_position < kNodeValues)); + to_move = (std::max)(1, to_move); + + if (((insert_position - to_move) >= 0) || + ((left->count() + to_move) < kNodeValues)) { + left->rebalance_right_to_left(to_move, node, mutable_allocator()); + + assert(node->max_count() - node->count() == to_move); + insert_position = insert_position - to_move; + if (insert_position < 0) { + insert_position = insert_position + left->count() + 1; + node = left; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + if (node->position() < parent->count()) { + // Try rebalancing with our right sibling. + node_type *right = parent->child(node->position() + 1); + assert(right->max_count() == kNodeValues); + if (right->count() < kNodeValues) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the beginning of the left node then we bias rebalancing + // to fill up the right node. + int to_move = + (kNodeValues - right->count()) / (1 + (insert_position > 0)); + to_move = (std::max)(1, to_move); + + if ((insert_position <= (node->count() - to_move)) || + ((right->count() + to_move) < kNodeValues)) { + node->rebalance_left_to_right(to_move, right, mutable_allocator()); + + if (insert_position > node->count()) { + insert_position = insert_position - node->count() - 1; + node = right; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + // Rebalancing failed, make sure there is room on the parent node for a new + // value. + assert(parent->max_count() == kNodeValues); + if (parent->count() == kNodeValues) { + iterator parent_iter(node->parent(), node->position()); + rebalance_or_split(&parent_iter); + } + } else { + // Rebalancing not possible because this is the root node. + // Create a new root node and set the current root node as the child of the + // new root. + parent = new_internal_node(parent); + parent->init_child(0, root()); + mutable_root() = parent; + // If the former root was a leaf node, then it's now the rightmost node. + assert(!parent->child(0)->leaf() || parent->child(0) == rightmost_); + } + + // Split the node. + node_type *split_node; + if (node->leaf()) { + split_node = new_leaf_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + if (rightmost_ == node) rightmost_ = split_node; + } else { + split_node = new_internal_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + } + + if (insert_position > node->count()) { + insert_position = insert_position - node->count() - 1; + node = split_node; + } + } + + template + void btree

::merge_nodes(node_type *left, node_type *right) { + left->merge(right, mutable_allocator()); + if (right->leaf()) { + if (rightmost_ == right) rightmost_ = left; + delete_leaf_node(right); + } else { + delete_internal_node(right); + } + } + + template + bool btree

::try_merge_or_rebalance(iterator *iter) { + node_type *parent = iter->node->parent(); + if (iter->node->position() > 0) { + // Try merging with our left sibling. + node_type *left = parent->child(iter->node->position() - 1); + assert(left->max_count() == kNodeValues); + if ((1 + left->count() + iter->node->count()) <= kNodeValues) { + iter->position += 1 + left->count(); + merge_nodes(left, iter->node); + iter->node = left; + return true; + } + } + if (iter->node->position() < parent->count()) { + // Try merging with our right sibling. + node_type *right = parent->child(iter->node->position() + 1); + assert(right->max_count() == kNodeValues); + if ((1 + iter->node->count() + right->count()) <= kNodeValues) { + merge_nodes(iter->node, right); + return true; + } + // Try rebalancing with our right sibling. We don't perform rebalancing if + // we deleted the first element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the front of the tree. + if ((right->count() > kMinNodeValues) && + ((iter->node->count() == 0) || + (iter->position > 0))) { + int to_move = (right->count() - iter->node->count()) / 2; + to_move = (std::min)(to_move, right->count() - 1); + iter->node->rebalance_right_to_left(to_move, right, mutable_allocator()); + return false; + } + } + if (iter->node->position() > 0) { + // Try rebalancing with our left sibling. We don't perform rebalancing if + // we deleted the last element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the back of the tree. + node_type *left = parent->child(iter->node->position() - 1); + if ((left->count() > kMinNodeValues) && + ((iter->node->count() == 0) || + (iter->position < iter->node->count()))) { + int to_move = (left->count() - iter->node->count()) / 2; + to_move = (std::min)(to_move, left->count() - 1); + left->rebalance_left_to_right(to_move, iter->node, mutable_allocator()); + iter->position += to_move; + return false; + } + } + return false; + } + + template + void btree

::try_shrink() { + if (root()->count() > 0) { + return; + } + // Deleted the last item on the root node, shrink the height of the tree. + if (root()->leaf()) { + assert(size() == 0); + delete_leaf_node(root()); + mutable_root() = EmptyNode(); + rightmost_ = EmptyNode(); + } else { + node_type *child = root()->child(0); + child->make_root(); + delete_internal_node(root()); + mutable_root() = child; + } + } + + template + template + inline IterType btree

::internal_last(IterType iter) { + assert(iter.node != nullptr); + while (iter.position == iter.node->count()) { + iter.position = iter.node->position(); + iter.node = iter.node->parent(); + if (iter.node->leaf()) { + iter.node = nullptr; + break; + } + } + return iter; + } + + template + template + inline auto btree

::internal_emplace(iterator iter, Args &&... args) + -> iterator { + if (!iter.node->leaf()) { + // We can't insert on an internal node. Instead, we'll insert after the + // previous value which is guaranteed to be on a leaf node. + --iter; + ++iter.position; + } + const int max_count = iter.node->max_count(); + if (iter.node->count() == max_count) { + // Make room in the leaf for the new item. + if (max_count < kNodeValues) { + // Insertion into the root where the root is smaller than the full node + // size. Simply grow the size of the root node. + assert(iter.node == root()); + iter.node = + new_leaf_root_node((std::min)(kNodeValues, 2 * max_count)); + iter.node->swap(root(), mutable_allocator()); + delete_leaf_node(root()); + mutable_root() = iter.node; + rightmost_ = iter.node; + } else { + rebalance_or_split(&iter); + } + } + iter.node->emplace_value(iter.position, mutable_allocator(), + std::forward(args)...); + ++size_; + return iter; + } + + template + template + inline auto btree

::internal_locate(const K &key) const + -> SearchResult { + return internal_locate_impl(key, is_key_compare_to()); + } + + template + template + inline auto btree

::internal_locate_impl( + const K &key, std::false_type /* IsCompareTo */) const + -> SearchResult { + iterator iter(const_cast(root()), 0); + for (;;) { + iter.position = iter.node->lower_bound(key, key_comp()).value; + // NOTE: we don't need to walk all the way down the tree if the keys are + // equal, but determining equality would require doing an extra comparison + // on each node on the way down, and we will need to go all the way to the + // leaf node in the expected case. + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return {iter}; + } + + template + template + inline auto btree

::internal_locate_impl( + const K &key, std::true_type /* IsCompareTo */) const + -> SearchResult { + iterator iter(const_cast(root()), 0); + for (;;) { + SearchResult res = iter.node->lower_bound(key, key_comp()); + iter.position = res.value; + if (res.match == MatchKind::kEq) { + return {iter, MatchKind::kEq}; + } + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return {iter, MatchKind::kNe}; + } + + template + template + auto btree

::internal_lower_bound(const K &key) const -> iterator { + iterator iter(const_cast(root()), 0); + for (;;) { + iter.position = iter.node->lower_bound(key, key_comp()).value; + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return internal_last(iter); + } + + template + template + auto btree

::internal_upper_bound(const K &key) const -> iterator { + iterator iter(const_cast(root()), 0); + for (;;) { + iter.position = iter.node->upper_bound(key, key_comp()); + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return internal_last(iter); + } + + template + template + auto btree

::internal_find(const K &key) const -> iterator { + auto res = internal_locate(key); + if (res.HasMatch()) { + if (res.IsEq()) { + return res.value; + } + } else { + const iterator iter = internal_last(res.value); + if (iter.node != nullptr && !compare_keys(key, iter.key())) { + return iter; + } + } + return {nullptr, 0}; + } + + template + void btree

::internal_clear(node_type *node) { + if (!node->leaf()) { + for (int i = 0; i <= node->count(); ++i) { + internal_clear(node->child(i)); + } + delete_internal_node(node); + } else { + delete_leaf_node(node); + } + } + + template + int btree

::internal_verify( + const node_type *node, const key_type *lo, const key_type *hi) const { + assert(node->count() > 0); + assert(node->count() <= node->max_count()); + if (lo) { + assert(!compare_keys(node->key(0), *lo)); + } + if (hi) { + assert(!compare_keys(*hi, node->key(node->count() - 1))); + } + for (int i = 1; i < node->count(); ++i) { + assert(!compare_keys(node->key(i), node->key(i - 1))); + } + int count = node->count(); + if (!node->leaf()) { + for (int i = 0; i <= node->count(); ++i) { + assert(node->child(i) != nullptr); + assert(node->child(i)->parent() == node); + assert(node->child(i)->position() == i); + count += internal_verify( + node->child(i), + (i == 0) ? lo : &node->key(i - 1), + (i == node->count()) ? hi : &node->key(i)); + } + } + return count; + } + + // A common base class for btree_set, btree_map, btree_multiset, and btree_multimap. + // --------------------------------------------------------------------------------- + template + class btree_container { + using params_type = typename Tree::params_type; + + protected: + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + template + using key_arg = + typename KeyArg::value>:: + template type; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using difference_type = typename Tree::difference_type; + using key_compare = typename Tree::key_compare; + using value_compare = typename Tree::value_compare; + using allocator_type = typename Tree::allocator_type; + using reference = typename Tree::reference; + using const_reference = typename Tree::const_reference; + using pointer = typename Tree::pointer; + using const_pointer = typename Tree::const_pointer; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using reverse_iterator = typename Tree::reverse_iterator; + using const_reverse_iterator = typename Tree::const_reverse_iterator; + using node_type = typename Tree::node_handle_type; + + // Constructors/assignments. + btree_container() : tree_(key_compare(), allocator_type()) {} + explicit btree_container(const key_compare &comp, + const allocator_type &alloc = allocator_type()) + : tree_(comp, alloc) {} + btree_container(const btree_container &x) = default; + btree_container(btree_container &&x) noexcept = default; + btree_container &operator=(const btree_container &x) = default; + btree_container &operator=(btree_container &&x) noexcept( + std::is_nothrow_move_assignable::value) = default; + + // Iterator routines. + iterator begin() { return tree_.begin(); } + const_iterator begin() const { return tree_.begin(); } + const_iterator cbegin() const { return tree_.begin(); } + iterator end() { return tree_.end(); } + const_iterator end() const { return tree_.end(); } + const_iterator cend() const { return tree_.end(); } + reverse_iterator rbegin() { return tree_.rbegin(); } + const_reverse_iterator rbegin() const { return tree_.rbegin(); } + const_reverse_iterator crbegin() const { return tree_.rbegin(); } + reverse_iterator rend() { return tree_.rend(); } + const_reverse_iterator rend() const { return tree_.rend(); } + const_reverse_iterator crend() const { return tree_.rend(); } + + // Lookup routines. + template + iterator find(const key_arg &key) { + return tree_.find(key); + } + template + const_iterator find(const key_arg &key) const { return tree_.find(key); } + + template + bool contains(const key_arg &key) const { return find(key) != end(); } + + template + iterator lower_bound(const key_arg &key) { return tree_.lower_bound(key); } + + template + const_iterator lower_bound(const key_arg &key) const { return tree_.lower_bound(key); } + + template + iterator upper_bound(const key_arg &key) { return tree_.upper_bound(key); } + + template + const_iterator upper_bound(const key_arg &key) const { return tree_.upper_bound(key); } + + template + std::pair equal_range(const key_arg &key) { return tree_.equal_range(key); } + + template + std::pair equal_range( + const key_arg &key) const { + return tree_.equal_range(key); + } + + iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); } + iterator erase(iterator iter) { return tree_.erase(iter); } + iterator erase(const_iterator first, const_iterator last) { + return tree_.erase(iterator(first), iterator(last)).second; + } + + node_type extract(iterator position) { + // Use Move instead of Transfer, because the rebalancing code expects to + // have a valid object to scribble metadata bits on top of. + auto node = CommonAccess::Move(get_allocator(), position.slot()); + erase(position); + return node; + } + + node_type extract(const_iterator position) { + return extract(iterator(position)); + } + + public: + void clear() { tree_.clear(); } + void swap(btree_container &x) { tree_.swap(x.tree_); } + void verify() const { tree_.verify(); } + + size_type size() const { return tree_.size(); } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { return tree_.empty(); } + + friend bool operator==(const btree_container &x, const btree_container &y) { + if (x.size() != y.size()) return false; + return std::equal(x.begin(), x.end(), y.begin()); + } + + friend bool operator!=(const btree_container &x, const btree_container &y) { return !(x == y); } + + friend bool operator<(const btree_container &x, const btree_container &y) { + return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); + } + + friend bool operator>(const btree_container &x, const btree_container &y) { return y < x; } + + friend bool operator<=(const btree_container &x, const btree_container &y) { return !(y < x); } + + friend bool operator>=(const btree_container &x, const btree_container &y) { return !(x < y); } + + // The allocator used by the btree. + allocator_type get_allocator() const { return tree_.get_allocator(); } + + // The key comparator used by the btree. + key_compare key_comp() const { return tree_.key_comp(); } + value_compare value_comp() const { return tree_.value_comp(); } + + // Support absl::Hash. + template + friend State AbslHashValue(State h, const btree_container &b) { + for (const auto &v : b) { + h = State::combine(std::move(h), v); + } + return State::combine(std::move(h), b.size()); + } + + protected: + Tree tree_; + }; + + // A common base class for btree_set and btree_map. + // ----------------------------------------------- + template + class btree_set_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; + + protected: + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + using insert_return_type = InsertReturnType; + using super_type::super_type; + btree_set_container() {} + + template + btree_set_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + btree_set_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_set_container(init.begin(), init.end(), comp, alloc) {} + + // Lookup routines. + template + size_type count(const key_arg &key) const { + return this->tree_.count_unique(key); + } + + // Insertion routines. + std::pair insert(const value_type &x) { + return this->tree_.insert_unique(params_type::key(x), x); + } + std::pair insert(value_type &&x) { + return this->tree_.insert_unique(params_type::key(x), std::move(x)); + } + template + std::pair emplace(Args &&... args) { + init_type v(std::forward(args)...); + return this->tree_.insert_unique(params_type::key(v), std::move(v)); + } + iterator insert(const_iterator position, const value_type &x) { + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(x), x) + .first; + } + iterator insert(const_iterator position, value_type &&x) { + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(x), + std::move(x)) + .first; + } + + template + iterator emplace_hint(const_iterator position, Args &&... args) { + init_type v(std::forward(args)...); + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(v), + std::move(v)) + .first; + } + + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_unique(b, e); + } + + void insert(std::initializer_list init) { + this->tree_.insert_iterator_unique(init.begin(), init.end()); + } + + insert_return_type insert(node_type &&node) { + if (!node) return {this->end(), false, node_type()}; + std::pair res = + this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) { + CommonAccess::Destroy(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + std::pair res = this->tree_.insert_hint_unique( + iterator(hint), params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) CommonAccess::Destroy(&node); + return res.first; + } + + template + size_type erase(const key_arg &key) { return this->tree_.erase_unique(key); } + using super_type::erase; + + template + node_type extract(const key_arg &key) { + auto it = this->find(key); + return it == this->end() ? node_type() : extract(it); + } + + using super_type::extract; + + // Merge routines. + // Moves elements from `src` into `this`. If the element already exists in + // `this`, it is left unmodified in `src`. + template < + typename T, + typename phmap::enable_if_t< + phmap::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + for (auto src_it = src.begin(); src_it != src.end();) { + if (insert(std::move(*src_it)).second) { + src_it = src.erase(src_it); + } else { + ++src_it; + } + } + } + + template < + typename T, + typename phmap::enable_if_t< + phmap::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } + }; + + // Base class for btree_map. + // ------------------------- + template + class btree_map_container : public btree_set_container { + using super_type = btree_set_container; + using params_type = typename Tree::params_type; + + protected: + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using mapped_type = typename params_type::mapped_type; + using value_type = typename Tree::value_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + + // Inherit constructors. + using super_type::super_type; + btree_map_container() {} + + // Insertion routines. + template + std::pair try_emplace(const key_type &k, Args &&... args) { + return this->tree_.insert_unique( + k, std::piecewise_construct, std::forward_as_tuple(k), + std::forward_as_tuple(std::forward(args)...)); + } + template + std::pair try_emplace(key_type &&k, Args &&... args) { + // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` + // and then using `k` unsequenced. This is safe because the move is into a + // forwarding reference and insert_unique guarantees that `key` is never + // referenced after consuming `args`. + const key_type& key_ref = k; + return this->tree_.insert_unique( + key_ref, std::piecewise_construct, std::forward_as_tuple(std::move(k)), + std::forward_as_tuple(std::forward(args)...)); + } + template + iterator try_emplace(const_iterator hint, const key_type &k, + Args &&... args) { + return this->tree_ + .insert_hint_unique(iterator(hint), k, std::piecewise_construct, + std::forward_as_tuple(k), + std::forward_as_tuple(std::forward(args)...)) + .first; + } + template + iterator try_emplace(const_iterator hint, key_type &&k, Args &&... args) { + // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` + // and then using `k` unsequenced. This is safe because the move is into a + // forwarding reference and insert_hint_unique guarantees that `key` is + // never referenced after consuming `args`. + const key_type& key_ref = k; + return this->tree_ + .insert_hint_unique(iterator(hint), key_ref, std::piecewise_construct, + std::forward_as_tuple(std::move(k)), + std::forward_as_tuple(std::forward(args)...)) + .first; + } + mapped_type &operator[](const key_type &k) { + return try_emplace(k).first->second; + } + mapped_type &operator[](key_type &&k) { + return try_emplace(std::move(k)).first->second; + } + + template + mapped_type &at(const key_arg &key) { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("phmap::btree_map::at"); + return it->second; + } + template + const mapped_type &at(const key_arg &key) const { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("phmap::btree_map::at"); + return it->second; + } + }; + + // A common base class for btree_multiset and btree_multimap. + template + class btree_multiset_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + + // Inherit constructors. + using super_type::super_type; + btree_multiset_container() {} + + // Range constructor. + template + btree_multiset_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + // Initializer list constructor. + btree_multiset_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} + + // Lookup routines. + template + size_type count(const key_arg &key) const { + return this->tree_.count_multi(key); + } + + // Insertion routines. + iterator insert(const value_type &x) { return this->tree_.insert_multi(x); } + iterator insert(value_type &&x) { + return this->tree_.insert_multi(std::move(x)); + } + iterator insert(const_iterator position, const value_type &x) { + return this->tree_.insert_hint_multi(iterator(position), x); + } + iterator insert(const_iterator position, value_type &&x) { + return this->tree_.insert_hint_multi(iterator(position), std::move(x)); + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_multi(b, e); + } + void insert(std::initializer_list init) { + this->tree_.insert_iterator_multi(init.begin(), init.end()); + } + template + iterator emplace(Args &&... args) { + return this->tree_.insert_multi(init_type(std::forward(args)...)); + } + template + iterator emplace_hint(const_iterator position, Args &&... args) { + return this->tree_.insert_hint_multi( + iterator(position), init_type(std::forward(args)...)); + } + iterator insert(node_type &&node) { + if (!node) return this->end(); + iterator res = + this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + CommonAccess::Destroy(&node); + return res; + } + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + iterator res = this->tree_.insert_hint_multi( + iterator(hint), + std::move(params_type::element(CommonAccess::GetSlot(node)))); + CommonAccess::Destroy(&node); + return res; + } + + // Deletion routines. + template + size_type erase(const key_arg &key) { + return this->tree_.erase_multi(key); + } + using super_type::erase; + + // Node extraction routines. + template + node_type extract(const key_arg &key) { + auto it = this->find(key); + return it == this->end() ? node_type() : extract(it); + } + using super_type::extract; + + // Merge routines. + // Moves all elements from `src` into `this`. + template < + typename T, + typename phmap::enable_if_t< + phmap::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + insert(std::make_move_iterator(src.begin()), + std::make_move_iterator(src.end())); + src.clear(); + } + + template < + typename T, + typename phmap::enable_if_t< + phmap::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } + }; + + // A base class for btree_multimap. + template + class btree_multimap_container : public btree_multiset_container { + using super_type = btree_multiset_container; + using params_type = typename Tree::params_type; + + public: + using mapped_type = typename params_type::mapped_type; + + // Inherit constructors. + using super_type::super_type; + btree_multimap_container() {} + }; + +} // namespace priv + + + + // ---------------------------------------------------------------------- + // btree_set - default values in phmap_fwd_decl.h + // ---------------------------------------------------------------------- + template + class btree_set : public priv::btree_set_container< + priv::btree>> + { + using Base = typename btree_set::btree_set_container; + + public: + btree_set() {} + using Base::Base; + using Base::begin; + using Base::cbegin; + using Base::end; + using Base::cend; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::get_allocator; + using Base::key_comp; + using Base::value_comp; + }; + + // Swaps the contents of two `phmap::btree_set` containers. + // ------------------------------------------------------- + template + void swap(btree_set &x, btree_set &y) { + return x.swap(y); + } + + // Erases all elements that satisfy the predicate pred from the container. + // ---------------------------------------------------------------------- + template + void erase_if(btree_set &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } + } + + // ---------------------------------------------------------------------- + // btree_multiset - default values in phmap_fwd_decl.h + // ---------------------------------------------------------------------- + template + class btree_multiset : public priv::btree_multiset_container< + priv::btree>> + { + using Base = typename btree_multiset::btree_multiset_container; + + public: + btree_multiset() {} + using Base::Base; + using Base::begin; + using Base::cbegin; + using Base::end; + using Base::cend; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::get_allocator; + using Base::key_comp; + using Base::value_comp; + }; + + // Swaps the contents of two `phmap::btree_multiset` containers. + // ------------------------------------------------------------ + template + void swap(btree_multiset &x, btree_multiset &y) { + return x.swap(y); + } + + // Erases all elements that satisfy the predicate pred from the container. + // ---------------------------------------------------------------------- + template + void erase_if(btree_multiset &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } + } + + + // ---------------------------------------------------------------------- + // btree_map - default values in phmap_fwd_decl.h + // ---------------------------------------------------------------------- + template + class btree_map : public priv::btree_map_container< + priv::btree>> + { + using Base = typename btree_map::btree_map_container; + + public: + btree_map() {} + using Base::Base; + using Base::begin; + using Base::cbegin; + using Base::end; + using Base::cend; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::try_emplace; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::at; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::operator[]; + using Base::get_allocator; + using Base::key_comp; + using Base::value_comp; + }; + + // Swaps the contents of two `phmap::btree_map` containers. + // ------------------------------------------------------- + template + void swap(btree_map &x, btree_map &y) { + return x.swap(y); + } + + // ---------------------------------------------------------------------- + template + void erase_if(btree_map &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } + } + + // ---------------------------------------------------------------------- + // btree_multimap - default values in phmap_fwd_decl.h + // ---------------------------------------------------------------------- + template + class btree_multimap : public priv::btree_multimap_container< + priv::btree>> + { + using Base = typename btree_multimap::btree_multimap_container; + + public: + btree_multimap() {} + using Base::Base; + using Base::begin; + using Base::cbegin; + using Base::end; + using Base::cend; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::get_allocator; + using Base::key_comp; + using Base::value_comp; + }; + + // Swaps the contents of two `phmap::btree_multimap` containers. + // ------------------------------------------------------------ + template + void swap(btree_multimap &x, btree_multimap &y) { + return x.swap(y); + } + + // Erases all elements that satisfy the predicate pred from the container. + // ---------------------------------------------------------------------- + template + void erase_if(btree_multimap &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } + } + + +} // namespace btree + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + +#endif // PHMAP_BTREE_BTREE_CONTAINER_H_ diff --git a/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/conanfile.py b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/conanfile.py new file mode 100644 index 00000000000..c046377d13b --- /dev/null +++ b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/conanfile.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from conans import ConanFile, tools +import os + +class SparseppConan(ConanFile): + name = "parallel_hashmap" + version = "1.27" + description = "A header-only, very fast and memory-friendly hash map" + + # Indicates License type of the packaged library + license = "https://github.com/greg7mdp/parallel-hashmap/blob/master/LICENSE" + + # Packages the license for the conanfile.py + exports = ["LICENSE"] + + # Custom attributes for Bincrafters recipe conventions + source_subfolder = "source_subfolder" + + def source(self): + source_url = "https://github.com/greg7mdp/parallel-hashmap" + tools.get("{0}/archive/{1}.tar.gz".format(source_url, self.version)) + extracted_dir = self.name + "-" + self.version + + #Rename to "source_folder" is a convention to simplify later steps + os.rename(extracted_dir, self.source_subfolder) + + + def package(self): + include_folder = os.path.join(self.source_subfolder, "parallel_hashmap") + self.copy(pattern="LICENSE") + self.copy(pattern="*", dst="include/parallel_hashmap", src=include_folder) + + def package_id(self): + self.info.header_only() diff --git a/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/meminfo.h b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/meminfo.h new file mode 100644 index 00000000000..872f3c6982f --- /dev/null +++ b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/meminfo.h @@ -0,0 +1,195 @@ +#if !defined(spp_memory_h_guard) +#define spp_memory_h_guard + +#include +#include +#include + +#if defined(_WIN32) || defined( __CYGWIN__) + #define SPP_WIN +#endif + +#ifdef SPP_WIN + #include + #include + #undef min + #undef max +#elif defined(__linux__) + #include + #include +#elif defined(__FreeBSD__) + #include + #include + #include + #include + #include + #include +#endif + +namespace spp +{ + uint64_t GetSystemMemory(); + uint64_t GetTotalMemoryUsed(); + uint64_t GetProcessMemoryUsed(); + uint64_t GetPhysicalMemory(); + + uint64_t GetSystemMemory() + { +#ifdef SPP_WIN + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPageFile); +#elif defined(__linux__) + struct sysinfo memInfo; + sysinfo (&memInfo); + auto totalVirtualMem = memInfo.totalram; + + totalVirtualMem += memInfo.totalswap; + totalVirtualMem *= memInfo.mem_unit; + return static_cast(totalVirtualMem); +#elif defined(__FreeBSD__) + kvm_t *kd; + u_int pageCnt; + size_t pageCntLen = sizeof(pageCnt); + u_int pageSize; + struct kvm_swap kswap; + uint64_t totalVirtualMem; + + pageSize = static_cast(getpagesize()); + + sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); + totalVirtualMem = pageCnt * pageSize; + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); + kvm_getswapinfo(kd, &kswap, 1, 0); + kvm_close(kd); + totalVirtualMem += kswap.ksw_total * pageSize; + + return totalVirtualMem; +#else + return 0; +#endif + } + + uint64_t GetTotalMemoryUsed() + { +#ifdef SPP_WIN + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile); +#elif defined(__linux__) + struct sysinfo memInfo; + sysinfo(&memInfo); + auto virtualMemUsed = memInfo.totalram - memInfo.freeram; + + virtualMemUsed += memInfo.totalswap - memInfo.freeswap; + virtualMemUsed *= memInfo.mem_unit; + + return static_cast(virtualMemUsed); +#elif defined(__FreeBSD__) + kvm_t *kd; + u_int pageSize; + u_int pageCnt, freeCnt; + size_t pageCntLen = sizeof(pageCnt); + size_t freeCntLen = sizeof(freeCnt); + struct kvm_swap kswap; + uint64_t virtualMemUsed; + + pageSize = static_cast(getpagesize()); + + sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); + sysctlbyname("vm.stats.vm.v_free_count", &freeCnt, &freeCntLen, NULL, 0); + virtualMemUsed = (pageCnt - freeCnt) * pageSize; + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); + kvm_getswapinfo(kd, &kswap, 1, 0); + kvm_close(kd); + virtualMemUsed += kswap.ksw_used * pageSize; + + return virtualMemUsed; +#else + return 0; +#endif + } + + uint64_t GetProcessMemoryUsed() + { +#ifdef SPP_WIN + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc)); + return static_cast(pmc.PrivateUsage); +#elif defined(__linux__) + auto parseLine = + [](char* line)->int + { + auto i = strlen(line); + + while(*line < '0' || *line > '9') + { + line++; + } + + line[i-3] = '\0'; + i = atoi(line); + return i; + }; + + auto file = fopen("/proc/self/status", "r"); + auto result = -1; + char line[128]; + + while(fgets(line, 128, file) != nullptr) + { + if(strncmp(line, "VmSize:", 7) == 0) + { + result = parseLine(line); + break; + } + } + + fclose(file); + return static_cast(result) * 1024; +#elif defined(__FreeBSD__) + struct kinfo_proc info; + size_t infoLen = sizeof(info); + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; + + sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &infoLen, NULL, 0); + return static_cast(info.ki_rssize * getpagesize()); +#else + return 0; +#endif + } + + uint64_t GetPhysicalMemory() + { +#ifdef SPP_WIN + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPhys); +#elif defined(__linux__) + struct sysinfo memInfo; + sysinfo(&memInfo); + + auto totalPhysMem = memInfo.totalram; + + totalPhysMem *= memInfo.mem_unit; + return static_cast(totalPhysMem); +#elif defined(__FreeBSD__) + u_long physMem; + size_t physMemLen = sizeof(physMem); + int mib[] = { CTL_HW, HW_PHYSMEM }; + + sysctl(mib, sizeof(mib) / sizeof(*mib), &physMem, &physMemLen, NULL, 0); + return physMem; +#else + return 0; +#endif + } + +} + +#endif // spp_memory_h_guard diff --git a/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap.h b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap.h new file mode 100644 index 00000000000..22b3cc887e9 --- /dev/null +++ b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap.h @@ -0,0 +1,4544 @@ +#if !defined(phmap_h_guard_) +#define phmap_h_guard_ + +// --------------------------------------------------------------------------- +// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp) +// with modifications. +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// --------------------------------------------------------------------------- + +#ifdef _MSC_VER + #pragma warning(push) + + #pragma warning(disable : 4127) // conditional expression is constant + #pragma warning(disable : 4324) // structure was padded due to alignment specifier + #pragma warning(disable : 4514) // unreferenced inline function has been removed + #pragma warning(disable : 4623) // default constructor was implicitly defined as deleted + #pragma warning(disable : 4625) // copy constructor was implicitly defined as deleted + #pragma warning(disable : 4626) // assignment operator was implicitly defined as deleted + #pragma warning(disable : 4710) // function not inlined + #pragma warning(disable : 4711) // selected for automatic inline expansion + #pragma warning(disable : 4820) // '6' bytes padding added after data member + #pragma warning(disable : 4868) // compiler may not enforce left-to-right evaluation order in braced initializer list + #pragma warning(disable : 5027) // move assignment operator was implicitly defined as deleted + #pragma warning(disable : 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phmap_fwd_decl.h" +#include "phmap_utils.h" +#include "phmap_base.h" + +#if PHMAP_HAVE_STD_STRING_VIEW + #include +#endif + +namespace phmap { + +namespace priv { + +// -------------------------------------------------------------------------- +template +class probe_seq +{ +public: + probe_seq(size_t hashval, size_t mask) { + assert(((mask + 1) & mask) == 0 && "not a mask"); + mask_ = mask; + offset_ = hashval & mask_; + } + size_t offset() const { return offset_; } + size_t offset(size_t i) const { return (offset_ + i) & mask_; } + + void next() { + index_ += Width; + offset_ += index_; + offset_ &= mask_; + } + // 0-based probe index. The i-th probe in the probe sequence. + size_t getindex() const { return index_; } + +private: + size_t mask_; + size_t offset_; + size_t index_ = 0; +}; + +// -------------------------------------------------------------------------- +template +struct RequireUsableKey +{ + template + std::pair< + decltype(std::declval()(std::declval())), + decltype(std::declval()(std::declval(), + std::declval()))>* + operator()(const PassedKey&, const Args&...) const; +}; + +// -------------------------------------------------------------------------- +template +struct IsDecomposable : std::false_type {}; + +template +struct IsDecomposable< + phmap::void_t(), + std::declval()...))>, + Policy, Hash, Eq, Ts...> : std::true_type {}; + +// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. +// -------------------------------------------------------------------------- +template +constexpr bool IsNoThrowSwappable() { + using std::swap; + return noexcept(swap(std::declval(), std::declval())); +} + +// -------------------------------------------------------------------------- +template +int TrailingZeros(T x) { + PHMAP_IF_CONSTEXPR(sizeof(T) == 8) + return base_internal::CountTrailingZerosNonZero64(static_cast(x)); + else + return base_internal::CountTrailingZerosNonZero32(static_cast(x)); +} + +// -------------------------------------------------------------------------- +template +int LeadingZeros(T x) { + PHMAP_IF_CONSTEXPR(sizeof(T) == 8) + return base_internal::CountLeadingZeros64(static_cast(x)); + else + return base_internal::CountLeadingZeros32(static_cast(x)); +} + +// -------------------------------------------------------------------------- +// An abstraction over a bitmask. It provides an easy way to iterate through the +// indexes of the set bits of a bitmask. When Shift=0 (platforms with SSE), +// this is a true bitmask. On non-SSE, platforms the arithematic used to +// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as +// either 0x00 or 0x80. +// +// For example: +// for (int i : BitMask(0x5)) -> yields 0, 2 +// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +// -------------------------------------------------------------------------- +template +class BitMask +{ + static_assert(std::is_unsigned::value, ""); + static_assert(Shift == 0 || Shift == 3, ""); + +public: + // These are useful for unit tests (gunit). + using value_type = int; + using iterator = BitMask; + using const_iterator = BitMask; + + explicit BitMask(T mask) : mask_(mask) {} + BitMask& operator++() { + mask_ &= (mask_ - 1); + return *this; + } + explicit operator bool() const { return mask_ != 0; } + int operator*() const { return LowestBitSet(); } + int LowestBitSet() const { + return priv::TrailingZeros(mask_) >> Shift; + } + int HighestBitSet() const { + return (sizeof(T) * CHAR_BIT - priv::LeadingZeros(mask_) - + 1) >> + Shift; + } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + + int TrailingZeros() const { + return priv::TrailingZeros(mask_) >> Shift; + } + + int LeadingZeros() const { + constexpr int total_significant_bits = SignificantBits << Shift; + constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; + return priv::LeadingZeros(mask_ << extra_bits) >> Shift; + } + +private: + friend bool operator==(const BitMask& a, const BitMask& b) { + return a.mask_ == b.mask_; + } + friend bool operator!=(const BitMask& a, const BitMask& b) { + return a.mask_ != b.mask_; + } + + T mask_; +}; + +// -------------------------------------------------------------------------- +using ctrl_t = signed char; +using h2_t = uint8_t; + +// -------------------------------------------------------------------------- +// The values here are selected for maximum performance. See the static asserts +// below for details. +// -------------------------------------------------------------------------- +enum Ctrl : ctrl_t +{ + kEmpty = -128, // 0b10000000 + kDeleted = -2, // 0b11111110 + kSentinel = -1, // 0b11111111 +}; + +static_assert( + kEmpty & kDeleted & kSentinel & 0x80, + "Special markers need to have the MSB to make checking for them efficient"); +static_assert(kEmpty < kSentinel && kDeleted < kSentinel, + "kEmpty and kDeleted must be smaller than kSentinel to make the " + "SIMD test of IsEmptyOrDeleted() efficient"); +static_assert(kSentinel == -1, + "kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(kEmpty == -128, + "kEmpty must be -128 to make the SIMD check for its " + "existence efficient (psignb xmm, xmm)"); +static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F, + "kEmpty and kDeleted must share an unset bit that is not shared " + "by kSentinel to make the scalar test for MatchEmptyOrDeleted() " + "efficient"); +static_assert(kDeleted == -2, + "kDeleted must be -2 to make the implementation of " + "ConvertSpecialToEmptyAndFullToDeleted efficient"); + +// -------------------------------------------------------------------------- +// A single block of empty control bytes for tables without any slots allocated. +// This enables removing a branch in the hot path of find(). +// -------------------------------------------------------------------------- +inline ctrl_t* EmptyGroup() { + alignas(16) static constexpr ctrl_t empty_group[] = { + kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, + kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty}; + return const_cast(empty_group); +} + +// -------------------------------------------------------------------------- +inline size_t HashSeed(const ctrl_t* ctrl) { + // The low bits of the pointer have little or no entropy because of + // alignment. We shift the pointer to try to use higher entropy bits. A + // good number seems to be 12 bits, because that aligns with page size. + return reinterpret_cast(ctrl) >> 12; +} + +#ifdef PHMAP_NON_DETERMINISTIC + +inline size_t H1(size_t hashval, const ctrl_t* ctrl) { + // use ctrl_ pointer to add entropy to ensure + // non-deterministic iteration order. + return (hashval >> 7) ^ HashSeed(ctrl); +} + +#else + +inline size_t H1(size_t hashval, const ctrl_t* ) { + return (hashval >> 7); +} + +#endif + + +inline ctrl_t H2(size_t hashval) { return (ctrl_t)(hashval & 0x7F); } + +inline bool IsEmpty(ctrl_t c) { return c == kEmpty; } +inline bool IsFull(ctrl_t c) { return c >= 0; } +inline bool IsDeleted(ctrl_t c) { return c == kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; } + +#if PHMAP_HAVE_SSE2 + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4365) // conversion from 'int' to 'T', signed/unsigned mismatch +#endif + +// -------------------------------------------------------------------------- +// https://github.com/abseil/abseil-cpp/issues/209 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853 +// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char +// Work around this by using the portable implementation of Group +// when using -funsigned-char under GCC. +// -------------------------------------------------------------------------- +inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) { +#if defined(__GNUC__) && !defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Woverflow" + + if (std::is_unsigned::value) { + const __m128i mask = _mm_set1_epi8(static_cast(0x80)); + const __m128i diff = _mm_subs_epi8(b, a); + return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask); + } + + #pragma GCC diagnostic pop +#endif + return _mm_cmpgt_epi8(a, b); +} + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +struct GroupSse2Impl +{ + enum { kWidth = 16 }; // the number of slots per group + + explicit GroupSse2Impl(const ctrl_t* pos) { + ctrl = _mm_loadu_si128(reinterpret_cast(pos)); + } + + // Returns a bitmask representing the positions of slots that match hash. + // ---------------------------------------------------------------------- + BitMask Match(h2_t hash) const { + auto match = _mm_set1_epi8((char)hash); + return BitMask( + _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))); + } + + // Returns a bitmask representing the positions of empty slots. + // ------------------------------------------------------------ + BitMask MatchEmpty() const { +#if PHMAP_HAVE_SSSE3 + // This only works because kEmpty is -128. + return BitMask( + _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); +#else + return Match(static_cast(kEmpty)); +#endif + } + + // Returns a bitmask representing the positions of empty or deleted slots. + // ----------------------------------------------------------------------- + BitMask MatchEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return BitMask( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))); + } + + // Returns the number of trailing empty or deleted elements in the group. + // ---------------------------------------------------------------------- + uint32_t CountLeadingEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return TrailingZeros( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1); + } + + // ---------------------------------------------------------------------- + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + auto msbs = _mm_set1_epi8(static_cast(-128)); + auto x126 = _mm_set1_epi8(126); +#if PHMAP_HAVE_SSSE3 + auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs); +#else + auto zero = _mm_setzero_si128(); + auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl); + auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126)); +#endif + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res); + } + + __m128i ctrl; +}; + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif // PHMAP_HAVE_SSE2 + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +struct GroupPortableImpl +{ + enum { kWidth = 8 }; + + explicit GroupPortableImpl(const ctrl_t* pos) + : ctrl(little_endian::Load64(pos)) {} + + BitMask Match(h2_t hash) const { + // For the technique, see: + // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord + // (Determine if a word has a byte equal to n). + // + // Caveat: there are false positives but: + // - they only occur if there is a real match + // - they never occur on kEmpty, kDeleted, kSentinel + // - they will be handled gracefully by subsequent checks in code + // + // Example: + // v = 0x1716151413121110 + // hash = 0x12 + // retval = (v - lsbs) & ~v & msbs = 0x0000000080800000 + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl ^ (lsbs * hash); + return BitMask((x - lsbs) & ~x & msbs); + } + + BitMask MatchEmpty() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 6)) & msbs); + } + + BitMask MatchEmptyOrDeleted() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 7)) & msbs); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; + return (uint32_t)((TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3); + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl & msbs; + auto res = (~x + (x >> 7)) & ~lsbs; + little_endian::Store64(dst, res); + } + + uint64_t ctrl; +}; + +#if PHMAP_HAVE_SSE2 + using Group = GroupSse2Impl; +#else + using Group = GroupPortableImpl; +#endif + +template +class raw_hash_set; + +inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } + +// -------------------------------------------------------------------------- +// PRECONDITION: +// IsValidCapacity(capacity) +// ctrl[capacity] == kSentinel +// ctrl[i] != kSentinel for all i < capacity +// Applies mapping for every byte in ctrl: +// DELETED -> EMPTY +// EMPTY -> EMPTY +// FULL -> DELETED +// -------------------------------------------------------------------------- +inline void ConvertDeletedToEmptyAndFullToDeleted( + ctrl_t* ctrl, size_t capacity) +{ + assert(ctrl[capacity] == kSentinel); + assert(IsValidCapacity(capacity)); + for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) { + Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); + } + // Copy the cloned ctrl bytes. + std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth); + ctrl[capacity] = kSentinel; +} + +// -------------------------------------------------------------------------- +// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1. +// -------------------------------------------------------------------------- +inline size_t NormalizeCapacity(size_t n) +{ + return n ? ~size_t{} >> LeadingZeros(n) : 1; +} + +// -------------------------------------------------------------------------- +// We use 7/8th as maximum load factor. +// For 16-wide groups, that gives an average of two empty slots per group. +// -------------------------------------------------------------------------- +inline size_t CapacityToGrowth(size_t capacity) +{ + assert(IsValidCapacity(capacity)); + // `capacity*7/8` + PHMAP_IF_CONSTEXPR (Group::kWidth == 8) { + if (capacity == 7) + { + // x-x/8 does not work when x==7. + return 6; + } + } + return capacity - capacity / 8; +} + +// -------------------------------------------------------------------------- +// From desired "growth" to a lowerbound of the necessary capacity. +// Might not be a valid one and required NormalizeCapacity(). +// -------------------------------------------------------------------------- +inline size_t GrowthToLowerboundCapacity(size_t growth) +{ + // `growth*8/7` + PHMAP_IF_CONSTEXPR (Group::kWidth == 8) { + if (growth == 7) + { + // x+(x-1)/7 does not work when x==7. + return 8; + } + } + return growth + static_cast((static_cast(growth) - 1) / 7); +} + +namespace hashtable_debug_internal { + +// If it is a map, call get<0>(). +using std::get; +template +auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) { + return get<0>(pair); +} + +// If it is not a map, return the value directly. +template +const typename T::key_type& GetKey(const typename T::key_type& key, char) { + return key; +} + +// -------------------------------------------------------------------------- +// Containers should specialize this to provide debug information for that +// container. +// -------------------------------------------------------------------------- +template +struct HashtableDebugAccess +{ + // Returns the number of probes required to find `key` in `c`. The "number of + // probes" is a concept that can vary by container. Implementations should + // return 0 when `key` was found in the minimum number of operations and + // should increment the result for each non-trivial operation required to find + // `key`. + // + // The default implementation uses the bucket api from the standard and thus + // works for `std::unordered_*` containers. + // -------------------------------------------------------------------------- + static size_t GetNumProbes(const Container& c, + const typename Container::key_type& key) { + if (!c.bucket_count()) return {}; + size_t num_probes = 0; + size_t bucket = c.bucket(key); + for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) { + if (it == e) return num_probes; + if (c.key_eq()(key, GetKey(*it, 0))) return num_probes; + } + } +}; + +} // namespace hashtable_debug_internal + +// ---------------------------------------------------------------------------- +// I N F O Z S T U B S +// ---------------------------------------------------------------------------- +struct HashtablezInfo +{ + void PrepareForSampling() {} +}; + +inline void RecordRehashSlow(HashtablezInfo*, size_t ) {} + +static inline void RecordInsertSlow(HashtablezInfo* , size_t, size_t ) {} + +static inline void RecordEraseSlow(HashtablezInfo*) {} + +static inline HashtablezInfo* SampleSlow(int64_t*) { return nullptr; } +static inline void UnsampleSlow(HashtablezInfo* ) {} + +class HashtablezInfoHandle +{ +public: + inline void RecordStorageChanged(size_t , size_t ) {} + inline void RecordRehash(size_t ) {} + inline void RecordInsert(size_t , size_t ) {} + inline void RecordErase() {} + friend inline void swap(HashtablezInfoHandle& , + HashtablezInfoHandle& ) noexcept {} +}; + +static inline HashtablezInfoHandle Sample() { return HashtablezInfoHandle(); } + +class HashtablezSampler +{ +public: + // Returns a global Sampler. + static HashtablezSampler& Global() { static HashtablezSampler hzs; return hzs; } + HashtablezInfo* Register() { static HashtablezInfo info; return &info; } + void Unregister(HashtablezInfo* ) {} + + using DisposeCallback = void (*)(const HashtablezInfo&); + DisposeCallback SetDisposeCallback(DisposeCallback ) { return nullptr; } + int64_t Iterate(const std::function& ) { return 0; } +}; + +static inline void SetHashtablezEnabled(bool ) {} +static inline void SetHashtablezSampleParameter(int32_t ) {} +static inline void SetHashtablezMaxSamples(int32_t ) {} + + +namespace memory_internal { + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +// ---------------------------------------------------------------------------- +template +void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, + phmap::index_sequence) { + phmap::allocator_traits::construct( + *alloc, ptr, std::get(std::forward(t))...); +} + +template +struct WithConstructedImplF { + template + decltype(std::declval()(std::declval())) operator()( + Args&&... args) const { + return std::forward(f)(T(std::forward(args)...)); + } + F&& f; +}; + +template +decltype(std::declval()(std::declval())) WithConstructedImpl( + Tuple&& t, phmap::index_sequence, F&& f) { + return WithConstructedImplF{std::forward(f)}( + std::get(std::forward(t))...); +} + +template +auto TupleRefImpl(T&& t, phmap::index_sequence) + -> decltype(std::forward_as_tuple(std::get(std::forward(t))...)) { + return std::forward_as_tuple(std::get(std::forward(t))...); +} + +// Returns a tuple of references to the elements of the input tuple. T must be a +// tuple. +// ---------------------------------------------------------------------------- +template +auto TupleRef(T&& t) -> decltype( + TupleRefImpl(std::forward(t), + phmap::make_index_sequence< + std::tuple_size::type>::value>())) { + return TupleRefImpl( + std::forward(t), + phmap::make_index_sequence< + std::tuple_size::type>::value>()); +} + +template +decltype(std::declval()(std::declval(), std::piecewise_construct, + std::declval>(), std::declval())) +DecomposePairImpl(F&& f, std::pair, V> p) { + const auto& key = std::get<0>(p.first); + return std::forward(f)(key, std::piecewise_construct, std::move(p.first), + std::move(p.second)); +} + +} // namespace memory_internal + + +// ---------------------------------------------------------------------------- +// R A W _ H A S H _ S E T +// ---------------------------------------------------------------------------- +// An open-addressing +// hashtable with quadratic probing. +// +// This is a low level hashtable on top of which different interfaces can be +// implemented, like flat_hash_set, node_hash_set, string_hash_set, etc. +// +// The table interface is similar to that of std::unordered_set. Notable +// differences are that most member functions support heterogeneous keys when +// BOTH the hash and eq functions are marked as transparent. They do so by +// providing a typedef called `is_transparent`. +// +// When heterogeneous lookup is enabled, functions that take key_type act as if +// they have an overload set like: +// +// iterator find(const key_type& key); +// template +// iterator find(const K& key); +// +// size_type erase(const key_type& key); +// template +// size_type erase(const K& key); +// +// std::pair equal_range(const key_type& key); +// template +// std::pair equal_range(const K& key); +// +// When heterogeneous lookup is disabled, only the explicit `key_type` overloads +// exist. +// +// find() also supports passing the hash explicitly: +// +// iterator find(const key_type& key, size_t hash); +// template +// iterator find(const U& key, size_t hash); +// +// In addition the pointer to element and iterator stability guarantees are +// weaker: all iterators and pointers are invalidated after a new element is +// inserted. +// +// IMPLEMENTATION DETAILS +// +// The table stores elements inline in a slot array. In addition to the slot +// array the table maintains some control state per slot. The extra state is one +// byte per slot and stores empty or deleted marks, or alternatively 7 bits from +// the hash of an occupied slot. The table is split into logical groups of +// slots, like so: +// +// Group 1 Group 2 Group 3 +// +---------------+---------------+---------------+ +// | | | | | | | | | | | | | | | | | | | | | | | | | +// +---------------+---------------+---------------+ +// +// On lookup the hash is split into two parts: +// - H2: 7 bits (those stored in the control bytes) +// - H1: the rest of the bits +// The groups are probed using H1. For each group the slots are matched to H2 in +// parallel. Because H2 is 7 bits (128 states) and the number of slots per group +// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit. +// +// On insert, once the right group is found (as in lookup), its slots are +// filled in order. +// +// On erase a slot is cleared. In case the group did not have any empty slots +// before the erase, the erased slot is marked as deleted. +// +// Groups without empty slots (but maybe with deleted slots) extend the probe +// sequence. The probing algorithm is quadratic. Given N the number of groups, +// the probing function for the i'th probe is: +// +// P(0) = H1 % N +// +// P(i) = (P(i - 1) + i) % N +// +// This probing function guarantees that after N probes, all the groups of the +// table will be probed exactly once. +// ---------------------------------------------------------------------------- +template +class raw_hash_set +{ + using PolicyTraits = hash_policy_traits; + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + +public: + using init_type = typename PolicyTraits::init_type; + using key_type = typename PolicyTraits::key_type; + // TODO(sbenza): Hide slot_type as it is an implementation detail. Needs user + // code fixes! + using slot_type = typename PolicyTraits::slot_type; + using allocator_type = Alloc; + using size_type = size_t; + using difference_type = ptrdiff_t; + using hasher = Hash; + using key_equal = Eq; + using policy_type = Policy; + using value_type = typename PolicyTraits::value_type; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename phmap::allocator_traits< + allocator_type>::template rebind_traits::pointer; + using const_pointer = typename phmap::allocator_traits< + allocator_type>::template rebind_traits::const_pointer; + + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + template + using key_arg = typename KeyArgImpl::template type; + +private: + // Give an early error when key_type is not hashable/eq. + auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k)); + auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k)); + + using Layout = phmap::priv::Layout; + + static Layout MakeLayout(size_t capacity) { + assert(IsValidCapacity(capacity)); + return Layout(capacity + Group::kWidth + 1, capacity); + } + + using AllocTraits = phmap::allocator_traits; + using SlotAlloc = typename phmap::allocator_traits< + allocator_type>::template rebind_alloc; + using SlotAllocTraits = typename phmap::allocator_traits< + allocator_type>::template rebind_traits; + + static_assert(std::is_lvalue_reference::value, + "Policy::element() must return a reference"); + + template + struct SameAsElementReference + : std::is_same::type>::type, + typename std::remove_cv< + typename std::remove_reference::type>::type> {}; + + // An enabler for insert(T&&): T must be convertible to init_type or be the + // same as [cv] value_type [ref]. + // Note: we separate SameAsElementReference into its own type to avoid using + // reference unless we need to. MSVC doesn't seem to like it in some + // cases. + template + using RequiresInsertable = typename std::enable_if< + phmap::disjunction, + SameAsElementReference>::value, + int>::type; + + // RequiresNotInit is a workaround for gcc prior to 7.1. + // See https://godbolt.org/g/Y4xsUh. + template + using RequiresNotInit = + typename std::enable_if::value, int>::type; + + template + using IsDecomposable = IsDecomposable; + +public: + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + + class iterator + { + friend class raw_hash_set; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename raw_hash_set::value_type; + using reference = + phmap::conditional_t; + using pointer = phmap::remove_reference_t*; + using difference_type = typename raw_hash_set::difference_type; + + iterator() {} + + // PRECONDITION: not an end() iterator. + reference operator*() const { return PolicyTraits::element(slot_); } + + // PRECONDITION: not an end() iterator. + pointer operator->() const { return &operator*(); } + + // PRECONDITION: not an end() iterator. + iterator& operator++() { + ++ctrl_; + ++slot_; + skip_empty_or_deleted(); + return *this; + } + // PRECONDITION: not an end() iterator. + iterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + +#if PHMAP_BIDIRECTIONAL + // PRECONDITION: not a begin() iterator. + iterator& operator--() { + assert(ctrl_); + do { + --ctrl_; + --slot_; + } while (IsEmptyOrDeleted(*ctrl_)); + return *this; + } + + // PRECONDITION: not a begin() iterator. + iterator operator--(int) { + auto tmp = *this; + --*this; + return tmp; + } +#endif + + friend bool operator==(const iterator& a, const iterator& b) { + return a.ctrl_ == b.ctrl_; + } + friend bool operator!=(const iterator& a, const iterator& b) { + return !(a == b); + } + + private: + iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end() + iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} + + void skip_empty_or_deleted() { + while (IsEmptyOrDeleted(*ctrl_)) { + // ctrl is not necessarily aligned to Group::kWidth. It is also likely + // to read past the space for ctrl bytes and into slots. This is ok + // because ctrl has sizeof() == 1 and slot has sizeof() >= 1 so there + // is no way to read outside the combined slot array. + uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); + ctrl_ += shift; + slot_ += shift; + } + } + + ctrl_t* ctrl_ = nullptr; + // To avoid uninitialized member warnings, put slot_ in an anonymous union. + // The member is not initialized on singleton and end iterators. + union { + slot_type* slot_; + }; + }; + + class const_iterator + { + friend class raw_hash_set; + + public: + using iterator_category = typename iterator::iterator_category; + using value_type = typename raw_hash_set::value_type; + using reference = typename raw_hash_set::const_reference; + using pointer = typename raw_hash_set::const_pointer; + using difference_type = typename raw_hash_set::difference_type; + + const_iterator() {} + // Implicit construction from iterator. + const_iterator(iterator i) : inner_(std::move(i)) {} + + reference operator*() const { return *inner_; } + pointer operator->() const { return inner_.operator->(); } + + const_iterator& operator++() { + ++inner_; + return *this; + } + const_iterator operator++(int) { return inner_++; } + + friend bool operator==(const const_iterator& a, const const_iterator& b) { + return a.inner_ == b.inner_; + } + friend bool operator!=(const const_iterator& a, const const_iterator& b) { + return !(a == b); + } + + private: + const_iterator(const ctrl_t* ctrl, const slot_type* slot) + : inner_(const_cast(ctrl), const_cast(slot)) {} + + iterator inner_; + }; + + using node_type = node_handle, Alloc>; + using insert_return_type = InsertReturnType; + + raw_hash_set() noexcept( + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value) {} + + explicit raw_hash_set(size_t bucket_count, const hasher& hashfn = hasher(), + const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : ctrl_(EmptyGroup()), settings_(0, hashfn, eq, alloc) { + if (bucket_count) { + capacity_ = NormalizeCapacity(bucket_count); + reset_growth_left(); + initialize_slots(); + } + } + + raw_hash_set(size_t bucket_count, const hasher& hashfn, + const allocator_type& alloc) + : raw_hash_set(bucket_count, hashfn, key_equal(), alloc) {} + + raw_hash_set(size_t bucket_count, const allocator_type& alloc) + : raw_hash_set(bucket_count, hasher(), key_equal(), alloc) {} + + explicit raw_hash_set(const allocator_type& alloc) + : raw_hash_set(0, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0, + const hasher& hashfn = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(bucket_count, hashfn, eq, alloc) { + insert(first, last); + } + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const hasher& hashfn, const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hashfn, key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, const allocator_type& alloc) + : raw_hash_set(first, last, 0, hasher(), key_equal(), alloc) {} + + // Instead of accepting std::initializer_list as the first + // argument like std::unordered_set does, we have two overloads + // that accept std::initializer_list and std::initializer_list. + // This is advantageous for performance. + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // std::unordered_set s = {"abc", "def"}; + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // phmap::flat_hash_set s = {"abc", "def"}; + // + // The same trick is used in insert(). + // + // The enabler is necessary to prevent this constructor from triggering where + // the copy constructor is meant to be called. + // + // phmap::flat_hash_set a, b{a}; + // + // RequiresNotInit is a workaround for gcc prior to 7.1. + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hashfn = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hashfn, eq, alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hashfn = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hashfn, eq, alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hashfn, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hashfn, key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hashfn, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hashfn, key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, + const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(const raw_hash_set& that) + : raw_hash_set(that, AllocTraits::select_on_container_copy_construction( + that.alloc_ref())) {} + + raw_hash_set(const raw_hash_set& that, const allocator_type& a) + : raw_hash_set(0, that.hash_ref(), that.eq_ref(), a) { + reserve(that.size()); + // Because the table is guaranteed to be empty, we can do something faster + // than a full `insert`. + for (const auto& v : that) { + const size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, v); + auto target = find_first_non_full(hashval); + set_ctrl(target.offset, H2(hashval)); + emplace_at(target.offset, v); + infoz_.RecordInsert(hashval, target.probe_length); + } + size_ = that.size(); + growth_left() -= that.size(); + } + + raw_hash_set(raw_hash_set&& that) noexcept( + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value) + : ctrl_(phmap::exchange(that.ctrl_, EmptyGroup())), + slots_(phmap::exchange(that.slots_, nullptr)), + size_(phmap::exchange(that.size_, 0)), + capacity_(phmap::exchange(that.capacity_, 0)), + infoz_(phmap::exchange(that.infoz_, HashtablezInfoHandle())), + // Hash, equality and allocator are copied instead of moved because + // `that` must be left valid. If Hash is std::function, moving it + // would create a nullptr functor that cannot be called. + settings_(that.settings_) { + // growth_left was copied above, reset the one from `that`. + that.growth_left() = 0; + } + + raw_hash_set(raw_hash_set&& that, const allocator_type& a) + : ctrl_(EmptyGroup()), + slots_(nullptr), + size_(0), + capacity_(0), + settings_(0, that.hash_ref(), that.eq_ref(), a) { + if (a == that.alloc_ref()) { + std::swap(ctrl_, that.ctrl_); + std::swap(slots_, that.slots_); + std::swap(size_, that.size_); + std::swap(capacity_, that.capacity_); + std::swap(growth_left(), that.growth_left()); + std::swap(infoz_, that.infoz_); + } else { + reserve(that.size()); + // Note: this will copy elements of dense_set and unordered_set instead of + // moving them. This can be fixed if it ever becomes an issue. + for (auto& elem : that) insert(std::move(elem)); + } + } + + raw_hash_set& operator=(const raw_hash_set& that) { + raw_hash_set tmp(that, + AllocTraits::propagate_on_container_copy_assignment::value + ? that.alloc_ref() + : alloc_ref()); + swap(tmp); + return *this; + } + + raw_hash_set& operator=(raw_hash_set&& that) noexcept( + phmap::allocator_traits::is_always_equal::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_assignable::value) { + // TODO(sbenza): We should only use the operations from the noexcept clause + // to make sure we actually adhere to that contract. + return move_assign( + std::move(that), + typename AllocTraits::propagate_on_container_move_assignment()); + } + + ~raw_hash_set() { destroy_slots(); } + + iterator begin() { + auto it = iterator_at(0); + it.skip_empty_or_deleted(); + return it; + } + iterator end() + { +#if PHMAP_BIDIRECTIONAL + return iterator_at(capacity_); +#else + return {ctrl_ + capacity_}; +#endif + } + + const_iterator begin() const { + return const_cast(this)->begin(); + } + const_iterator end() const { return const_cast(this)->end(); } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + bool empty() const { return !size(); } + size_t size() const { return size_; } + size_t capacity() const { return capacity_; } + size_t max_size() const { return (std::numeric_limits::max)(); } + + PHMAP_ATTRIBUTE_REINITIALIZES void clear() { + // Iterating over this container is O(bucket_count()). When bucket_count() + // is much greater than size(), iteration becomes prohibitively expensive. + // For clear() it is more important to reuse the allocated array when the + // container is small because allocation takes comparatively long time + // compared to destruction of the elements of the container. So we pick the + // largest bucket_count() threshold for which iteration is still fast and + // past that we simply deallocate the array. + if (empty()) + return; + if (capacity_ > 127) { + destroy_slots(); + } else if (capacity_) { + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + size_ = 0; + reset_ctrl(); + reset_growth_left(); + } + assert(empty()); + infoz_.RecordStorageChanged(0, capacity_); + } + + // This overload kicks in when the argument is an rvalue of insertable and + // decomposable type other than init_type. + // + // flat_hash_map m; + // m.insert(std::make_pair("abc", 42)); + template = 0, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + std::pair insert(T&& value) { + return emplace(std::forward(value)); + } + + // This overload kicks in when the argument is a bitfield or an lvalue of + // insertable and decomposable type. + // + // union { int n : 1; }; + // flat_hash_set s; + // s.insert(n); + // + // flat_hash_set s; + // const char* p = "hello"; + // s.insert(p); + // + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template = 0, + typename std::enable_if::value, int>::type = 0> + std::pair insert(const T& value) { + return emplace(value); + } + + // This overload kicks in when the argument is an rvalue of init_type. Its + // purpose is to handle brace-init-list arguments. + // + // flat_hash_set s; + // s.insert({"abc", 42}); + std::pair insert(init_type&& value) { + return emplace(std::move(value)); + } + + template = 0, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + iterator insert(const_iterator, T&& value) { + return insert(std::forward(value)).first; + } + + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template = 0, + typename std::enable_if::value, int>::type = 0> + iterator insert(const_iterator, const T& value) { + return insert(value).first; + } + + iterator insert(const_iterator, init_type&& value) { + return insert(std::move(value)).first; + } + + template + using IsRandomAccess = std::is_same::iterator_category, + std::random_access_iterator_tag>; + + + template + struct has_difference_operator + { + private: + using yes = std::true_type; + using no = std::false_type; + + template static auto test(int) -> decltype(std::declval() - std::declval() == 1, yes()); + template static no test(...); + + public: + static constexpr bool value = std::is_same(0)), yes>::value; + }; + + template ::value, int> = 0> + void insert(InputIt first, InputIt last) { + this->reserve(this->size() + (last - first)); + for (; first != last; ++first) + insert(*first); + } + + template ::value, int> = 0> + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) + insert(*first); + } + + template = 0, RequiresInsertable = 0> + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + insert_return_type insert(node_type&& node) { + if (!node) return {end(), false, node_type()}; + const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node)); + auto res = PolicyTraits::apply( + InsertSlot{*this, std::move(*CommonAccess::GetSlot(node))}, + elem); + if (res.second) { + CommonAccess::Reset(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + + insert_return_type insert(node_type&& node, size_t hashval) { + if (!node) return {end(), false, node_type()}; + const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node)); + auto res = PolicyTraits::apply( + InsertSlotWithHash{*this, std::move(*CommonAccess::GetSlot(node)), hashval}, + elem); + if (res.second) { + CommonAccess::Reset(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + + iterator insert(const_iterator, node_type&& node) { + return insert(std::move(node)).first; + } + + // This overload kicks in if we can deduce the key from args. This enables us + // to avoid constructing value_type if an entry with the same key already + // exists. + // + // For example: + // + // flat_hash_map m = {{"abc", "def"}}; + // // Creates no std::string copies and makes no heap allocations. + // m.emplace("abc", "xyz"); + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + return PolicyTraits::apply(EmplaceDecomposable{*this}, + std::forward(args)...); + } + + // This overload kicks in if we cannot deduce the key from args. It constructs + // value_type unconditionally and then either moves it into the table or + // destroys. + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + typename std::aligned_storage::type + raw; + slot_type* slot = reinterpret_cast(&raw); + + PolicyTraits::construct(&alloc_ref(), slot, std::forward(args)...); + const auto& elem = PolicyTraits::element(slot); + return PolicyTraits::apply(InsertSlot{*this, std::move(*slot)}, elem); + } + + template + iterator emplace_hint(const_iterator, Args&&... args) { + return emplace(std::forward(args)...).first; + } + + // Extension API: support for lazy emplace. + // + // Looks up key in the table. If found, returns the iterator to the element. + // Otherwise calls f with one argument of type raw_hash_set::constructor. f + // MUST call raw_hash_set::constructor with arguments as if a + // raw_hash_set::value_type is constructed, otherwise the behavior is + // undefined. + // + // For example: + // + // std::unordered_set s; + // // Makes ArenaStr even if "abc" is in the map. + // s.insert(ArenaString(&arena, "abc")); + // + // flat_hash_set s; + // // Makes ArenaStr only if "abc" is not in the map. + // s.lazy_emplace("abc", [&](const constructor& ctor) { + // ctor(&arena, "abc"); + // }); + // + // WARNING: This API is currently experimental. If there is a way to implement + // the same thing with the rest of the API, prefer that. + class constructor + { + friend class raw_hash_set; + + public: + template + void operator()(Args&&... args) const { + assert(*slot_); + PolicyTraits::construct(alloc_, *slot_, std::forward(args)...); + *slot_ = nullptr; + } + + private: + constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {} + + allocator_type* alloc_; + slot_type** slot_; + }; + + template + iterator lazy_emplace(const key_arg& key, F&& f) { + auto res = find_or_prepare_insert(key); + if (res.second) { + lazy_emplace_at(res.first, std::forward(f)); + } + return iterator_at(res.first); + } + + template + iterator lazy_emplace_with_hash(const key_arg& key, size_t &hashval, F&& f) { + auto res = find_or_prepare_insert(key, hashval); + if (res.second) { + lazy_emplace_at(res.first, std::forward(f)); + } + return iterator_at(res.first); + } + + template + void lazy_emplace_at(size_t& idx, F&& f) { + slot_type* slot = slots_ + idx; + std::forward(f)(constructor(&alloc_ref(), &slot)); + assert(!slot); + } + + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.erase("abc"); + // + // flat_hash_set s; + // // Uses "abc" directly without copying it into std::string. + // s.erase("abc"); + template + size_type erase(const key_arg& key) { + auto it = find(key); + if (it == end()) return 0; + _erase(it); + return 1; + } + + + iterator erase(const_iterator cit) { return erase(cit.inner_); } + + // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`, + // this method returns void to reduce algorithmic complexity to O(1). In + // order to erase while iterating across a map, use the following idiom (which + // also works for standard containers): + // + // for (auto it = m.begin(), end = m.end(); it != end;) { + // if () { + // m._erase(it++); + // } else { + // ++it; + // } + // } + void _erase(iterator it) { + assert(it != end()); + PolicyTraits::destroy(&alloc_ref(), it.slot_); + erase_meta_only(it); + } + void _erase(const_iterator cit) { _erase(cit.inner_); } + + // This overload is necessary because otherwise erase(const K&) would be + // a better match if non-const iterator is passed as an argument. + iterator erase(iterator it) { + auto res = it; + ++res; + _erase(it); + return res; + } + + iterator erase(const_iterator first, const_iterator last) { + while (first != last) { + _erase(first++); + } + return last.inner_; + } + + // Moves elements from `src` into `this`. + // If the element already exists in `this`, it is left unmodified in `src`. + template + void merge(raw_hash_set& src) { // NOLINT + assert(this != &src); + for (auto it = src.begin(), e = src.end(); it != e; ++it) { + if (PolicyTraits::apply(InsertSlot{*this, std::move(*it.slot_)}, + PolicyTraits::element(it.slot_)) + .second) { + src.erase_meta_only(it); + } + } + } + + template + void merge(raw_hash_set&& src) { + merge(src); + } + + node_type extract(const_iterator position) { + auto node = + CommonAccess::Make(alloc_ref(), position.inner_.slot_); + erase_meta_only(position); + return node; + } + + template < + class K = key_type, + typename std::enable_if::value, int>::type = 0> + node_type extract(const key_arg& key) { + auto it = find(key); + return it == end() ? node_type() : extract(const_iterator{it}); + } + + void swap(raw_hash_set& that) noexcept( + IsNoThrowSwappable() && IsNoThrowSwappable() && + (!AllocTraits::propagate_on_container_swap::value || + IsNoThrowSwappable())) { + using std::swap; + swap(ctrl_, that.ctrl_); + swap(slots_, that.slots_); + swap(size_, that.size_); + swap(capacity_, that.capacity_); + swap(growth_left(), that.growth_left()); + swap(hash_ref(), that.hash_ref()); + swap(eq_ref(), that.eq_ref()); + swap(infoz_, that.infoz_); + if (AllocTraits::propagate_on_container_swap::value) { + swap(alloc_ref(), that.alloc_ref()); + } else { + // If the allocators do not compare equal it is officially undefined + // behavior. We choose to do nothing. + } + } + +#ifndef PHMAP_NON_DETERMINISTIC + template + bool dump(OutputArchive&) const; + + template + bool load(InputArchive&); +#endif + + void rehash(size_t n) { + if (n == 0 && capacity_ == 0) return; + if (n == 0 && size_ == 0) { + destroy_slots(); + infoz_.RecordStorageChanged(0, 0); + return; + } + // bitor is a faster way of doing `max` here. We will round up to the next + // power-of-2-minus-1, so bitor is good enough. + auto m = NormalizeCapacity((std::max)(n, size())); + // n == 0 unconditionally rehashes as per the standard. + if (n == 0 || m > capacity_) { + resize(m); + } + } + + void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.count("abc"); + // + // ch_set s; + // // Uses "abc" directly without copying it into std::string. + // s.count("abc"); + template + size_t count(const key_arg& key) const { + return find(key) == end() ? size_t(0) : size_t(1); + } + + // Issues CPU prefetch instructions for the memory needed to find or insert + // a key. Like all lookup functions, this support heterogeneous keys. + // + // NOTE: This is a very low level operation and should not be used without + // specific benchmarks indicating its importance. + void prefetch_hash(size_t hashval) const { + (void)hashval; +#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) + auto seq = probe(hashval); + _mm_prefetch((const char *)(ctrl_ + seq.offset()), _MM_HINT_NTA); + _mm_prefetch((const char *)(slots_ + seq.offset()), _MM_HINT_NTA); +#elif defined(__GNUC__) + auto seq = probe(hashval); + __builtin_prefetch(static_cast(ctrl_ + seq.offset())); + __builtin_prefetch(static_cast(slots_ + seq.offset())); +#endif // __GNUC__ + } + + template + void prefetch(const key_arg& key) const { + prefetch_hash(this->hash(key)); + } + + // The API of find() has two extensions. + // + // 1. The hash can be passed by the user. It must be equal to the hash of the + // key. + // + // 2. The type of the key argument doesn't have to be key_type. This is so + // called heterogeneous key support. + template + iterator find(const key_arg& key, size_t hashval) { + auto seq = probe(hashval); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match((h2_t)H2(hashval))) { + if (PHMAP_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset((size_t)i))))) + return iterator_at(seq.offset((size_t)i)); + } + if (PHMAP_PREDICT_TRUE(g.MatchEmpty())) + return end(); + seq.next(); + } + } + template + iterator find(const key_arg& key) { + return find(key, this->hash(key)); + } + + template + const_iterator find(const key_arg& key, size_t hashval) const { + return const_cast(this)->find(key, hashval); + } + template + const_iterator find(const key_arg& key) const { + return find(key, this->hash(key)); + } + + template + bool contains(const key_arg& key) const { + return find(key) != end(); + } + + template + bool contains(const key_arg& key, size_t hashval) const { + return find(key, hashval) != end(); + } + + template + std::pair equal_range(const key_arg& key) { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + template + std::pair equal_range( + const key_arg& key) const { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + + size_t bucket_count() const { return capacity_; } + float load_factor() const { + return capacity_ ? static_cast(size()) / capacity_ : 0.0; + } + float max_load_factor() const { return 1.0f; } + void max_load_factor(float) { + // Does nothing. + } + + hasher hash_function() const { return hash_ref(); } // warning: doesn't match internal hash - use hash() member function + key_equal key_eq() const { return eq_ref(); } + allocator_type get_allocator() const { return alloc_ref(); } + + friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) { + if (a.size() != b.size()) return false; + const raw_hash_set* outer = &a; + const raw_hash_set* inner = &b; + if (outer->capacity() > inner->capacity()) + std::swap(outer, inner); + for (const value_type& elem : *outer) + if (!inner->has_element(elem)) return false; + return true; + } + + friend bool operator!=(const raw_hash_set& a, const raw_hash_set& b) { + return !(a == b); + } + + friend void swap(raw_hash_set& a, + raw_hash_set& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); + } + + template + size_t hash(const K& key) const { + return HashElement{hash_ref()}(key); + } + +private: + template + friend struct phmap::priv::hashtable_debug_internal::HashtableDebugAccess; + + struct FindElement + { + template + const_iterator operator()(const K& key, Args&&...) const { + return s.find(key); + } + const raw_hash_set& s; + }; + + struct HashElement + { + template + size_t operator()(const K& key, Args&&...) const { + return phmap_mix()(h(key)); + } + const hasher& h; + }; + + template + struct EqualElement + { + template + bool operator()(const K2& lhs, Args&&...) const { + return eq(lhs, rhs); + } + const K1& rhs; + const key_equal& eq; + }; + + template + std::pair emplace_decomposable(const K& key, size_t hashval, + Args&&... args) + { + auto res = find_or_prepare_insert(key, hashval); + if (res.second) { + emplace_at(res.first, std::forward(args)...); + } + return {iterator_at(res.first), res.second}; + } + + struct EmplaceDecomposable + { + template + std::pair operator()(const K& key, Args&&... args) const { + return s.emplace_decomposable(key, s.hash(key), std::forward(args)...); + } + raw_hash_set& s; + }; + + template + struct InsertSlot + { + template + std::pair operator()(const K& key, Args&&...) && { + auto res = s.find_or_prepare_insert(key); + if (res.second) { + PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot); + } else if (do_destroy) { + PolicyTraits::destroy(&s.alloc_ref(), &slot); + } + return {s.iterator_at(res.first), res.second}; + } + raw_hash_set& s; + // Constructed slot. Either moved into place or destroyed. + slot_type&& slot; + }; + + template + struct InsertSlotWithHash + { + template + std::pair operator()(const K& key, Args&&...) && { + auto res = s.find_or_prepare_insert(key, hashval); + if (res.second) { + PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot); + } else if (do_destroy) { + PolicyTraits::destroy(&s.alloc_ref(), &slot); + } + return {s.iterator_at(res.first), res.second}; + } + raw_hash_set& s; + // Constructed slot. Either moved into place or destroyed. + slot_type&& slot; + size_t &hashval; + }; + + // "erases" the object from the container, except that it doesn't actually + // destroy the object. It only updates all the metadata of the class. + // This can be used in conjunction with Policy::transfer to move the object to + // another place. + void erase_meta_only(const_iterator it) { + assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator"); + --size_; + const size_t index = (size_t)(it.inner_.ctrl_ - ctrl_); + const size_t index_before = (index - Group::kWidth) & capacity_; + const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty(); + const auto empty_before = Group(ctrl_ + index_before).MatchEmpty(); + + // We count how many consecutive non empties we have to the right and to the + // left of `it`. If the sum is >= kWidth then there is at least one probe + // window that might have seen a full group. + bool was_never_full = + empty_before && empty_after && + static_cast(empty_after.TrailingZeros() + + empty_before.LeadingZeros()) < Group::kWidth; + + set_ctrl(index, was_never_full ? kEmpty : kDeleted); + growth_left() += was_never_full; + infoz_.RecordErase(); + } + + void initialize_slots() { + assert(capacity_); + if (std::is_same>::value && + slots_ == nullptr) { + infoz_ = Sample(); + } + + auto layout = MakeLayout(capacity_); + char* mem = static_cast( + Allocate(&alloc_ref(), layout.AllocSize())); + ctrl_ = reinterpret_cast(layout.template Pointer<0>(mem)); + slots_ = layout.template Pointer<1>(mem); + reset_ctrl(); + reset_growth_left(); + infoz_.RecordStorageChanged(size_, capacity_); + } + + void destroy_slots() { + if (!capacity_) return; + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + auto layout = MakeLayout(capacity_); + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + Deallocate(&alloc_ref(), ctrl_, layout.AllocSize()); + ctrl_ = EmptyGroup(); + slots_ = nullptr; + size_ = 0; + capacity_ = 0; + growth_left() = 0; + } + + void resize(size_t new_capacity) { + assert(IsValidCapacity(new_capacity)); + auto* old_ctrl = ctrl_; + auto* old_slots = slots_; + const size_t old_capacity = capacity_; + capacity_ = new_capacity; + initialize_slots(); + + for (size_t i = 0; i != old_capacity; ++i) { + if (IsFull(old_ctrl[i])) { + size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, + PolicyTraits::element(old_slots + i)); + auto target = find_first_non_full(hashval); + size_t new_i = target.offset; + set_ctrl(new_i, H2(hashval)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); + } + } + if (old_capacity) { + SanitizerUnpoisonMemoryRegion(old_slots, + sizeof(slot_type) * old_capacity); + auto layout = MakeLayout(old_capacity); + Deallocate(&alloc_ref(), old_ctrl, + layout.AllocSize()); + } + } + + void drop_deletes_without_resize() PHMAP_ATTRIBUTE_NOINLINE { + assert(IsValidCapacity(capacity_)); + assert(!is_small()); + // Algorithm: + // - mark all DELETED slots as EMPTY + // - mark all FULL slots as DELETED + // - for each slot marked as DELETED + // hash = Hash(element) + // target = find_first_non_full(hash) + // if target is in the same group + // mark slot as FULL + // else if target is EMPTY + // transfer element to target + // mark slot as EMPTY + // mark target as FULL + // else if target is DELETED + // swap current element with target element + // mark target as FULL + // repeat procedure for current slot with moved from element (target) + ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_); + typename std::aligned_storage::type + raw; + slot_type* slot = reinterpret_cast(&raw); + for (size_t i = 0; i != capacity_; ++i) { + if (!IsDeleted(ctrl_[i])) continue; + size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, + PolicyTraits::element(slots_ + i)); + auto target = find_first_non_full(hashval); + size_t new_i = target.offset; + + // Verify if the old and new i fall within the same group wrt the hashval. + // If they do, we don't need to move the object as it falls already in the + // best probe we can. + const auto probe_index = [&](size_t pos) { + return ((pos - probe(hashval).offset()) & capacity_) / Group::kWidth; + }; + + // Element doesn't move. + if (PHMAP_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { + set_ctrl(i, H2(hashval)); + continue; + } + if (IsEmpty(ctrl_[new_i])) { + // Transfer element to the empty spot. + // set_ctrl poisons/unpoisons the slots so we have to call it at the + // right time. + set_ctrl(new_i, H2(hashval)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); + set_ctrl(i, kEmpty); + } else { + assert(IsDeleted(ctrl_[new_i])); + set_ctrl(new_i, H2(hashval)); + // Until we are done rehashing, DELETED marks previously FULL slots. + // Swap i and new_i elements. + PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i); + PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot); + --i; // repeat + } + } + reset_growth_left(); + } + + void rehash_and_grow_if_necessary() { + if (capacity_ == 0) { + resize(1); + } else if (size() <= CapacityToGrowth(capacity()) / 2) { + // Squash DELETED without growing if there is enough capacity. + drop_deletes_without_resize(); + } else { + // Otherwise grow the container. + resize(capacity_ * 2 + 1); + } + } + + bool has_element(const value_type& elem, size_t hashval) const { + auto seq = probe(hashval); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match((h2_t)H2(hashval))) { + if (PHMAP_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset((size_t)i)) == + elem)) + return true; + } + if (PHMAP_PREDICT_TRUE(g.MatchEmpty())) return false; + seq.next(); + assert(seq.getindex() < capacity_ && "full table!"); + } + return false; + } + + bool has_element(const value_type& elem) const { + size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, elem); + return has_element(elem, hashval); + } + + // Probes the raw_hash_set with the probe sequence for hash and returns the + // pointer to the first empty or deleted slot. + // NOTE: this function must work with tables having both kEmpty and kDelete + // in one group. Such tables appears during drop_deletes_without_resize. + // + // This function is very useful when insertions happen and: + // - the input is already a set + // - there are enough slots + // - the element with the hash is not in the table + struct FindInfo + { + size_t offset; + size_t probe_length; + }; + FindInfo find_first_non_full(size_t hashval) { + auto seq = probe(hashval); + while (true) { + Group g{ctrl_ + seq.offset()}; + auto mask = g.MatchEmptyOrDeleted(); + if (mask) { + return {seq.offset((size_t)mask.LowestBitSet()), seq.getindex()}; + } + assert(seq.getindex() < capacity_ && "full table!"); + seq.next(); + } + } + + // TODO(alkis): Optimize this assuming *this and that don't overlap. + raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) { + raw_hash_set tmp(std::move(that)); + swap(tmp); + return *this; + } + raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) { + raw_hash_set tmp(std::move(that), alloc_ref()); + swap(tmp); + return *this; + } + +protected: + template + std::pair find_or_prepare_insert(const K& key, size_t hashval) { + auto seq = probe(hashval); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match((h2_t)H2(hashval))) { + if (PHMAP_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset((size_t)i))))) + return {seq.offset((size_t)i), false}; + } + if (PHMAP_PREDICT_TRUE(g.MatchEmpty())) break; + seq.next(); + } + return {prepare_insert(hashval), true}; + } + + template + std::pair find_or_prepare_insert(const K& key) { + return find_or_prepare_insert(key, this->hash(key)); + } + + size_t prepare_insert(size_t hashval) PHMAP_ATTRIBUTE_NOINLINE { + auto target = find_first_non_full(hashval); + if (PHMAP_PREDICT_FALSE(growth_left() == 0 && + !IsDeleted(ctrl_[target.offset]))) { + rehash_and_grow_if_necessary(); + target = find_first_non_full(hashval); + } + ++size_; + growth_left() -= IsEmpty(ctrl_[target.offset]); + set_ctrl(target.offset, H2(hashval)); + infoz_.RecordInsert(hashval, target.probe_length); + return target.offset; + } + + // Constructs the value in the space pointed by the iterator. This only works + // after an unsuccessful find_or_prepare_insert() and before any other + // modifications happen in the raw_hash_set. + // + // PRECONDITION: i is an index returned from find_or_prepare_insert(k), where + // k is the key decomposed from `forward(args)...`, and the bool + // returned by find_or_prepare_insert(k) was true. + // POSTCONDITION: *m.iterator_at(i) == value_type(forward(args)...). + template + void emplace_at(size_t i, Args&&... args) { + PolicyTraits::construct(&alloc_ref(), slots_ + i, + std::forward(args)...); + + assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) == + iterator_at(i) && + "constructed value does not match the lookup key"); + } + + iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; } + const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; } + +private: + friend struct RawHashSetTestOnlyAccess; + + probe_seq probe(size_t hashval) const { + return probe_seq(H1(hashval, ctrl_), capacity_); + } + + // Reset all ctrl bytes back to kEmpty, except the sentinel. + void reset_ctrl() { + std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth + 1); + ctrl_[capacity_] = kSentinel; + SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + } + + void reset_growth_left() { + growth_left() = CapacityToGrowth(capacity()) - size_; + } + + // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at + // the end too. + void set_ctrl(size_t i, ctrl_t h) { + assert(i < capacity_); + + if (IsFull(h)) { + SanitizerUnpoisonObject(slots_ + i); + } else { + SanitizerPoisonObject(slots_ + i); + } + + ctrl_[i] = h; + ctrl_[((i - Group::kWidth) & capacity_) + 1 + + ((Group::kWidth - 1) & capacity_)] = h; + } + + size_t& growth_left() { return settings_.template get<0>(); } + + template class RefSet, + class M, class P, class H, class E, class A> + friend class parallel_hash_set; + + template class RefSet, + class M, class P, class H, class E, class A> + friend class parallel_hash_map; + + // The representation of the object has two modes: + // - small: For capacities < kWidth-1 + // - large: For the rest. + // + // Differences: + // - In small mode we are able to use the whole capacity. The extra control + // bytes give us at least one "empty" control byte to stop the iteration. + // This is important to make 1 a valid capacity. + // + // - In small mode only the first `capacity()` control bytes after the + // sentinel are valid. The rest contain dummy kEmpty values that do not + // represent a real slot. This is important to take into account on + // find_first_non_full(), where we never try ShouldInsertBackwards() for + // small tables. + bool is_small() const { return capacity_ < Group::kWidth - 1; } + + hasher& hash_ref() { return settings_.template get<1>(); } + const hasher& hash_ref() const { return settings_.template get<1>(); } + key_equal& eq_ref() { return settings_.template get<2>(); } + const key_equal& eq_ref() const { return settings_.template get<2>(); } + allocator_type& alloc_ref() { return settings_.template get<3>(); } + const allocator_type& alloc_ref() const { + return settings_.template get<3>(); + } + + // TODO(alkis): Investigate removing some of these fields: + // - ctrl/slots can be derived from each other + // - size can be moved into the slot array + ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t] + slot_type* slots_ = nullptr; // [capacity * slot_type] + size_t size_ = 0; // number of full slots + size_t capacity_ = 0; // total number of slots + HashtablezInfoHandle infoz_; + phmap::priv::CompressedTuple + settings_{0, hasher{}, key_equal{}, allocator_type{}}; +}; + + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +template +class raw_hash_map : public raw_hash_set +{ + // P is Policy. It's passed as a template argument to support maps that have + // incomplete types as values, as in unordered_map. + // MappedReference<> may be a non-reference type. + template + using MappedReference = decltype(P::value( + std::addressof(std::declval()))); + + // MappedConstReference<> may be a non-reference type. + template + using MappedConstReference = decltype(P::value( + std::addressof(std::declval()))); + + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + + using Base = raw_hash_set; + +public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + template + using key_arg = typename KeyArgImpl::template type; + + static_assert(!std::is_reference::value, ""); + // TODO(alkis): remove this assertion and verify that reference mapped_type is + // supported. + static_assert(!std::is_reference::value, ""); + + using iterator = typename raw_hash_map::raw_hash_set::iterator; + using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator; + + raw_hash_map() {} + using Base::raw_hash_set; // use raw_hash_set constructor + + // The last two template parameters ensure that both arguments are rvalues + // (lvalue arguments are handled by the overloads below). This is necessary + // for supporting bitfield arguments. + // + // union { int n : 1; }; + // flat_hash_map m; + // m.insert_or_assign(n, n); + template + std::pair insert_or_assign(key_arg&& k, V&& v) { + return insert_or_assign_impl(std::forward(k), std::forward(v)); + } + + template + std::pair insert_or_assign(key_arg&& k, const V& v) { + return insert_or_assign_impl(std::forward(k), v); + } + + template + std::pair insert_or_assign(const key_arg& k, V&& v) { + return insert_or_assign_impl(k, std::forward(v)); + } + + template + std::pair insert_or_assign(const key_arg& k, const V& v) { + return insert_or_assign_impl(k, v); + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, V&& v) { + return insert_or_assign(std::forward(k), std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, const V& v) { + return insert_or_assign(std::forward(k), v).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, V&& v) { + return insert_or_assign(k, std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, const V& v) { + return insert_or_assign(k, v).first; + } + + template ::value, int>::type = 0, + K* = nullptr> + std::pair try_emplace(key_arg&& k, Args&&... args) { + return try_emplace_impl(std::forward(k), std::forward(args)...); + } + + template ::value, int>::type = 0> + std::pair try_emplace(const key_arg& k, Args&&... args) { + return try_emplace_impl(k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator, key_arg&& k, Args&&... args) { + return try_emplace(std::forward(k), std::forward(args)...).first; + } + + template + iterator try_emplace(const_iterator, const key_arg& k, Args&&... args) { + return try_emplace(k, std::forward(args)...).first; + } + + template + MappedReference

at(const key_arg& key) { + auto it = this->find(key); + if (it == this->end()) + phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key"); + return Policy::value(&*it); + } + + template + MappedConstReference

at(const key_arg& key) const { + auto it = this->find(key); + if (it == this->end()) + phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key"); + return Policy::value(&*it); + } + + template + MappedReference

operator[](key_arg&& key) { + return Policy::value(&*try_emplace(std::forward(key)).first); + } + + template + MappedReference

operator[](const key_arg& key) { + return Policy::value(&*try_emplace(key).first); + } + +private: + template + std::pair insert_or_assign_impl(K&& k, V&& v) { + auto res = this->find_or_prepare_insert(k); + if (res.second) + this->emplace_at(res.first, std::forward(k), std::forward(v)); + else + Policy::value(&*this->iterator_at(res.first)) = std::forward(v); + return {this->iterator_at(res.first), res.second}; + } + + template + std::pair try_emplace_impl(K&& k, Args&&... args) { + auto res = this->find_or_prepare_insert(k); + if (res.second) + this->emplace_at(res.first, std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + return {this->iterator_at(res.first), res.second}; + } +}; + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// Returns "random" seed. +inline size_t RandomSeed() +{ +#if PHMAP_HAVE_THREAD_LOCAL + static thread_local size_t counter = 0; + size_t value = ++counter; +#else // PHMAP_HAVE_THREAD_LOCAL + static std::atomic counter(0); + size_t value = counter.fetch_add(1, std::memory_order_relaxed); +#endif // PHMAP_HAVE_THREAD_LOCAL + return value ^ static_cast(reinterpret_cast(&counter)); +} + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +template class RefSet, + class Mtx_, + class Policy, class Hash, class Eq, class Alloc> +class parallel_hash_set +{ + using PolicyTraits = hash_policy_traits; + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + + static_assert(N <= 12, "N = 12 means 4096 hash tables!"); + constexpr static size_t num_tables = 1 << N; + constexpr static size_t mask = num_tables - 1; + +public: + using EmbeddedSet = RefSet; + using EmbeddedIterator= typename EmbeddedSet::iterator; + using EmbeddedConstIterator= typename EmbeddedSet::const_iterator; + using constructor = typename EmbeddedSet::constructor; + using init_type = typename PolicyTraits::init_type; + using key_type = typename PolicyTraits::key_type; + using slot_type = typename PolicyTraits::slot_type; + using allocator_type = Alloc; + using size_type = size_t; + using difference_type = ptrdiff_t; + using hasher = Hash; + using key_equal = Eq; + using policy_type = Policy; + using value_type = typename PolicyTraits::value_type; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename phmap::allocator_traits< + allocator_type>::template rebind_traits::pointer; + using const_pointer = typename phmap::allocator_traits< + allocator_type>::template rebind_traits::const_pointer; + + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + // -------------------------------------------------------------------- + template + using key_arg = typename KeyArgImpl::template type; + +protected: + using Lockable = phmap::LockableImpl; + + // -------------------------------------------------------------------- + struct Inner : public Lockable + { + bool operator==(const Inner& o) const + { + typename Lockable::SharedLocks l(const_cast(*this), const_cast(o)); + return set_ == o.set_; + } + + EmbeddedSet set_; + }; + +private: + // Give an early error when key_type is not hashable/eq. + // -------------------------------------------------------------------- + auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k)); + auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k)); + + using AllocTraits = phmap::allocator_traits; + + static_assert(std::is_lvalue_reference::value, + "Policy::element() must return a reference"); + + template + struct SameAsElementReference : std::is_same< + typename std::remove_cv::type>::type, + typename std::remove_cv::type>::type> {}; + + // An enabler for insert(T&&): T must be convertible to init_type or be the + // same as [cv] value_type [ref]. + // Note: we separate SameAsElementReference into its own type to avoid using + // reference unless we need to. MSVC doesn't seem to like it in some + // cases. + // -------------------------------------------------------------------- + template + using RequiresInsertable = typename std::enable_if< + phmap::disjunction, + SameAsElementReference>::value, + int>::type; + + // RequiresNotInit is a workaround for gcc prior to 7.1. + // See https://godbolt.org/g/Y4xsUh. + template + using RequiresNotInit = + typename std::enable_if::value, int>::type; + + template + using IsDecomposable = IsDecomposable; + +public: + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + + // --------------------- i t e r a t o r ------------------------------ + class iterator + { + friend class parallel_hash_set; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename parallel_hash_set::value_type; + using reference = + phmap::conditional_t; + using pointer = phmap::remove_reference_t*; + using difference_type = typename parallel_hash_set::difference_type; + using Inner = typename parallel_hash_set::Inner; + using EmbeddedSet = typename parallel_hash_set::EmbeddedSet; + using EmbeddedIterator = typename EmbeddedSet::iterator; + + iterator() {} + + reference operator*() const { return *it_; } + pointer operator->() const { return &operator*(); } + + iterator& operator++() { + assert(inner_); // null inner means we are already at the end + ++it_; + skip_empty(); + return *this; + } + + iterator operator++(int) { + assert(inner_); // null inner means we are already at the end + auto tmp = *this; + ++*this; + return tmp; + } + + friend bool operator==(const iterator& a, const iterator& b) { + return a.inner_ == b.inner_ && (!a.inner_ || a.it_ == b.it_); + } + + friend bool operator!=(const iterator& a, const iterator& b) { + return !(a == b); + } + + private: + iterator(Inner *inner, Inner *inner_end, const EmbeddedIterator& it) : + inner_(inner), inner_end_(inner_end), it_(it) { // for begin() and end() + if (inner) + it_end_ = inner->set_.end(); + } + + void skip_empty() { + while (it_ == it_end_) { + ++inner_; + if (inner_ == inner_end_) { + inner_ = nullptr; // marks end() + break; + } + else { + it_ = inner_->set_.begin(); + it_end_ = inner_->set_.end(); + } + } + } + + Inner *inner_ = nullptr; + Inner *inner_end_ = nullptr; + EmbeddedIterator it_, it_end_; + }; + + // --------------------- c o n s t i t e r a t o r ----------------- + class const_iterator + { + friend class parallel_hash_set; + + public: + using iterator_category = typename iterator::iterator_category; + using value_type = typename parallel_hash_set::value_type; + using reference = typename parallel_hash_set::const_reference; + using pointer = typename parallel_hash_set::const_pointer; + using difference_type = typename parallel_hash_set::difference_type; + using Inner = typename parallel_hash_set::Inner; + + const_iterator() {} + // Implicit construction from iterator. + const_iterator(iterator i) : iter_(std::move(i)) {} + + reference operator*() const { return *(iter_); } + pointer operator->() const { return iter_.operator->(); } + + const_iterator& operator++() { + ++iter_; + return *this; + } + const_iterator operator++(int) { return iter_++; } + + friend bool operator==(const const_iterator& a, const const_iterator& b) { + return a.iter_ == b.iter_; + } + friend bool operator!=(const const_iterator& a, const const_iterator& b) { + return !(a == b); + } + + private: + const_iterator(const Inner *inner, const Inner *inner_end, const EmbeddedIterator& it) + : iter_(const_cast(inner), + const_cast(inner_end), + const_cast(it)) {} + + iterator iter_; + }; + + using node_type = node_handle, Alloc>; + using insert_return_type = InsertReturnType; + + // ------------------------- c o n s t r u c t o r s ------------------ + + parallel_hash_set() noexcept( + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value) {} + + explicit parallel_hash_set(size_t bucket_count, + const hasher& hash_param = hasher(), + const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) { + for (auto& inner : sets_) + inner.set_ = EmbeddedSet(bucket_count / N, hash_param, eq, alloc); + } + + parallel_hash_set(size_t bucket_count, + const hasher& hash_param, + const allocator_type& alloc) + : parallel_hash_set(bucket_count, hash_param, key_equal(), alloc) {} + + parallel_hash_set(size_t bucket_count, const allocator_type& alloc) + : parallel_hash_set(bucket_count, hasher(), key_equal(), alloc) {} + + explicit parallel_hash_set(const allocator_type& alloc) + : parallel_hash_set(0, hasher(), key_equal(), alloc) {} + + template + parallel_hash_set(InputIter first, InputIter last, size_t bucket_count = 0, + const hasher& hash_param = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : parallel_hash_set(bucket_count, hash_param, eq, alloc) { + insert(first, last); + } + + template + parallel_hash_set(InputIter first, InputIter last, size_t bucket_count, + const hasher& hash_param, const allocator_type& alloc) + : parallel_hash_set(first, last, bucket_count, hash_param, key_equal(), alloc) {} + + template + parallel_hash_set(InputIter first, InputIter last, size_t bucket_count, + const allocator_type& alloc) + : parallel_hash_set(first, last, bucket_count, hasher(), key_equal(), alloc) {} + + template + parallel_hash_set(InputIter first, InputIter last, const allocator_type& alloc) + : parallel_hash_set(first, last, 0, hasher(), key_equal(), alloc) {} + + // Instead of accepting std::initializer_list as the first + // argument like std::unordered_set does, we have two overloads + // that accept std::initializer_list and std::initializer_list. + // This is advantageous for performance. + // + // // Turns {"abc", "def"} into std::initializer_list, then copies + // // the strings into the set. + // std::unordered_set s = {"abc", "def"}; + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // phmap::flat_hash_set s = {"abc", "def"}; + // + // The same trick is used in insert(). + // + // The enabler is necessary to prevent this constructor from triggering where + // the copy constructor is meant to be called. + // + // phmap::flat_hash_set a, b{a}; + // + // RequiresNotInit is a workaround for gcc prior to 7.1. + // -------------------------------------------------------------------- + template = 0, RequiresInsertable = 0> + parallel_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash_param = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : parallel_hash_set(init.begin(), init.end(), bucket_count, hash_param, eq, alloc) {} + + parallel_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash_param = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : parallel_hash_set(init.begin(), init.end(), bucket_count, hash_param, eq, alloc) {} + + template = 0, RequiresInsertable = 0> + parallel_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash_param, const allocator_type& alloc) + : parallel_hash_set(init, bucket_count, hash_param, key_equal(), alloc) {} + + parallel_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash_param, const allocator_type& alloc) + : parallel_hash_set(init, bucket_count, hash_param, key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + parallel_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : parallel_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + parallel_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : parallel_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + parallel_hash_set(std::initializer_list init, const allocator_type& alloc) + : parallel_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + parallel_hash_set(std::initializer_list init, + const allocator_type& alloc) + : parallel_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + parallel_hash_set(const parallel_hash_set& that) + : parallel_hash_set(that, AllocTraits::select_on_container_copy_construction( + that.alloc_ref())) {} + + parallel_hash_set(const parallel_hash_set& that, const allocator_type& a) + : parallel_hash_set(0, that.hash_ref(), that.eq_ref(), a) { + for (size_t i=0; i::value&& + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value) + : parallel_hash_set(std::move(that), that.alloc_ref()) { + } + + parallel_hash_set(parallel_hash_set&& that, const allocator_type& a) + { + for (size_t i=0; i::is_always_equal::value && + std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value) { + for (size_t i=0; i(this)->begin(); } + const_iterator end() const { return const_cast(this)->end(); } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + bool empty() const { return !size(); } + + size_t size() const { + size_t sz = 0; + for (const auto& inner : sets_) + sz += inner.set_.size(); + return sz; + } + + size_t capacity() const { + size_t c = 0; + for (const auto& inner : sets_) + c += inner.set_.capacity(); + return c; + } + + size_t max_size() const { return (std::numeric_limits::max)(); } + + PHMAP_ATTRIBUTE_REINITIALIZES void clear() { + for (auto& inner : sets_) + inner.set_.clear(); + } + + // This overload kicks in when the argument is an rvalue of insertable and + // decomposable type other than init_type. + // + // flat_hash_map m; + // m.insert(std::make_pair("abc", 42)); + // -------------------------------------------------------------------- + template = 0, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + std::pair insert(T&& value) { + return emplace(std::forward(value)); + } + + // This overload kicks in when the argument is a bitfield or an lvalue of + // insertable and decomposable type. + // + // union { int n : 1; }; + // flat_hash_set s; + // s.insert(n); + // + // flat_hash_set s; + // const char* p = "hello"; + // s.insert(p); + // + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + // -------------------------------------------------------------------- + template < + class T, RequiresInsertable = 0, + typename std::enable_if::value, int>::type = 0> + std::pair insert(const T& value) { + return emplace(value); + } + + // This overload kicks in when the argument is an rvalue of init_type. Its + // purpose is to handle brace-init-list arguments. + // + // flat_hash_set> s; + // s.insert({"abc", 42}); + // -------------------------------------------------------------------- + std::pair insert(init_type&& value) { + return emplace(std::move(value)); + } + + template = 0, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + iterator insert(const_iterator, T&& value) { + return insert(std::forward(value)).first; + } + + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + // -------------------------------------------------------------------- + template < + class T, RequiresInsertable = 0, + typename std::enable_if::value, int>::type = 0> + iterator insert(const_iterator, const T& value) { + return insert(value).first; + } + + iterator insert(const_iterator, init_type&& value) { + return insert(std::move(value)).first; + } + + template + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) insert(*first); + } + + template = 0, RequiresInsertable = 0> + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + insert_return_type insert(node_type&& node) { + if (!node) + return {end(), false, node_type()}; + auto& key = node.key(); + size_t hashval = this->hash(key); + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + + typename Lockable::UniqueLock m(inner); + auto res = set.insert(std::move(node), hashval); + return { make_iterator(&inner, res.position), + res.inserted, + res.inserted ? node_type() : std::move(res.node) }; + } + + iterator insert(const_iterator, node_type&& node) { + return insert(std::move(node)).first; + } + + struct ReturnKey_ + { + template + Key operator()(Key&& k, const Args&...) const { + return std::forward(k); + } + }; + + template + std::pair emplace_decomposable(const K& key, Args&&... args) + { + size_t hashval = this->hash(key); + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + typename Lockable::UniqueLock m(inner); + return make_rv(&inner, set.emplace_decomposable(key, hashval, std::forward(args)...)); + } + + struct EmplaceDecomposable + { + template + std::pair operator()(const K& key, Args&&... args) const { + return s.emplace_decomposable(key, std::forward(args)...); + } + parallel_hash_set& s; + }; + + // This overload kicks in if we can deduce the key from args. This enables us + // to avoid constructing value_type if an entry with the same key already + // exists. + // + // For example: + // + // flat_hash_map m = {{"abc", "def"}}; + // // Creates no std::string copies and makes no heap allocations. + // m.emplace("abc", "xyz"); + // -------------------------------------------------------------------- + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + return PolicyTraits::apply(EmplaceDecomposable{*this}, + std::forward(args)...); + } + + // This overload kicks in if we cannot deduce the key from args. It constructs + // value_type unconditionally and then either moves it into the table or + // destroys. + // -------------------------------------------------------------------- + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + typename std::aligned_storage::type + raw; + slot_type* slot = reinterpret_cast(&raw); + + PolicyTraits::construct(&alloc_ref(), slot, std::forward(args)...); + const auto& elem = PolicyTraits::element(slot); + size_t hashval = this->hash(PolicyTraits::key(slot)); + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + typename Lockable::UniqueLock m(inner); + typename EmbeddedSet::template InsertSlotWithHash f { + inner, std::move(*slot), hashval}; + return make_rv(PolicyTraits::apply(f, elem)); + } + + template + iterator emplace_hint(const_iterator, Args&&... args) { + return emplace(std::forward(args)...).first; + } + + iterator make_iterator(Inner* inner, const EmbeddedIterator it) + { + if (it == inner->set_.end()) + return iterator(); + return iterator(inner, &sets_[0] + num_tables, it); + } + + std::pair make_rv(Inner* inner, + const std::pair& res) + { + return {iterator(inner, &sets_[0] + num_tables, res.first), res.second}; + } + + template + iterator lazy_emplace(const key_arg& key, F&& f) { + auto hashval = this->hash(key); + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + typename Lockable::UniqueLock m(inner); + return make_iterator(&inner, set.lazy_emplace_with_hash(key, hashval, std::forward(f))); + } + + template + bool lazy_emplace_l(const key_arg& key, FExists&& fExists, FEmplace&& fEmplace) { + typename Lockable::UniqueLock m; + auto res = this->find_or_prepare_insert(key, m); + Inner* inner = std::get<0>(res); + if (std::get<2>(res)) + inner->set_.lazy_emplace_at(std::get<1>(res), std::forward(fEmplace)); + else { + auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res))); + std::forward(fExists)(Policy::value(&*it)); + } + return std::get<2>(res); + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.erase("abc"); + // + // flat_hash_set s; + // // Uses "abc" directly without copying it into std::string. + // s.erase("abc"); + // -------------------------------------------------------------------- + template + size_type erase(const key_arg& key) { + auto hashval = this->hash(key); + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + typename Lockable::UpgradeLock m(inner); + auto it = set.find(key, hashval); + if (it == set.end()) + return 0; + + typename Lockable::UpgradeToUnique unique(m); + set._erase(it); + return 1; + } + + // -------------------------------------------------------------------- + iterator erase(const_iterator cit) { return erase(cit.iter_); } + + // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`, + // this method returns void to reduce algorithmic complexity to O(1). In + // order to erase while iterating across a map, use the following idiom (which + // also works for standard containers): + // + // for (auto it = m.begin(), end = m.end(); it != end;) { + // if () { + // m._erase(it++); + // } else { + // ++it; + // } + // } + // -------------------------------------------------------------------- + void _erase(iterator it) { + assert(it.inner_ != nullptr); + it.inner_->set_._erase(it.it_); + } + void _erase(const_iterator cit) { _erase(cit.iter_); } + + // This overload is necessary because otherwise erase(const K&) would be + // a better match if non-const iterator is passed as an argument. + // -------------------------------------------------------------------- + iterator erase(iterator it) { _erase(it++); return it; } + + iterator erase(const_iterator first, const_iterator last) { + while (first != last) { + _erase(first++); + } + return last.iter_; + } + + // Moves elements from `src` into `this`. + // If the element already exists in `this`, it is left unmodified in `src`. + // -------------------------------------------------------------------- + template + void merge(parallel_hash_set& src) { // NOLINT + assert(this != &src); + if (this != &src) + { + for (size_t i=0; i + void merge(parallel_hash_set&& src) { + merge(src); + } + + node_type extract(const_iterator position) { + return position.iter_.inner_->set_.extract(EmbeddedConstIterator(position.iter_.it_)); + } + + template < + class K = key_type, + typename std::enable_if::value, int>::type = 0> + node_type extract(const key_arg& key) { + auto it = find(key); + return it == end() ? node_type() : extract(const_iterator{it}); + } + + void swap(parallel_hash_set& that) noexcept( + IsNoThrowSwappable() && + (!AllocTraits::propagate_on_container_swap::value || + IsNoThrowSwappable())) { + using std::swap; + for (size_t i=0; i target ? normalized : target); + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.count("abc"); + // + // ch_set s; + // // Uses "abc" directly without copying it into std::string. + // s.count("abc"); + // -------------------------------------------------------------------- + template + size_t count(const key_arg& key) const { + return find(key) == end() ? 0 : 1; + } + + // Issues CPU prefetch instructions for the memory needed to find or insert + // a key. Like all lookup functions, this support heterogeneous keys. + // + // NOTE: This is a very low level operation and should not be used without + // specific benchmarks indicating its importance. + // -------------------------------------------------------------------- + template + void prefetch(const key_arg& key) const { + (void)key; + size_t hashval = this->hash(key); + const Inner& inner = sets_[subidx(hashval)]; + const auto& set = inner.set_; + typename Lockable::SharedLock m(const_cast(inner)); + set.prefetch_hash(hashval); + } + + // The API of find() has two extensions. + // + // 1. The hash can be passed by the user. It must be equal to the hash of the + // key. + // + // 2. The type of the key argument doesn't have to be key_type. This is so + // called heterogeneous key support. + // -------------------------------------------------------------------- + template + iterator find(const key_arg& key, size_t hashval) { + typename Lockable::SharedLock m; + return find(key, hashval, m); + } + + template + iterator find(const key_arg& key) { + return find(key, this->hash(key)); + } + + template + const_iterator find(const key_arg& key, size_t hashval) const { + return const_cast(this)->find(key, hashval); + } + + template + const_iterator find(const key_arg& key) const { + return find(key, this->hash(key)); + } + + template + bool contains(const key_arg& key) const { + return find(key) != end(); + } + + template + bool contains(const key_arg& key, size_t hashval) const { + return find(key, hashval) != end(); + } + + template + std::pair equal_range(const key_arg& key) { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + + template + std::pair equal_range( + const key_arg& key) const { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + + size_t bucket_count() const { + size_t sz = 0; + for (const auto& inner : sets_) + { + typename Lockable::SharedLock m(const_cast(inner)); + sz += inner.set_.bucket_count(); + } + return sz; + } + + float load_factor() const { + size_t capacity = bucket_count(); + return capacity ? static_cast(static_cast(size()) / capacity) : 0; + } + + float max_load_factor() const { return 1.0f; } + void max_load_factor(float) { + // Does nothing. + } + + hasher hash_function() const { return hash_ref(); } // warning: doesn't match internal hash - use hash() member function + key_equal key_eq() const { return eq_ref(); } + allocator_type get_allocator() const { return alloc_ref(); } + + friend bool operator==(const parallel_hash_set& a, const parallel_hash_set& b) { + return std::equal(a.sets_.begin(), a.sets_.end(), b.sets_.begin()); + } + + friend bool operator!=(const parallel_hash_set& a, const parallel_hash_set& b) { + return !(a == b); + } + + friend void swap(parallel_hash_set& a, + parallel_hash_set& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); + } + + template + size_t hash(const K& key) const { + return HashElement{hash_ref()}(key); + } + +#ifndef PHMAP_NON_DETERMINISTIC + template + bool dump(OutputArchive& ar) const; + + template + bool load(InputArchive& ar); +#endif + +private: + template + friend struct phmap::priv::hashtable_debug_internal::HashtableDebugAccess; + + struct FindElement + { + template + const_iterator operator()(const K& key, Args&&...) const { + return s.find(key); + } + const parallel_hash_set& s; + }; + + struct HashElement + { + template + size_t operator()(const K& key, Args&&...) const { + return phmap_mix()(h(key)); + } + const hasher& h; + }; + + template + struct EqualElement + { + template + bool operator()(const K2& lhs, Args&&...) const { + return eq(lhs, rhs); + } + const K1& rhs; + const key_equal& eq; + }; + + // "erases" the object from the container, except that it doesn't actually + // destroy the object. It only updates all the metadata of the class. + // This can be used in conjunction with Policy::transfer to move the object to + // another place. + // -------------------------------------------------------------------- + void erase_meta_only(const_iterator cit) { + auto &it = cit.iter_; + assert(it.set_ != nullptr); + it.set_.erase_meta_only(const_iterator(it.it_)); + } + + void drop_deletes_without_resize() PHMAP_ATTRIBUTE_NOINLINE { + for (auto& inner : sets_) + { + typename Lockable::UniqueLock m(inner); + inner.set_.drop_deletes_without_resize(); + } + } + + bool has_element(const value_type& elem) const { + size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, elem); + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + typename Lockable::SharedLock m(const_cast(inner)); + return set.has_element(elem, hashval); + } + + // TODO(alkis): Optimize this assuming *this and that don't overlap. + // -------------------------------------------------------------------- + parallel_hash_set& move_assign(parallel_hash_set&& that, std::true_type) { + parallel_hash_set tmp(std::move(that)); + swap(tmp); + return *this; + } + + parallel_hash_set& move_assign(parallel_hash_set&& that, std::false_type) { + parallel_hash_set tmp(std::move(that), alloc_ref()); + swap(tmp); + return *this; + } + +protected: + template + iterator find(const key_arg& key, size_t hashval, L &mutexlock) { + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + mutexlock = std::move(L(inner)); + auto it = set.find(key, hashval); + return make_iterator(&inner, it); + } + + template + std::tuple + find_or_prepare_insert(const K& key, typename Lockable::UniqueLock &mutexlock) { + auto hashval = this->hash(key); + Inner& inner = sets_[subidx(hashval)]; + auto& set = inner.set_; + mutexlock = std::move(typename Lockable::UniqueLock(inner)); + auto p = set.find_or_prepare_insert(key, hashval); // std::pair + return std::make_tuple(&inner, p.first, p.second); + } + + iterator iterator_at(Inner *inner, + const EmbeddedIterator& it) { + return {inner, &sets_[0] + num_tables, it}; + } + const_iterator iterator_at(Inner *inner, + const EmbeddedIterator& it) const { + return {inner, &sets_[0] + num_tables, it}; + } + + static size_t subidx(size_t hashval) { + return ((hashval >> 8) ^ (hashval >> 16) ^ (hashval >> 24)) & mask; + } + + static size_t subcnt() { + return num_tables; + } + +private: + friend struct RawHashSetTestOnlyAccess; + + size_t growth_left() { + size_t sz = 0; + for (const auto& set : sets_) + sz += set.growth_left(); + return sz; + } + + hasher& hash_ref() { return sets_[0].set_.hash_ref(); } + const hasher& hash_ref() const { return sets_[0].set_.hash_ref(); } + key_equal& eq_ref() { return sets_[0].set_.eq_ref(); } + const key_equal& eq_ref() const { return sets_[0].set_.eq_ref(); } + allocator_type& alloc_ref() { return sets_[0].set_.alloc_ref(); } + const allocator_type& alloc_ref() const { + return sets_[0].set_.alloc_ref(); + } + + std::array sets_; +}; + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +template class RefSet, + class Mtx_, + class Policy, class Hash, class Eq, class Alloc> +class parallel_hash_map : public parallel_hash_set +{ + // P is Policy. It's passed as a template argument to support maps that have + // incomplete types as values, as in unordered_map. + // MappedReference<> may be a non-reference type. + template + using MappedReference = decltype(P::value( + std::addressof(std::declval()))); + + // MappedConstReference<> may be a non-reference type. + template + using MappedConstReference = decltype(P::value( + std::addressof(std::declval()))); + + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + + using Base = typename parallel_hash_map::parallel_hash_set; + using Lockable = phmap::LockableImpl; + +public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + template + using key_arg = typename KeyArgImpl::template type; + + static_assert(!std::is_reference::value, ""); + // TODO(alkis): remove this assertion and verify that reference mapped_type is + // supported. + static_assert(!std::is_reference::value, ""); + + using iterator = typename parallel_hash_map::parallel_hash_set::iterator; + using const_iterator = typename parallel_hash_map::parallel_hash_set::const_iterator; + + parallel_hash_map() {} + +#ifdef __INTEL_COMPILER + using Base::parallel_hash_set; +#else + using parallel_hash_map::parallel_hash_set::parallel_hash_set; +#endif + + // The last two template parameters ensure that both arguments are rvalues + // (lvalue arguments are handled by the overloads below). This is necessary + // for supporting bitfield arguments. + // + // union { int n : 1; }; + // flat_hash_map m; + // m.insert_or_assign(n, n); + template + std::pair insert_or_assign(key_arg&& k, V&& v) { + return insert_or_assign_impl(std::forward(k), std::forward(v)); + } + + template + std::pair insert_or_assign(key_arg&& k, const V& v) { + return insert_or_assign_impl(std::forward(k), v); + } + + template + std::pair insert_or_assign(const key_arg& k, V&& v) { + return insert_or_assign_impl(k, std::forward(v)); + } + + template + std::pair insert_or_assign(const key_arg& k, const V& v) { + return insert_or_assign_impl(k, v); + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, V&& v) { + return insert_or_assign(std::forward(k), std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, const V& v) { + return insert_or_assign(std::forward(k), v).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, V&& v) { + return insert_or_assign(k, std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, const V& v) { + return insert_or_assign(k, v).first; + } + + template ::value, int>::type = 0, + K* = nullptr> + std::pair try_emplace(key_arg&& k, Args&&... args) { + return try_emplace_impl(std::forward(k), std::forward(args)...); + } + + template ::value, int>::type = 0> + std::pair try_emplace(const key_arg& k, Args&&... args) { + return try_emplace_impl(k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator, key_arg&& k, Args&&... args) { + return try_emplace(std::forward(k), std::forward(args)...).first; + } + + template + iterator try_emplace(const_iterator, const key_arg& k, Args&&... args) { + return try_emplace(k, std::forward(args)...).first; + } + + template + MappedReference

at(const key_arg& key) { + auto it = this->find(key); + if (it == this->end()) + phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key"); + return Policy::value(&*it); + } + + template + MappedConstReference

at(const key_arg& key) const { + auto it = this->find(key); + if (it == this->end()) + phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key"); + return Policy::value(&*it); + } + + // ----------- phmap extensions -------------------------- + + // if map contains key, lambda is called with the mapped value (under read lock protection), + // and if_contains returns true. This is a const API and lambda should not modify the value + // ----------------------------------------------------------------------------------------- + template + bool if_contains(const key_arg& key, F&& f) const { + return const_cast(this)->template + modify_if_impl(key, std::forward(f)); + } + + // if map contains key, lambda is called with the mapped value (under write lock protection), + // and modify_if returns true. This is a non-const API and lambda is allowed to modify the mapped value + // ---------------------------------------------------------------------------------------------------- + template + bool modify_if(const key_arg& key, F&& f) { + return modify_if_impl(key, std::forward(f)); + } + + // if map does not contains key, it is inserted and the mapped value is value-constructed + // with the provided arguments (if any), as with try_emplace. + // Then the lambda is called with the mapped value (under write lock protection) and can + // update the mapped value. + // --------------------------------------------------------------------------------------- + template + bool try_emplace_l(K&& k, F&& f, Args&&... args) { + typename Lockable::UniqueLock m; + auto res = this->find_or_prepare_insert(k, m); + typename Base::Inner *inner = std::get<0>(res); + if (std::get<2>(res)) + inner->set_.emplace_at(std::get<1>(res), std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + else { + auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res))); + std::forward(f)(Policy::value(&*it)); + } + return std::get<2>(res); + } + + // ----------- end of phmap extensions -------------------------- + + template + MappedReference

operator[](key_arg&& key) { + return Policy::value(&*try_emplace(std::forward(key)).first); + } + + template + MappedReference

operator[](const key_arg& key) { + return Policy::value(&*try_emplace(key).first); + } + +private: + template + bool modify_if_impl(const key_arg& key, F&& f) { +#if __cplusplus >= 201703L + static_assert(std::is_invocable::value); +#endif + L m; + auto it = this->template find(key, this->hash(key), m); + if (it == this->end()) + return false; + std::forward(f)(Policy::value(&*it)); + return true; + } + + template + std::pair insert_or_assign_impl(K&& k, V&& v) { + typename Lockable::UniqueLock m; + auto res = this->find_or_prepare_insert(k, m); + typename Base::Inner *inner = std::get<0>(res); + if (std::get<2>(res)) + inner->set_.emplace_at(std::get<1>(res), std::forward(k), std::forward(v)); + else + Policy::value(&*inner->set_.iterator_at(std::get<1>(res))) = std::forward(v); + return {this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res))), + std::get<2>(res)}; + } + + template + std::pair try_emplace_impl(K&& k, Args&&... args) { + typename Lockable::UniqueLock m; + auto res = this->find_or_prepare_insert(k, m); + typename Base::Inner *inner = std::get<0>(res); + if (std::get<2>(res)) + inner->set_.emplace_at(std::get<1>(res), std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + return {this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res))), + std::get<2>(res)}; + } +}; + + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +// ---------------------------------------------------------------------------- +template +void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { + memory_internal::ConstructFromTupleImpl( + alloc, ptr, std::forward(t), + phmap::make_index_sequence< + std::tuple_size::type>::value>()); +} + +// Constructs T using the args specified in the tuple and calls F with the +// constructed value. +// ---------------------------------------------------------------------------- +template +decltype(std::declval()(std::declval())) WithConstructed( + Tuple&& t, F&& f) { + return memory_internal::WithConstructedImpl( + std::forward(t), + phmap::make_index_sequence< + std::tuple_size::type>::value>(), + std::forward(f)); +} + +// ---------------------------------------------------------------------------- +// Given arguments of an std::pair's consructor, PairArgs() returns a pair of +// tuples with references to the passed arguments. The tuples contain +// constructor arguments for the first and the second elements of the pair. +// +// The following two snippets are equivalent. +// +// 1. std::pair p(args...); +// +// 2. auto a = PairArgs(args...); +// std::pair p(std::piecewise_construct, +// std::move(p.first), std::move(p.second)); +// ---------------------------------------------------------------------------- +inline std::pair, std::tuple<>> PairArgs() { return {}; } + +template +std::pair, std::tuple> PairArgs(F&& f, S&& s) { + return {std::piecewise_construct, std::forward_as_tuple(std::forward(f)), + std::forward_as_tuple(std::forward(s))}; +} + +template +std::pair, std::tuple> PairArgs( + const std::pair& p) { + return PairArgs(p.first, p.second); +} + +template +std::pair, std::tuple> PairArgs(std::pair&& p) { + return PairArgs(std::forward(p.first), std::forward(p.second)); +} + +template +auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) + -> decltype(std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s)))) { + return std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s))); +} + +// A helper function for implementing apply() in map policies. +// ---------------------------------------------------------------------------- +template +auto DecomposePair(F&& f, Args&&... args) + -> decltype(memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...))) { + return memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...)); +} + +// A helper function for implementing apply() in set policies. +// ---------------------------------------------------------------------------- +template +decltype(std::declval()(std::declval(), std::declval())) +DecomposeValue(F&& f, Arg&& arg) { + const auto& key = arg; + return std::forward(f)(key, std::forward(arg)); +} + + +// -------------------------------------------------------------------------- +// Policy: a policy defines how to perform different operations on +// the slots of the hashtable (see hash_policy_traits.h for the full interface +// of policy). +// +// Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The +// functor should accept a key and return size_t as hash. For best performance +// it is important that the hash function provides high entropy across all bits +// of the hash. +// +// Eq: a (possibly polymorphic) functor that compares two keys for equality. It +// should accept two (of possibly different type) keys and return a bool: true +// if they are equal, false if they are not. If two keys compare equal, then +// their hash values as defined by Hash MUST be equal. +// +// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which +// the storage of the hashtable will be allocated and the elements will be +// constructed and destroyed. +// -------------------------------------------------------------------------- +template +struct FlatHashSetPolicy +{ + using slot_type = T; + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + phmap::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + phmap::allocator_traits::destroy(*alloc, slot); + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + construct(alloc, new_slot, std::move(*old_slot)); + destroy(alloc, old_slot); + } + + static T& element(slot_type* slot) { return *slot; } + + template + static decltype(phmap::priv::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return phmap::priv::DecomposeValue( + std::forward(f), std::forward(args)...); + } + + static size_t space_used(const T*) { return 0; } +}; + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +template +struct FlatHashMapPolicy +{ + using slot_policy = priv::map_slot_policy; + using slot_type = typename slot_policy::slot_type; + using key_type = K; + using mapped_type = V; + using init_type = std::pair; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + slot_policy::construct(alloc, slot, std::forward(args)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + slot_policy::destroy(alloc, slot); + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + slot_policy::transfer(alloc, new_slot, old_slot); + } + + template + static decltype(phmap::priv::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return phmap::priv::DecomposePair(std::forward(f), + std::forward(args)...); + } + + static size_t space_used(const slot_type*) { return 0; } + + static std::pair& element(slot_type* slot) { return slot->value; } + + static V& value(std::pair* kv) { return kv->second; } + static const V& value(const std::pair* kv) { return kv->second; } +}; + +template +struct node_hash_policy { + static_assert(std::is_lvalue_reference::value, ""); + + using slot_type = typename std::remove_cv< + typename std::remove_reference::type>::type*; + + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + *slot = Policy::new_element(alloc, std::forward(args)...); + } + + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::delete_element(alloc, *slot); + } + + template + static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) { + *new_slot = *old_slot; + } + + static size_t space_used(const slot_type* slot) { + if (slot == nullptr) return Policy::element_space_used(nullptr); + return Policy::element_space_used(*slot); + } + + static Reference element(slot_type* slot) { return **slot; } + + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + template + static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward(ts)...)) { + return P::apply(std::forward(ts)...); + } +}; + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +template +struct NodeHashSetPolicy + : phmap::priv::node_hash_policy> +{ + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static T* new_element(Allocator* alloc, Args&&... args) { + using ValueAlloc = + typename phmap::allocator_traits::template rebind_alloc; + ValueAlloc value_alloc(*alloc); + T* res = phmap::allocator_traits::allocate(value_alloc, 1); + phmap::allocator_traits::construct(value_alloc, res, + std::forward(args)...); + return res; + } + + template + static void delete_element(Allocator* alloc, T* elem) { + using ValueAlloc = + typename phmap::allocator_traits::template rebind_alloc; + ValueAlloc value_alloc(*alloc); + phmap::allocator_traits::destroy(value_alloc, elem); + phmap::allocator_traits::deallocate(value_alloc, elem, 1); + } + + template + static decltype(phmap::priv::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return phmap::priv::DecomposeValue( + std::forward(f), std::forward(args)...); + } + + static size_t element_space_used(const T*) { return sizeof(T); } +}; + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +template +class NodeHashMapPolicy + : public phmap::priv::node_hash_policy< + std::pair&, NodeHashMapPolicy> +{ + using value_type = std::pair; + +public: + using key_type = Key; + using mapped_type = Value; + using init_type = std::pair; + + template + static value_type* new_element(Allocator* alloc, Args&&... args) { + using PairAlloc = typename phmap::allocator_traits< + Allocator>::template rebind_alloc; + PairAlloc pair_alloc(*alloc); + value_type* res = + phmap::allocator_traits::allocate(pair_alloc, 1); + phmap::allocator_traits::construct(pair_alloc, res, + std::forward(args)...); + return res; + } + + template + static void delete_element(Allocator* alloc, value_type* pair) { + using PairAlloc = typename phmap::allocator_traits< + Allocator>::template rebind_alloc; + PairAlloc pair_alloc(*alloc); + phmap::allocator_traits::destroy(pair_alloc, pair); + phmap::allocator_traits::deallocate(pair_alloc, pair, 1); + } + + template + static decltype(phmap::priv::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return phmap::priv::DecomposePair(std::forward(f), + std::forward(args)...); + } + + static size_t element_space_used(const value_type*) { + return sizeof(value_type); + } + + static Value& value(value_type* elem) { return elem->second; } + static const Value& value(const value_type* elem) { return elem->second; } +}; + + +// -------------------------------------------------------------------------- +// hash_default +// -------------------------------------------------------------------------- + +#if PHMAP_HAVE_STD_STRING_VIEW + +// support char16_t wchar_t .... +template +struct StringHashT +{ + using is_transparent = void; + + size_t operator()(std::basic_string_view v) const { + std::string_view bv{reinterpret_cast(v.data()), v.size() * sizeof(CharT)}; + return std::hash()(bv); + } +}; + +// Supports heterogeneous lookup for basic_string-like elements. +template +struct StringHashEqT +{ + using Hash = StringHashT; + + struct Eq { + using is_transparent = void; + + bool operator()(std::basic_string_view lhs, std::basic_string_view rhs) const { + return lhs == rhs; + } + }; +}; + +template <> +struct HashEq : StringHashEqT {}; + +template <> +struct HashEq : StringHashEqT {}; + +// char16_t +template <> +struct HashEq : StringHashEqT {}; + +template <> +struct HashEq : StringHashEqT {}; + +// wchar_t +template <> +struct HashEq : StringHashEqT {}; + +template <> +struct HashEq : StringHashEqT {}; + +#endif + +// Supports heterogeneous lookup for pointers and smart pointers. +// ------------------------------------------------------------- +template +struct HashEq +{ + struct Hash { + using is_transparent = void; + template + size_t operator()(const U& ptr) const { + return phmap::Hash{}(HashEq::ToPtr(ptr)); + } + }; + + struct Eq { + using is_transparent = void; + template + bool operator()(const A& a, const B& b) const { + return HashEq::ToPtr(a) == HashEq::ToPtr(b); + } + }; + +private: + static const T* ToPtr(const T* ptr) { return ptr; } + + template + static const T* ToPtr(const std::unique_ptr& ptr) { + return ptr.get(); + } + + template + static const T* ToPtr(const std::shared_ptr& ptr) { + return ptr.get(); + } +}; + +template +struct HashEq> : HashEq {}; + +template +struct HashEq> : HashEq {}; + +namespace hashtable_debug_internal { + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +template +struct HashtableDebugAccess> +{ + using Traits = typename Set::PolicyTraits; + using Slot = typename Traits::slot_type; + + static size_t GetNumProbes(const Set& set, + const typename Set::key_type& key) { + size_t num_probes = 0; + size_t hashval = set.hash(key); + auto seq = set.probe(hashval); + while (true) { + priv::Group g{set.ctrl_ + seq.offset()}; + for (int i : g.Match(priv::H2(hashval))) { + if (Traits::apply( + typename Set::template EqualElement{ + key, set.eq_ref()}, + Traits::element(set.slots_ + seq.offset((size_t)i)))) + return num_probes; + ++num_probes; + } + if (g.MatchEmpty()) return num_probes; + seq.next(); + ++num_probes; + } + } + + static size_t AllocatedByteSize(const Set& c) { + size_t capacity = c.capacity_; + if (capacity == 0) return 0; + auto layout = Set::MakeLayout(capacity); + size_t m = layout.AllocSize(); + + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * c.size(); + } else { + for (size_t i = 0; i != capacity; ++i) { + if (priv::IsFull(c.ctrl_[i])) { + m += Traits::space_used(c.slots_ + i); + } + } + } + return m; + } + + static size_t LowerBoundAllocatedByteSize(size_t size) { + size_t capacity = GrowthToLowerboundCapacity(size); + if (capacity == 0) return 0; + auto layout = Set::MakeLayout(NormalizeCapacity(capacity)); + size_t m = layout.AllocSize(); + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * size; + } + return m; + } +}; + +} // namespace hashtable_debug_internal +} // namespace priv + +// ----------------------------------------------------------------------------- +// phmap::flat_hash_set +// ----------------------------------------------------------------------------- +// An `phmap::flat_hash_set` is an unordered associative container which has +// been optimized for both speed and memory footprint in most common use cases. +// Its interface is similar to that of `std::unordered_set` with the +// following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the set is provided a compatible heterogeneous +// hashing function and equality operator. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash set. +// * Returns `void` from the `_erase(iterator)` overload. +// ----------------------------------------------------------------------------- +template // default values in phmap_fwd_decl.h +class flat_hash_set + : public phmap::priv::raw_hash_set< + phmap::priv::FlatHashSetPolicy, Hash, Eq, Alloc> +{ + using Base = typename flat_hash_set::raw_hash_set; + +public: + flat_hash_set() {} +#ifdef __INTEL_COMPILER + using Base::raw_hash_set; +#else + using Base::Base; +#endif + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; // may shrink - To avoid shrinking `erase(begin(), end())` + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::hash; + using Base::key_eq; +}; + +// ----------------------------------------------------------------------------- +// phmap::flat_hash_map +// ----------------------------------------------------------------------------- +// +// An `phmap::flat_hash_map` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_map` with +// the following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `_erase(iterator)` overload. +// ----------------------------------------------------------------------------- +template // default values in phmap_fwd_decl.h +class flat_hash_map : public phmap::priv::raw_hash_map< + phmap::priv::FlatHashMapPolicy, + Hash, Eq, Alloc> { + using Base = typename flat_hash_map::raw_hash_map; + +public: + flat_hash_map() {} +#ifdef __INTEL_COMPILER + using Base::raw_hash_map; +#else + using Base::Base; +#endif + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::insert_or_assign; + using Base::emplace; + using Base::emplace_hint; + using Base::try_emplace; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::at; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::operator[]; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::hash; + using Base::key_eq; +}; + +// ----------------------------------------------------------------------------- +// phmap::node_hash_set +// ----------------------------------------------------------------------------- +// An `phmap::node_hash_set` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_set` with the +// following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash set. +// * Returns `void` from the `erase(iterator)` overload. +// ----------------------------------------------------------------------------- +template // default values in phmap_fwd_decl.h +class node_hash_set + : public phmap::priv::raw_hash_set< + phmap::priv::NodeHashSetPolicy, Hash, Eq, Alloc> +{ + using Base = typename node_hash_set::raw_hash_set; + +public: + node_hash_set() {} +#ifdef __INTEL_COMPILER + using Base::raw_hash_set; +#else + using Base::Base; +#endif + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::hash; + using Base::key_eq; + typename Base::hasher hash_funct() { return this->hash_function(); } + void resize(typename Base::size_type hint) { this->rehash(hint); } +}; + +// ----------------------------------------------------------------------------- +// phmap::node_hash_map +// ----------------------------------------------------------------------------- +// +// An `phmap::node_hash_map` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_map` with +// the following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `erase(iterator)` overload. +// ----------------------------------------------------------------------------- +template // default values in phmap_fwd_decl.h +class node_hash_map + : public phmap::priv::raw_hash_map< + phmap::priv::NodeHashMapPolicy, Hash, Eq, + Alloc> +{ + using Base = typename node_hash_map::raw_hash_map; + +public: + node_hash_map() {} +#ifdef __INTEL_COMPILER + using Base::raw_hash_map; +#else + using Base::Base; +#endif + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::insert_or_assign; + using Base::emplace; + using Base::emplace_hint; + using Base::try_emplace; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::at; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::operator[]; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::hash; + using Base::key_eq; + typename Base::hasher hash_funct() { return this->hash_function(); } + void resize(typename Base::size_type hint) { this->rehash(hint); } +}; + +// ----------------------------------------------------------------------------- +// phmap::parallel_flat_hash_set +// ----------------------------------------------------------------------------- +template // default values in phmap_fwd_decl.h +class parallel_flat_hash_set + : public phmap::priv::parallel_hash_set< + N, phmap::priv::raw_hash_set, Mtx_, + phmap::priv::FlatHashSetPolicy, + Hash, Eq, Alloc> +{ + using Base = typename parallel_flat_hash_set::parallel_hash_set; + +public: + parallel_flat_hash_set() {} +#ifdef __INTEL_COMPILER + using Base::parallel_hash_set; +#else + using Base::Base; +#endif + using Base::hash; + using Base::subidx; + using Base::subcnt; + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::key_eq; +}; + +// ----------------------------------------------------------------------------- +// phmap::parallel_flat_hash_map - default values in phmap_fwd_decl.h +// ----------------------------------------------------------------------------- +template +class parallel_flat_hash_map : public phmap::priv::parallel_hash_map< + N, phmap::priv::raw_hash_set, Mtx_, + phmap::priv::FlatHashMapPolicy, + Hash, Eq, Alloc> +{ + using Base = typename parallel_flat_hash_map::parallel_hash_map; + +public: + parallel_flat_hash_map() {} +#ifdef __INTEL_COMPILER + using Base::parallel_hash_map; +#else + using Base::Base; +#endif + using Base::hash; + using Base::subidx; + using Base::subcnt; + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::insert_or_assign; + using Base::emplace; + using Base::emplace_hint; + using Base::try_emplace; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::at; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::operator[]; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::key_eq; +}; + +// ----------------------------------------------------------------------------- +// phmap::parallel_node_hash_set +// ----------------------------------------------------------------------------- +template +class parallel_node_hash_set + : public phmap::priv::parallel_hash_set< + N, phmap::priv::raw_hash_set, Mtx_, + phmap::priv::NodeHashSetPolicy, Hash, Eq, Alloc> +{ + using Base = typename parallel_node_hash_set::parallel_hash_set; + +public: + parallel_node_hash_set() {} +#ifdef __INTEL_COMPILER + using Base::parallel_hash_set; +#else + using Base::Base; +#endif + using Base::hash; + using Base::subidx; + using Base::subcnt; + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::emplace; + using Base::emplace_hint; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::key_eq; + typename Base::hasher hash_funct() { return this->hash_function(); } + void resize(typename Base::size_type hint) { this->rehash(hint); } +}; + +// ----------------------------------------------------------------------------- +// phmap::parallel_node_hash_map +// ----------------------------------------------------------------------------- +template +class parallel_node_hash_map + : public phmap::priv::parallel_hash_map< + N, phmap::priv::raw_hash_set, Mtx_, + phmap::priv::NodeHashMapPolicy, Hash, Eq, + Alloc> +{ + using Base = typename parallel_node_hash_map::parallel_hash_map; + +public: + parallel_node_hash_map() {} +#ifdef __INTEL_COMPILER + using Base::parallel_hash_map; +#else + using Base::Base; +#endif + using Base::hash; + using Base::subidx; + using Base::subcnt; + using Base::begin; + using Base::cbegin; + using Base::cend; + using Base::end; + using Base::capacity; + using Base::empty; + using Base::max_size; + using Base::size; + using Base::clear; + using Base::erase; + using Base::insert; + using Base::insert_or_assign; + using Base::emplace; + using Base::emplace_hint; + using Base::try_emplace; + using Base::extract; + using Base::merge; + using Base::swap; + using Base::rehash; + using Base::reserve; + using Base::at; + using Base::contains; + using Base::count; + using Base::equal_range; + using Base::find; + using Base::operator[]; + using Base::bucket_count; + using Base::load_factor; + using Base::max_load_factor; + using Base::get_allocator; + using Base::hash_function; + using Base::key_eq; + typename Base::hasher hash_funct() { return this->hash_function(); } + void resize(typename Base::size_type hint) { this->rehash(hint); } +}; + +} // namespace phmap + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + +#endif // phmap_h_guard_ diff --git a/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_base.h b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_base.h new file mode 100644 index 00000000000..e8f4f839e2c --- /dev/null +++ b/third-party/mockturtle/lib/parallel_hashmap/parallel_hashmap/phmap_base.h @@ -0,0 +1,5171 @@ +#if !defined(phmap_base_h_guard_) +#define phmap_base_h_guard_ + +// --------------------------------------------------------------------------- +// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp) +// with modifications. +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// --------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for std::lock + +#include "phmap_config.h" + +#ifdef PHMAP_HAVE_SHARED_MUTEX + #include // after "phmap_config.h" +#endif + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4514) // unreferenced inline function has been removed + #pragma warning(disable : 4582) // constructor is not implicitly called + #pragma warning(disable : 4625) // copy constructor was implicitly defined as deleted + #pragma warning(disable : 4626) // assignment operator was implicitly defined as deleted + #pragma warning(disable : 4710) // function not inlined + #pragma warning(disable : 4711) // selected for automatic inline expansion + #pragma warning(disable : 4820) // '6' bytes padding added after data member +#endif // _MSC_VER + +namespace phmap { + +template using Allocator = typename std::allocator; + +template using Pair = typename std::pair; + +template +struct EqualTo +{ + inline bool operator()(const T& a, const T& b) const + { + return std::equal_to()(a, b); + } +}; + +template +struct Less +{ + inline bool operator()(const T& a, const T& b) const + { + return std::less()(a, b); + } +}; + +namespace type_traits_internal { + +template +struct VoidTImpl { + using type = void; +}; + +// This trick to retrieve a default alignment is necessary for our +// implementation of aligned_storage_t to be consistent with any implementation +// of std::aligned_storage. +// --------------------------------------------------------------------------- +template > +struct default_alignment_of_aligned_storage; + +template +struct default_alignment_of_aligned_storage> { + static constexpr size_t value = Align; +}; + +// NOTE: The `is_detected` family of templates here differ from the library +// fundamentals specification in that for library fundamentals, `Op` is +// evaluated as soon as the type `is_detected` undergoes +// substitution, regardless of whether or not the `::value` is accessed. That +// is inconsistent with all other standard traits and prevents lazy evaluation +// in larger contexts (such as if the `is_detected` check is a trailing argument +// of a `conjunction`. This implementation opts to instead be lazy in the same +// way that the standard traits are (this "defect" of the detection idiom +// specifications has been reported). +// --------------------------------------------------------------------------- + +template class Op, class... Args> +struct is_detected_impl { + using type = std::false_type; +}; + +template