├── .gitignore ├── CMakeLists.txt ├── Doxyfile ├── LICENSE ├── README.md ├── setup.py └── src ├── api ├── AbcInterfaceAPI.cpp └── api.cpp ├── global ├── constant.h ├── define.h ├── global.h ├── namespace.h ├── parameter.h └── type.h ├── interface ├── AbcInterface.cpp ├── AbcInterface.h └── abcIncl.h └── util ├── Assert.h ├── BasicTypeSelection.h ├── Box.h ├── GdsHelper.h ├── MsgPrinter.cpp ├── MsgPrinter.h ├── Polygon2Rect.h ├── Vector2D.h ├── XY.h ├── XYZ.h ├── kLibBase.h └── thirdparty ├── AlmostEquals.h ├── RTree.h ├── StdCapture.h └── cmdline.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | docs 3 | .ycm_extra_conf.py 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON ) 3 | set(CMAKE_VERBOSE_MAKEFILE ON) 4 | 5 | set(PROJECT_NAME "abc_py") 6 | project(${PROJECT_NAME}) 7 | 8 | 9 | #The project version number 10 | set(VERSION_MAJOR 0 CACHE STRING "Project major version number.") 11 | set(VERSION_MINOR 2 CACHE STRING "Project minor version number.") 12 | set(VERSION_PATCH 0 CACHE STRING "Project patch version number.") 13 | 14 | # output path 15 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin) 16 | set(DOCUMENT_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/docs) 17 | set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 18 | 19 | 20 | #Check if intall prefix flag is found, if not, set a default path 21 | if (CMAKE_INSTALL_PREFIX) 22 | set(CMAKE_INSTALL_PREFIX_DIR ${CMAKE_INSTALL_PREFIX}) 23 | else() 24 | set(CMAKE_INSTALL_PREFIX_DIR ${CMAKE_CURRENT_SOURCE_DIR}/install) 25 | endif() 26 | unset(CMAKE_INSTALL_PREFIX) 27 | 28 | 29 | message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") 30 | 31 | 32 | 33 | # Find the directory for ABC. If cache variable ABC_DIR is defined, use it as the path to ABC, otherwise use the system ABC_DIR 34 | if (ABC_DIR) 35 | set(ABC_ROOT_DIR ${ABC_DIR}) 36 | message(STATUS "Use ABC: ${ABC_ROOT_DIR}") 37 | else() 38 | set(ABC_ROOT_DIR $ENV{ABC_DIR}) 39 | message(STATUS "Use system ABC ${ABC_ROOT_DIR}") 40 | endif() 41 | unset (ABC_DIR CACHE) 42 | 43 | find_package(Boost 1.6 COMPONENTS system graph iostreams) 44 | 45 | # add a target to generate API documentation with Doxygen 46 | find_package(Doxygen) 47 | option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) 48 | 49 | if(BUILD_DOCUMENTATION) 50 | if(NOT DOXYGEN_FOUND) 51 | message(FATAL_ERROR "Doxygen is needed to build the documentation.") 52 | endif() 53 | 54 | set(doxy_main_page ${CMAKE_CURRENT_SOURCE_DIR}/Readme.md) 55 | set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile) 56 | set(doxyfile ${DOCUMENT_OUTPUT_PATH}/Doxyfile) 57 | 58 | #request to configure the file 59 | configure_file(${doxyfile_in} ${doxyfile} @ONLY) 60 | 61 | add_custom_target(doc 62 | COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} 63 | WORKING_DIRECTORY ${DOCUMENT_OUTPUT_PATH} 64 | COMMENT "Generating API documentation with Doxygen" 65 | VERBATIM) 66 | 67 | #install(DIRECTORY ${DOCUMENT_OUTPUT_PATH}/html DESTINATION ${CMAKE_INSTALL_PREFIX_DIR}/doc) 68 | endif() 69 | mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH) 70 | 71 | 72 | 73 | file(GLOB SOURCES src/global/*.h src/global/*.cpp 74 | src/interface/*.h src/interface/*.cpp 75 | src/util/*.h src/util/*.cpp 76 | src/util/thirdparty/*.h src/util/thirdparty/*.cpp 77 | ) 78 | 79 | file(GLOB EXE_SOURCES src/main/main.cpp) 80 | file(GLOB PY_API_SOURCES src/api/*.cpp) 81 | file(GLOB UNITTEST_SOURCES 82 | unittest/main/*.cpp 83 | unittest/db/*.cpp 84 | unittest/parser/*.cpp 85 | ${SOURCES}) 86 | 87 | #pybind11 88 | if (PYBIND11_DIR) 89 | set(PYBIND11_ROOT_DIR ${PYBIND11_DIR}) 90 | message(STATUS "Use pybind11: ${PYBIND11_ROOT_DIR}") 91 | else() 92 | set(PYBIND11_ROOT_DIR $ENV{PYBIND11_DIR}) 93 | message(STATUS "Use system pybind11: ${PYBIND11_ROOT_DIR}") 94 | endif() 95 | unset(PYBIND11_DIR CACHE) 96 | 97 | add_subdirectory(${PYBIND11_ROOT_DIR} "./pybind11") 98 | 99 | # Python 100 | if (NOT PYTHON_INCLUDE_DIR OR NOT PYTHON_LIBRARIES) 101 | find_package(PythonLibs REQUIRED) 102 | endif() 103 | #set(PYTHON_INCLUDE_DIR "/home/local/eda09/keren/libs/python-3.7.5-shared/include/python3.7m") 104 | #set(PYTHON_LIBRARIES "/home/local/eda09/keren/libs/python-3.7.5-shared/lib/libpython3.7m.a") 105 | message(STATUS "python include directory: ${PYTHON_INCLUDE_DIR}") 106 | message(STATUS "python libraries: ${PYTHON_LIBRARIES}") 107 | 108 | #Print out the used build type 109 | IF(CMAKE_BUILD_TYPE MATCHES Debug) 110 | message(STATUS "Using build type DEBUG") 111 | #ENDIF(CMAKE_BUILD_TYPE MATCHES Debug) 112 | ELSEIF(CMAKE_BUILD_TYPE MATCHES Release) 113 | message(STATUS "Using build type RELEASE") 114 | #ENDIF(CMAKE_BUILD_TYPE MATCHES Release) 115 | ELSEIF(CMAKE_BUILD_TYPE MATCHES Profile) 116 | message(STATUS "Using build type PROFILE") 117 | #ENDIF(CMAKE_BUILD_TYPE MATCHES Profile) 118 | ELSE() 119 | message(STATUS "Using build type DEFAULT: using Release flag") 120 | set(CMAKE_BUILD_TYPE Release) 121 | ENDIF() 122 | #set(CMAKE_CXX_COMPILER "/usr/bin/clang++") 123 | set(CMAKE_CXX_FLAGS "-std=c++14 -Wall -march=native -fopenmp -fPIC") 124 | #set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -march=native -fopenmp -fext-numeric-literals") 125 | set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -fno-inline ") 126 | #set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-inline") 127 | #set(CMAKE_CXX_FLAGS_RELEASE "-O3") 128 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -frename-registers -fprofile-use -fprofile-correction") 129 | set(CMAKE_CXX_FLAGS_PROFILE "-Ofast -pg -Winline") 130 | set(CMAKE_EXE_LINKER_FLAGS "-pthread -static-libgcc -static-libstdc++ -static") 131 | 132 | 133 | #add_executable(${PROJECT_NAME} ${SOURCES} ${EXE_SOURCES}) 134 | 135 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src 136 | ${CMAKE_CURRENT_SOURCE_DIR}/unittest 137 | ${Boost_INCLUDE_DIR} 138 | ${ZLIB_INCLUDE_DIRS} 139 | ${ABC_ROOT_DIR} 140 | ${ABC_ROOT_DIR}/src 141 | ) 142 | 143 | 144 | 145 | link_libraries ( 146 | ${GTEST_MAIN_LIB} 147 | ${PYTHON_LIBRARIES} 148 | ) 149 | 150 | # linking libraries 151 | file(GLOB STATIC_LIB 152 | ${ABC_ROOT_DIR}/libabc.a 153 | ) 154 | 155 | 156 | 157 | # Add modules to pybind 158 | pybind11_add_module("abc_py" ${PY_API_SOURCES} ${SOURCES}) 159 | target_link_libraries("abc_py" PUBLIC ${STATIC_LIB} ${Boost_LIBRARIES} ) 160 | 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Keren Zhu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | abc\_py 2 | -------- 3 | A simple Python interface for logic synthesis framework [abc](https://github.com/berkeley-abc/abc). 4 | You are welcome to cite ` Keren Zhu, Mingjie Liu, Hao Chen, Zheng Zhao and David Z. Pan,"Exploring Logic Optimizations with Reinforcement Learning and Graph Convolutional Network," 2nd ACM/IEEE Workshop on Machine Learning for CAD (MLCAD), Nov. 2020. ` 5 | 6 | 7 | 8 | -------- 9 | # Install 10 | 11 | # abc 12 | [abc](https://github.com/berkeley-abc/abc) 13 | 14 | abc\_py requires a static library for abc, and to make it shareable. `-fPIC` flag needs to be added to gcc or Clang. 15 | 16 | Then compile the abc 17 | ``` 18 | make ABC_USE_NO_READLINE=1 ABC_USE_PIC=1 libabc.a 19 | ``` 20 | 21 | Note that ABC is defining several macros at build. Please check their Makafile and define them too in CMakeLists.txt, otherwise there might be errors when including the headers. 22 | 23 | # pybind11 24 | [pybind11](https://github.com/pybind/pybind11) 25 | 26 | Please see the official [document](http://pybind11.readthedocs.org/en/master) for installing the pybind11. 27 | 28 | # Makefile Configure 29 | 30 | The project is tested with `cmake 3.13.2`. [cmake](https://cmake.org/) 31 | Export `CXX` to the compiler you like. The tested is made on `g++ 6.3.0 and 9.2.0`. 32 | 33 | Correctly set the path to abc directory. Either export to system variable `ABC_DIR` or add flag to cmake `cmake -DABC_DIR=`. 34 | Make sure the libabc.a is inside `${ABC_DIR}`. 35 | 36 | Set the path to pybind11. Either export to system variable `PYBIND11_DIR` or add flag to cmake `cmake -DPYBIND11_DIR=`. 37 | 38 | The cmake will automatically find the system Python. 39 | To use the other Python, add cmake flags `cmake -DPYTHON_INCLUDE_DIR= -DPYTHON_LIBRARIES=`. 40 | For example, `-DPYTHON_INCLUDE_DIR=/include/python3.7m -DPYTHON_LIBRARIES=/lib/libpython3.7m.a` 41 | 42 | It's also important to find the correct platform configuration for ABC (`error: #error unknown platform`). The ABC uses different data types for different data configuration. 43 | In the ABC software, there is a script `arch_flags` to identify the data types of your platform. It will gives the configuration, for example, `-DLIN64 -DSIZEOF_VOID_P=8 -DSIZEOF_LONG=8 -DSIZEOF_INT=4`, and those are the compile flags need to be included. They need be added to `CMAKE_CXX_FLAGS`. Please edit the `CMakeLists.txt`, and add the flags. An common example is `set(CMAKE_CXX_FLAGS "-DLIN64 -DSIZEOF_VOID_P=8 -DSIZEOF_LONG=8 -DSIZEOF_INT=4 -std=c++14 -Wall -march=native -fopenmp -fPIC") ` 44 | 45 | # Build 46 | ``` 47 | mkdir build 48 | cd build 49 | cmake .. 50 | make 51 | cd ../../ 52 | pip install abc_py/ 53 | ``` 54 | -------- 55 | # Usage 56 | 57 | `import abc_py` like the standard Python library. 58 | 59 | -------- 60 | # Acknolwedgement 61 | 62 | I want to thank binderwang and Zehua Pei for investigating some building issues of this software. 63 | 64 | -------- 65 | # Contact. 66 | Keren Zhu, The University of Texas at Austin 67 | [UTDA](https://www.cerc.utexas.edu/utda/) 68 | 69 | Please let me know if there is any issue or suggestions. 70 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import platform 5 | import subprocess 6 | 7 | from setuptools import setup, Extension 8 | from setuptools.command.build_ext import build_ext 9 | from distutils.version import LooseVersion 10 | 11 | 12 | class CMakeExtension(Extension): 13 | def __init__(self, name, sourcedir=''): 14 | Extension.__init__(self, name, sources=[]) 15 | self.sourcedir = os.path.abspath(sourcedir) 16 | 17 | 18 | class CMakeBuild(build_ext): 19 | def run(self): 20 | try: 21 | out = subprocess.check_output(['cmake', '--version']) 22 | except OSError: 23 | raise RuntimeError("CMake must be installed to build the following extensions: " + 24 | ", ".join(e.name for e in self.extensions)) 25 | 26 | if platform.system() == "Windows": 27 | cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) 28 | if cmake_version < '3.1.0': 29 | raise RuntimeError("CMake >= 3.1.0 is required on Windows") 30 | 31 | for ext in self.extensions: 32 | self.build_extension(ext) 33 | 34 | def build_extension(self, ext): 35 | extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) 36 | cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, 37 | '-DPYTHON_EXECUTABLE=' + sys.executable] 38 | 39 | cfg = 'Debug' if self.debug else 'Release' 40 | build_args = ['--config', cfg] 41 | 42 | if platform.system() == "Windows": 43 | cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] 44 | if sys.maxsize > 2**32: 45 | cmake_args += ['-A', 'x64'] 46 | build_args += ['--', '/m'] 47 | else: 48 | cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] 49 | build_args += ['--', '-j2'] 50 | 51 | env = os.environ.copy() 52 | env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), 53 | self.distribution.get_version()) 54 | if not os.path.exists(self.build_temp): 55 | os.makedirs(self.build_temp) 56 | subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) 57 | subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp) 58 | 59 | setup( 60 | name='abc_py', 61 | version='0.0.1', 62 | author='Keren Zhu', 63 | author_email='keren.zhu@utexas.edu', 64 | description='The python interface to ABC', 65 | long_description='', 66 | ext_modules=[CMakeExtension('abc_py')], 67 | cmdclass=dict(build_ext=CMakeBuild), 68 | zip_safe=False, 69 | ) 70 | -------------------------------------------------------------------------------- /src/api/AbcInterfaceAPI.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AbcInterfaceAPI.cpp 3 | * @brief The Python interface for the classes AbcInterface 4 | * @author Keren Zhu 5 | * @date 10/23/2019 6 | */ 7 | 8 | #include 9 | #include 10 | #include "interface/AbcInterface.h" 11 | 12 | namespace py = pybind11; 13 | void initAbcInterfaceAPI(py::module &m) 14 | { 15 | py::class_(m , "AbcInterface") 16 | .def(py::init<>()) 17 | .def("start", &PROJECT_NAMESPACE::AbcInterface::start, "Start the ABC framework") 18 | .def("end", &PROJECT_NAMESPACE::AbcInterface::end, "Stop the ABC framework") 19 | .def("read", &PROJECT_NAMESPACE::AbcInterface::read, "Read a file") 20 | .def("aigStats", &PROJECT_NAMESPACE::AbcInterface::aigStats, "Get the AIG stats from the ABC framework`") 21 | .def("balance", &PROJECT_NAMESPACE::AbcInterface::balance, "balance action", 22 | py::arg("l") = false, py::arg("d") = false, py::arg("s") = false, py::arg("x") = false) 23 | .def("resub", &PROJECT_NAMESPACE::AbcInterface::resub, "resub action", 24 | py::arg("k") = -1, py::arg("n") = -1, py::arg("f") = -1, 25 | py::arg("l") = false, py::arg("z") = false) 26 | .def("rewrite", &PROJECT_NAMESPACE::AbcInterface::rewrite, "rewrite action", 27 | py::arg("l") = false, py::arg("z") = false) 28 | .def("refactor", &PROJECT_NAMESPACE::AbcInterface::refactor, "refactor action", 29 | py::arg("n") = -1, py::arg("l") = false, py::arg("z") = false) 30 | .def("compress2rs", &PROJECT_NAMESPACE::AbcInterface::compress2rs) 31 | .def("aigNode", &PROJECT_NAMESPACE::AbcInterface::aigNode, "Get one AigNode") 32 | .def("numNodes", &PROJECT_NAMESPACE::AbcInterface::numNodes, "Get the number of nodes"); 33 | 34 | py::class_(m , "AigStats") 35 | .def(py::init<>()) 36 | .def_property("numIn", &PROJECT_NAMESPACE::AigStats::numIn, &PROJECT_NAMESPACE::AigStats::setNumIn) 37 | .def_property("numOut", &PROJECT_NAMESPACE::AigStats::numOut, &PROJECT_NAMESPACE::AigStats::setNumOut) 38 | .def_property("numLat", &PROJECT_NAMESPACE::AigStats::numLat, &PROJECT_NAMESPACE::AigStats::setNumLat) 39 | .def_property("numAnd", &PROJECT_NAMESPACE::AigStats::numAnd, &PROJECT_NAMESPACE::AigStats::setNumAnd) 40 | .def_property("lev", &PROJECT_NAMESPACE::AigStats::lev, &PROJECT_NAMESPACE::AigStats::setLev); 41 | 42 | py::class_(m, "AigNode") 43 | .def(py::init<>()) 44 | .def("hasFanin0", &PROJECT_NAMESPACE::AigNode::hasFanin0, "Whether the node has fanin0") 45 | .def("fanin0", &PROJECT_NAMESPACE::AigNode::fanin0, "The node index of fanin 0") 46 | .def("hasFanin1", &PROJECT_NAMESPACE::AigNode::hasFanin1, "Whether the node has fanin1") 47 | .def("fanin1", &PROJECT_NAMESPACE::AigNode::fanin1, "The node index of fanin 1") 48 | .def("nodeType", &PROJECT_NAMESPACE::AigNode::nodeType, "The node type. 0: const 1, 1: PO, 2: PI, 3: a and b, 4: not a and b, 5: not a and not b, 6 unknown") 49 | .def("numFanouts", &PROJECT_NAMESPACE::AigNode::numFanouts, "The number of fanouts") 50 | .def("fanout", &PROJECT_NAMESPACE::AigNode::fanout, "A fanout node"); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/api/api.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file api.cpp 3 | * @brief The top level cpp for initialize the pybind module 4 | * @author Keren Zhu 5 | * @date 10/23/2019 6 | */ 7 | 8 | #include 9 | #include 10 | #include "global/global.h" 11 | 12 | namespace py = pybind11; 13 | 14 | void initAbcInterfaceAPI(py::module &); 15 | 16 | PYBIND11_MAKE_OPAQUE(std::vector); 17 | 18 | PYBIND11_MODULE(abc_py, m) 19 | { 20 | initAbcInterfaceAPI(m); 21 | } 22 | -------------------------------------------------------------------------------- /src/global/constant.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file constant.h 3 | * @brief Define some constants 4 | * @author Keren Zhu 5 | * @date 09/30/2019 6 | */ 7 | 8 | #ifndef ABC_PY_CONSTANT_H_ 9 | #define ABC_PY_CONSTANT_H_ 10 | 11 | #include "type.h" 12 | 13 | PROJECT_NAMESPACE_BEGIN 14 | 15 | PROJECT_NAMESPACE_END 16 | 17 | #endif // ABC_PY_CONSTANT_H_ 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/global/define.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file define.h 3 | * @brief Define the flags 4 | * @author Keren Zhu 5 | * @date 09/30/2019 6 | */ 7 | 8 | #ifndef ABC_PY_DEFINE_H_ 9 | #define ABC_PY_DEFINE_H_ 10 | 11 | 12 | #endif /// ABC_PY_DEFINE_H 13 | -------------------------------------------------------------------------------- /src/global/global.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file global.h 3 | * @brief The header file intended to be included globally in the project 4 | * @author Keren Zhu 5 | * @date 09/30/2019 6 | */ 7 | 8 | #ifndef ABC_PY_GLOBAL_H_ 9 | #define ABC_PY_GLOBAL_H_ 10 | 11 | // Add all global/common header files here 12 | 13 | #include "type.h" 14 | #include "constant.h" 15 | #include "parameter.h" 16 | #include "util/MsgPrinter.h" 17 | #include "util/Assert.h" 18 | #include "util/kLibBase.h" 19 | 20 | PROJECT_NAMESPACE_BEGIN 21 | 22 | // Function aliases 23 | static const auto &INF = MsgPrinter::inf; 24 | static const auto &WRN = MsgPrinter::wrn; 25 | static const auto &ERR = MsgPrinter::err; 26 | static const auto &DBG = MsgPrinter::dbg; 27 | 28 | PROJECT_NAMESPACE_END 29 | 30 | #endif // ABC_PY_GLOBAL_H_ 31 | -------------------------------------------------------------------------------- /src/global/namespace.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file namespace.h 3 | * @brief Define the namespaces 4 | * @author Keren Zhu 5 | * @date 09/30/2019 6 | */ 7 | 8 | #ifndef ABC_PY_NAMESPACE_H_ 9 | #define ABC_PY_NAMESPACE_H_ 10 | 11 | #include "define.h" 12 | 13 | // Namespace of the project 14 | #define PROJECT_NAMESPACE ABC_PY 15 | #define PROJECT_NAMESPACE_BEGIN namespace PROJECT_NAMESPACE{ 16 | #define PROJECT_NAMESPACE_END } 17 | 18 | #endif // ABC_PY_NAMESPACE_H_ 19 | -------------------------------------------------------------------------------- /src/global/parameter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file parameter.h 3 | * @brief Define some hyperparameters 4 | * @author Keren Zhu 5 | * @date 09/30/2019 6 | */ 7 | 8 | #ifndef ABC_PY_PARAMETER_H_ 9 | #define ABC_PY_PARAMETER_H_ 10 | 11 | #include "type.h" 12 | 13 | PROJECT_NAMESPACE_BEGIN 14 | 15 | PROJECT_NAMESPACE_END 16 | 17 | #endif ///ABC_PY_PARAMETER_H_ 18 | -------------------------------------------------------------------------------- /src/global/type.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file type.h 3 | * @brief Define some the types being used globally 4 | * @author Keren Zhu 5 | * @date 09/30/2019 6 | */ 7 | 8 | #ifndef ABC_PY_TYPE_H_ 9 | #define ABC_PY_TYPE_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include "namespace.h" 15 | 16 | PROJECT_NAMESPACE_BEGIN 17 | // Built-in type aliases 18 | using IndexType = std::uint32_t; 19 | using IntType = std::int32_t; 20 | using RealType = double; 21 | using Byte = std::uint8_t; 22 | using LocType = std::int32_t; // Location/design unit // Location/design unit 23 | // Location/design unit 24 | // Built-in type constants 25 | constexpr IndexType INDEX_TYPE_MAX = UINT32_MAX; 26 | constexpr IntType INT_TYPE_MAX = INT32_MAX; 27 | constexpr IntType INT_TYPE_MIN = INT32_MIN; 28 | constexpr RealType REAL_TYPE_MAX = 1e100; 29 | constexpr RealType REAL_TYPE_MIN = -1e100; 30 | constexpr RealType REAL_TYPE_TOL = 1e-6; 31 | constexpr LocType LOC_TYPE_MAX = INT32_MAX; 32 | constexpr LocType LOC_TYPE_MIN = INT32_MIN; 33 | 34 | // Type aliases 35 | //using CostTy = double; 36 | using CostType = RealType; 37 | constexpr CostType COST_TYPE_INVALID = REAL_TYPE_MIN; 38 | constexpr CostType COST_TYPE_MAX = REAL_TYPE_MAX; 39 | 40 | 41 | // Enums 42 | 43 | 44 | 45 | 46 | PROJECT_NAMESPACE_END 47 | 48 | #endif // ABC_PY_TYPE_H_ 49 | 50 | -------------------------------------------------------------------------------- /src/interface/AbcInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "AbcInterface.h" 2 | 3 | 4 | #if defined(ABC_NAMESPACE) 5 | namespace ABC_NAMESPACE 6 | { 7 | #elif defined(__cplusplus) 8 | extern "C" 9 | { 10 | #endif 11 | 12 | // procedures to start and stop the ABC framework 13 | // (should be called before and after the ABC procedures are called) 14 | void Abc_Start(); 15 | void Abc_Stop(); 16 | 17 | // procedures to get the ABC framework and execute commands in it 18 | //typedef struct Abc_Frame_t_ Abc_Frame_t; 19 | 20 | Abc_Frame_t_ * Abc_FrameGetGlobalFrame(); 21 | int Cmd_CommandExecute( Abc_Frame_t_ * pAbc, const char * sCommand ); 22 | 23 | 24 | #if defined(ABC_NAMESPACE) 25 | } 26 | using namespace ABC_NAMESPACE; 27 | #elif defined(__cplusplus) 28 | } 29 | #endif 30 | 31 | PROJECT_NAMESPACE_BEGIN 32 | 33 | 34 | /// @brief strip the dash after the string and return the integer the string represent 35 | IntType stripDash(std::string word) 36 | { 37 | auto pos = word.rfind('/'); 38 | while (pos != std::string::npos) 39 | { 40 | word = word.substr(0, pos); 41 | pos = word.rfind('/'); 42 | } 43 | return ::atoi(word.c_str()); 44 | } 45 | 46 | /// @brief strip the dash after the string and return the integer the string represent 47 | IntType stripEqual(std::string word) 48 | { 49 | auto pos = word.find('='); 50 | while (pos != std::string::npos) 51 | { 52 | word = word.substr(pos+1); 53 | pos = word.find('='); 54 | } 55 | return ::atoi(word.c_str()); 56 | } 57 | 58 | void AbcInterface::start() 59 | { 60 | // start the ABC framework 61 | Abc_Start(); 62 | _pAbc = Abc_FrameGetGlobalFrame(); 63 | // Std streams capture 64 | //_stdCap.Init(); 65 | } 66 | 67 | void AbcInterface::end() 68 | { 69 | // stop t;he ABC framework 70 | Abc_Stop(); 71 | } 72 | 73 | bool AbcInterface::read(const std::string &filename) 74 | { 75 | auto beginClk = clock(); 76 | char Command[1000]; 77 | // read the file 78 | sprintf( Command, "read %s", filename.c_str() ); 79 | if ( Cmd_CommandExecute( _pAbc, Command ) ) 80 | { 81 | ERR("Cannot execute command \"%s\".\n", Command ); 82 | return false; 83 | } 84 | // Default do a strash 85 | sprintf( Command, "strash" ); 86 | if ( Cmd_CommandExecute( _pAbc, Command ) ) 87 | { 88 | ERR("Cannot execute command \"%s\".\n", Command ); 89 | return false; 90 | } 91 | auto endClk = clock(); 92 | _lastClk = beginClk - endClk; 93 | this->updateGraph(); 94 | return true; 95 | 96 | } 97 | 98 | bool AbcInterface::balance(bool l, bool d, bool s, bool x) 99 | { 100 | std::string cmd = "balance"; 101 | if (l) 102 | { 103 | cmd += " -l "; 104 | } 105 | if (d) 106 | { 107 | cmd += " -d "; 108 | } 109 | if (s) 110 | { 111 | cmd += " -s "; 112 | } 113 | if (x) 114 | { 115 | cmd += " -x "; 116 | } 117 | auto beginClk = clock(); 118 | if ( Cmd_CommandExecute( _pAbc, cmd.c_str() ) ) 119 | { 120 | ERR("Cannot execute command \"%s\".\n", cmd.c_str() ); 121 | return false; 122 | } 123 | auto endClk = clock(); 124 | _lastClk = beginClk - endClk; 125 | return true; 126 | } 127 | 128 | bool AbcInterface::resub(IntType k, IntType n, IntType f, bool l, bool z) 129 | { 130 | std::string cmd = "resub"; 131 | if (k != -1) 132 | { 133 | cmd += " -K "; 134 | cmd += std::to_string(k); 135 | } 136 | if (n != -1) 137 | { 138 | cmd += " -N "; 139 | cmd += std::to_string(n); 140 | } 141 | if (f != -1) 142 | { 143 | cmd += " -F "; 144 | cmd += std::to_string(f); 145 | } 146 | if (l) 147 | { 148 | cmd += " -l "; 149 | } 150 | if (z) 151 | { 152 | cmd += " -z "; 153 | } 154 | auto beginClk = clock(); 155 | if ( Cmd_CommandExecute( _pAbc, cmd.c_str() ) ) 156 | { 157 | ERR("Cannot execute command \"%s\".\n", cmd.c_str() ); 158 | return false; 159 | } 160 | auto endClk = clock(); 161 | _lastClk = beginClk - endClk; 162 | return true; 163 | } 164 | 165 | bool AbcInterface::rewrite(bool l, bool z) 166 | { 167 | std::string cmd = "rewrite"; 168 | if (l) 169 | { 170 | cmd += " -l "; 171 | } 172 | if (z) 173 | { 174 | cmd += " -z "; 175 | } 176 | auto beginClk = clock(); 177 | if ( Cmd_CommandExecute( _pAbc, cmd.c_str() ) ) 178 | { 179 | ERR("Cannot execute command \"%s\".\n", cmd.c_str() ); 180 | return false; 181 | } 182 | auto endClk = clock(); 183 | _lastClk = beginClk - endClk; 184 | return true; 185 | } 186 | 187 | bool AbcInterface::refactor(IntType n, bool l, bool z) 188 | { 189 | std::string cmd = "refactor"; 190 | if (n != -1) 191 | { 192 | cmd += " -N "; 193 | cmd += std::to_string(n); 194 | } 195 | if (l) 196 | { 197 | cmd += " -l "; 198 | } 199 | if (z) 200 | { 201 | cmd += " -z "; 202 | } 203 | auto beginClk = clock(); 204 | if ( Cmd_CommandExecute( _pAbc, cmd.c_str() ) ) 205 | { 206 | ERR("Cannot execute command \"%s\".\n", cmd.c_str() ); 207 | return false; 208 | } 209 | auto endClk = clock(); 210 | _lastClk = beginClk - endClk; 211 | return true; 212 | } 213 | 214 | bool AbcInterface::compress2rs() 215 | { 216 | 217 | // "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 218 | if (!this->balance(true)) { return false; } 219 | if (!this->resub(6, -1, -1, true, false)) { return false; } 220 | if (!this->rewrite(true, false)) { return false; } 221 | if (!this->resub(6, 2, -1, true, false)) { return false; } 222 | if (!this->refactor(-1, true, false)) { return false; } 223 | if (!this->resub(8, -1, -1, true, false)) { return false; } 224 | if (!this->balance(true, false, false,false)) { return false; } 225 | if (!this->resub(8, 2, -1, true, false)) { return false; } 226 | if (!this->rewrite(true, false)) { return false; } 227 | if (!this->resub(10, -1, -1, true, false)) { return false; } 228 | if (!this->rewrite(true, true)) { return false; } 229 | if (!this->resub(10, 2, -1, true, false)) { return false; } 230 | if (!this->balance(true, false, false, false)) { return false; } 231 | if (!this->resub(12, -1, -1, true, false)) { return false; } 232 | if (!this->refactor(-1, true, true)) { return false; } 233 | if (!this->resub(12, 2, -1, true, false)) { return false; } 234 | if (!this->rewrite(true, true)) { return false; } 235 | if (!this->balance(true, false, false, false)) { return false; } 236 | return true; 237 | } 238 | 239 | IntType AbcInterface::numNodes() 240 | { 241 | IntType nObj = _pAbc->pNtkCur->nObjs; 242 | return nObj; 243 | } 244 | 245 | void AbcInterface::updateGraph() 246 | { 247 | _numAigAnds = 0; 248 | _depth = -1; 249 | _numPO = 0; 250 | _numPI = 0; 251 | _numConst = 0; 252 | 253 | _aigNodes.resize(this->numNodes()); 254 | //DBG("update graph: num of nodes %d \n", this->numNodes()); 255 | 256 | for (IntType idx = 0; idx < this->numNodes(); ++idx) 257 | { 258 | auto pObj = (Abc_Obj_t*)_pAbc->pNtkCur->vObjs->pArray[idx]; 259 | if (pObj->Level > _depth) 260 | { 261 | _depth = pObj->Level; 262 | } 263 | if (pObj->Type == ABC_OBJ_CONST1) 264 | { 265 | _numConst++; 266 | } 267 | else if (pObj->Type == ABC_OBJ_PI) 268 | { 269 | _numPI++; 270 | } 271 | else if (pObj->Type == ABC_OBJ_PO) 272 | { 273 | _numPO++; 274 | } 275 | else if (pObj->Type == ABC_OBJ_NODE) 276 | { 277 | _numAigAnds++; 278 | } 279 | else 280 | { 281 | AssertMsg(false, "Unexpected node type %d \n", pObj->Type); 282 | } 283 | // Configure the AIG node maintained 284 | _aigNodes[idx].configureNodeFromAbc(pObj); 285 | } 286 | } 287 | 288 | AigStats AbcInterface::aigStats() 289 | { 290 | this->updateGraph(); 291 | AigStats stats; 292 | stats.setNumIn(_numPI); 293 | stats.setNumOut(_numPO); 294 | stats.setNumAnd(_numAigAnds); 295 | stats.setLev(_depth); 296 | return stats; 297 | 298 | char Command[1000]; 299 | sprintf( Command, "print_stats" ); 300 | 301 | // Trick to capture the stdout from ABC print 302 | char string[10000]; 303 | freopen("/dev/null", "a", stdout); 304 | setbuf(stdout, string); 305 | 306 | if ( Cmd_CommandExecute( _pAbc, Command ) ) 307 | { 308 | ERR("Cannot execute command \"%s\".\n", Command ); 309 | } 310 | fflush(stdout); 311 | std::string str(string); 312 | // Process the string 313 | std::stringstream ss(str); 314 | //DBG("%s: get %s \n", __FUNCTION__, string); 315 | std::vector words; 316 | std::string token; 317 | while (ss >> token) 318 | { 319 | words.emplace_back(token); 320 | } 321 | for (IndexType idx = 0; idx < words.size(); ++idx) 322 | { 323 | if (words.at(idx) == "i/o") 324 | { 325 | IntType numIn = stripDash(words.at(idx + 2)); 326 | IntType numOut = std::stoi(words.at(idx + 3)); 327 | idx = idx + 4; 328 | stats.setNumIn(numIn); 329 | stats.setNumOut(numOut); 330 | } 331 | if (words.at(idx) == "lat") 332 | { 333 | IntType numLat = std::stoi(words.at(idx + 2)); 334 | idx = idx + 3; 335 | stats.setNumLat(numLat); 336 | } 337 | if (words.at(idx) == "and") 338 | { 339 | IntType numAnd = std::stoi(words.at(idx + 2)); 340 | idx = idx + 3; 341 | stats.setNumAnd(numAnd); 342 | } 343 | if (words.at(idx) == "lev") 344 | { 345 | IndexType numLev; 346 | if (words.at(idx + 1).length() > 1) 347 | { 348 | numLev = stripEqual(words.at(idx + 1)); 349 | idx = idx + 2; 350 | } 351 | else 352 | { 353 | numLev = std::stoi(words.at(idx + 2)); 354 | idx = idx + 3; 355 | } 356 | stats.setLev(numLev); 357 | break; 358 | } 359 | } 360 | return stats; 361 | } 362 | 363 | PROJECT_NAMESPACE_END 364 | -------------------------------------------------------------------------------- /src/interface/AbcInterface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AbcInterface.h 3 | * @brief The interface to ABC 4 | * @author Keren Zhu 5 | * @date 10/23/2019 6 | */ 7 | 8 | #ifndef ABC_PY_ABC_INTERFACE_H_ 9 | #define ABC_PY_ABC_INTERFACE_H_ 10 | 11 | #include "global/global.h" 12 | #include 13 | #include 14 | 15 | PROJECT_NAMESPACE_BEGIN 16 | 17 | /// @class ABC_PY::AigStats 18 | /// @brief stats of current design in AIG format 19 | class AigStats 20 | { 21 | public: 22 | explicit AigStats() = default; 23 | IndexType numIn() const { return _numIn; } 24 | IndexType numOut() const { return _numOut; } 25 | IndexType numLat() const { return _numLat; } 26 | IndexType numAnd() const { return _numAnd; } 27 | IndexType lev() const { return _lev; } 28 | 29 | void setNumIn(IndexType numIn) { _numIn = numIn; } 30 | void setNumOut(IndexType numOut) { _numOut = numOut; } 31 | void setNumLat(IndexType numLat) { _numLat = numLat; } 32 | void setNumAnd(IndexType numAnd) { _numAnd = numAnd; } 33 | void setLev(IndexType lev) { _lev = lev; } 34 | private: 35 | IndexType _numIn = 0; ///< Input port 36 | IndexType _numOut = 0; ///< Output port 37 | IndexType _numLat = 0; ///< Number of latches 38 | IndexType _numAnd = 0; ///< Number of AND 39 | IndexType _lev = 0; ///< The deepest logic level 40 | }; 41 | 42 | 43 | // object types 44 | typedef enum { 45 | AIG_NODE_CONST1= 0, // 0: constant 1 node 46 | AIG_NODE_PO, // 1: primary output terminal 47 | AIG_NODE_PI, // 2: primary input terminal 48 | AIG_NODE_NONO, // 3: fanin 0: no inverter fanin 1: no inv 49 | AIG_NODE_INVNO, // 4: fanin 0: has inverter fanin 1: no inverter 50 | AIG_NODE_INVINV, // 5: fanin 0: has inverter fanin 1: has inverter 51 | AIG_NODE_NUMBER // 6: unused 52 | } AigNodeType; 53 | 54 | /// @class ABC_PY::AigNode 55 | /// @brief Single AigNode of the graph. Basically a entry in adjacent list representation 56 | class AigNode 57 | { 58 | public: 59 | /// @brief default constructor 60 | explicit AigNode() = default; 61 | /// @brief whether has fanin 0 62 | /// @return if has fanin 0 63 | bool hasFanin0() { return _fanin0 != -1; } 64 | /// @brief get the index of fanin 0 node 65 | /// @return the index of fanin 0 node 66 | IntType fanin0() { AssertMsg(hasFanin0(), "The node does not has fanin 0!\n"); return _fanin0; } 67 | /// @brief whether has fanin 1 68 | /// @return if has fanin 1 69 | bool hasFanin1() { return _fanin1 != -1; } 70 | /// @brief get the index of fanin 1 node 71 | /// @return the index of fanin 1 node 72 | IntType fanin1() { AssertMsg(hasFanin1(), "The node does not has fanin 1!\n"); return _fanin1; } 73 | /// @brief Get number of fanouts 74 | /// @reutn number of fanouts 75 | IntType numFanouts() { return _fanouts.size(); } 76 | /// @brief Get the fanout node 77 | /// @param the index of nodes saved in this node 78 | /// @return the fanout node index in the network 79 | IntType fanout(IntType idx) 80 | { 81 | AssertMsg(idx < numFanouts(), "The node only has %d fanouts, but ask for %d-th \n", numFanouts(), idx); 82 | return _fanouts[idx]; 83 | } 84 | /// @brief Add one fanout node 85 | /// @param The index of the fanout node in the network 86 | void addFanout(IntType nodeIdx) { _fanouts.emplace_back(nodeIdx); } 87 | /// @brief Set the type of the node 88 | /// @param The type of the node. The type of defined in AigNodeType enum 89 | void setNodeType(IntType nodeType) { _nodeType = nodeType; } 90 | /// @brief Get the type of the node 91 | /// @param The type of the node. 92 | IntType nodeType() 93 | { 94 | AssertMsg(_nodeType != AIG_NODE_NUMBER, "Node type is unknown! \n"); 95 | return _nodeType; 96 | } 97 | /// @brief Configure the node with Abc_Obj_t 98 | /// @param Pointer to Abc_Obj_t 99 | void configureNodeFromAbc(Abc_Obj_t *pObj); 100 | private: 101 | IntType _fanin0 = -1; ///< The fanin 0. -1 if no fanin 0 102 | IntType _fanin1 = -1; ///< The fanin 1. -1 if no fanin 1 103 | std::vector _fanouts; ///< Indices to fanout nodes 104 | IntType _nodeType = 6; ///< The type of this node 105 | }; 106 | 107 | inline void AigNode::configureNodeFromAbc(Abc_Obj_t *pObj) 108 | { 109 | if (pObj->Type == ABC_OBJ_CONST1) 110 | { 111 | _nodeType = AIG_NODE_CONST1; 112 | } 113 | else if (pObj->Type == ABC_OBJ_PI) 114 | { 115 | _nodeType = AIG_NODE_PI; 116 | IntType numFanouts = pObj->vFanouts.nSize; 117 | _fanouts.resize(numFanouts); 118 | for (IntType fanout = 0; fanout < numFanouts; ++fanout) 119 | { 120 | IntType idx = pObj->vFanouts.pArray[fanout]; 121 | _fanouts[fanout] = idx; 122 | } 123 | } 124 | else if (pObj->Type == ABC_OBJ_PO) 125 | { 126 | _nodeType = AIG_NODE_PO; 127 | IntType numFanin = pObj->vFanins.nSize; 128 | AssertMsg(numFanin == 1, "PO node has %d fanin \n", numFanin); 129 | _fanin0 = pObj->vFanins.pArray[0]; 130 | } 131 | else if (pObj->Type == ABC_OBJ_NODE) 132 | { 133 | // Determine the type based on whether has inverters, and set fanin 134 | if (pObj->fCompl0 == 0 && pObj->fCompl1 == 0) 135 | { 136 | _nodeType = AIG_NODE_NONO; 137 | _fanin0 = pObj->vFanins.pArray[0]; 138 | _fanin1 = pObj->vFanins.pArray[1]; 139 | } 140 | else if (pObj->fCompl0 == 1 && pObj->fCompl1 == 0) 141 | { 142 | _nodeType = AIG_NODE_INVNO; 143 | _fanin0 = pObj->vFanins.pArray[0]; 144 | _fanin1 = pObj->vFanins.pArray[1]; 145 | } 146 | else if (pObj->fCompl0 == 0 && pObj->fCompl1 == 1) 147 | { 148 | _nodeType = AIG_NODE_INVNO; 149 | _fanin0 = pObj->vFanins.pArray[1]; 150 | _fanin1 = pObj->vFanins.pArray[0]; 151 | } 152 | else if (pObj->fCompl0 == 1 && pObj->fCompl1 == 1) 153 | { 154 | _nodeType = AIG_NODE_INVINV; 155 | _fanin0 = pObj->vFanins.pArray[0]; 156 | _fanin1 = pObj->vFanins.pArray[1]; 157 | } 158 | else 159 | { 160 | AssertMsg(false, "Unknown fanin complement type \n"); 161 | } 162 | // Fanout can be anything... 163 | IntType numFanouts = pObj->vFanouts.nSize; 164 | _fanouts.resize(numFanouts); 165 | for (IntType fanout = 0; fanout < numFanouts; ++fanout) 166 | { 167 | IntType idx = pObj->vFanouts.pArray[fanout]; 168 | _fanouts[fanout] = idx; 169 | } 170 | } 171 | else 172 | { 173 | AssertMsg(false, "Unexpected node type %d \n", pObj->Type); 174 | } 175 | } 176 | 177 | /// @class ABC_PY::AbcInterface 178 | /// @brief the interface to ABC 179 | class AbcInterface 180 | { 181 | public: 182 | explicit AbcInterface() = default; 183 | /*------------------------------*/ 184 | /* Start and stop the framework */ 185 | /*------------------------------*/ 186 | /// @brief start the ABC framework 187 | void start(); 188 | /// @brief end the ABC framework 189 | void end(); 190 | /// @brief read a file 191 | /// @param filename 192 | /// @return if successful 193 | bool read(const std::string & filename); 194 | /*------------------------------*/ 195 | /* Take actions */ 196 | /*------------------------------*/ 197 | /// @brief balance. transforms the current network into a well-balanced AIG 198 | /// @param first -l toggle minimizing the number of levels [default = yes] 199 | /// @param second -d : toggle duplication of logic [default = no] 200 | /// @param third -s : toggle duplication on the critical paths [default = no] 201 | /// @param fourth -x : toggle balancing multi-input EXORs [default = no] 202 | /// @return if successful 203 | bool balance(bool l = false, bool d = false, bool s = false, bool x = false); 204 | /// @brief resub. performs technology-independent restructuring of the AIG 205 | /// @param first: -K : the max cut size (4 <= num <= 16) [default = 8]. -1 then no flag 206 | /// @param second: -N : the max number of nodes to add (0 <= num <= 3) [default = 1]. -1 then no flag 207 | /// @param third: -F : the number of fanout levels for ODC computation [default = 0]. -1 then no flag 208 | /// @param fourth: -l : toggle preserving the number of levels [default = yes] 209 | /// @param fifth: -z : toggle using zero-cost replacements [default = no] 210 | /// @return if successful 211 | bool resub(IntType k = -1, IntType n = -1, IntType f = -1, bool l = false, bool z = false); 212 | /// @brief rewrite. performs technology-independent rewriting of the AIG 213 | /// @param first: -l : toggle preserving the number of levels [default = yes] 214 | /// @param second: -z : toggle using zero-cost replacements [default = no] 215 | /// @return if successful 216 | bool rewrite(bool l = false, bool z = false); 217 | /// @brief refactor: performs technology-independent refactoring of the AIG 218 | /// @param first: -N : the max support of the collapsed node [default = 10]. -1 then no flag 219 | /// @param second: -l : toggle preserving the number of levels [default = yes] 220 | /// @param third: -z : toggle using zero-cost replacements [default = no] 221 | /// @return if successful 222 | bool refactor(IntType n = -1, bool l = false, bool z = false); 223 | /*------------------------------*/ 224 | /* Baselines */ 225 | /*------------------------------*/ 226 | /// @brief 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 227 | /// @return if successful 228 | bool compress2rs(); 229 | /*------------------------------*/ 230 | /* Query the information */ 231 | /*------------------------------*/ 232 | /// @brief get the design AIG stats from ABC 233 | /// @return the AIG stats from ABC 234 | AigStats aigStats(); 235 | /// @brief get the number of nodes (aig + PI + PO) 236 | /// @return the number of total nodes 237 | IntType numNodes(); 238 | /// @brief update the graph 239 | void updateGraph(); 240 | /// @brief Get one AigNode 241 | /// @param The index of AigNode 242 | /// @return The AigNode 243 | AigNode & aigNode(IntType nodeIdx) 244 | { 245 | AssertMsg(nodeIdx < this->numNodes(), "Access node out of range %d / %d \n", nodeIdx, this->numNodes()); 246 | return _aigNodes[nodeIdx]; 247 | } 248 | 249 | private: 250 | Abc_Frame_t_ * _pAbc = nullptr; ///< The pointer to the ABC framework 251 | RealType _lastClk; ///< The time of last operation 252 | IntType _numAigAnds = -1; ///< Number of AIG AND nodes 253 | IntType _depth = -1; ///< The depth of the AIG network 254 | IntType _numPI = -1; ///< Number of PIs of the AIG network 255 | IntType _numPO = -1; ///< Number of POs of the AIG network 256 | IntType _numConst = -1; ///< Number of CONST of the AIG network 257 | std::vector _aigNodes; ///< The current AIG network nodes 258 | }; 259 | 260 | PROJECT_NAMESPACE_END 261 | 262 | #endif //ABC_PY_ABC_INTERFACE_H_ 263 | -------------------------------------------------------------------------------- /src/interface/abcIncl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AbcIncl.h 3 | * @brief The essential components need to be included in this frameowrk. Codes copy from original abc 4 | * @author Keren Zhu 5 | * @date 11/15/2019 6 | */ 7 | 8 | #ifndef ABC_PY_ABC_INCL_H_ 9 | #define ABC_PY_ABC_INCL_H_ 10 | 11 | 12 | #if defined(ABC_NAMESPACE) 13 | namespace ABC_NAMESPACE 14 | { 15 | #elif defined(__cplusplus) 16 | extern "C" 17 | { 18 | #endif 19 | 20 | // procedures to start and stop the ABC framework 21 | // (should be called before and after the ABC procedures are called) 22 | void Abc_Start(); 23 | void Abc_Stop(); 24 | 25 | // procedures to get the ABC framework and execute commands in it 26 | typedef struct Abc_Frame_t_ Abc_Frame_t; 27 | 28 | Abc_Frame_t * Abc_FrameGetGlobalFrame(); 29 | int Cmd_CommandExecute( Abc_Frame_t * pAbc, const char * sCommand ); 30 | 31 | 32 | #if defined(ABC_NAMESPACE) 33 | } 34 | using namespace ABC_NAMESPACE; 35 | #elif defined(__cplusplus) 36 | } 37 | #endif 38 | 39 | #endif //ABC_PY_ABC_INCL_H_ 40 | -------------------------------------------------------------------------------- /src/util/Assert.h: -------------------------------------------------------------------------------- 1 | #ifndef ZKUTIL_ASSERT_H_ 2 | #define ZKUTIL_ASSERT_H_ 3 | 4 | 5 | #ifndef NDEBUG 6 | 7 | #include 8 | #include 9 | #include "MsgPrinter.h" 10 | #include 11 | 12 | 13 | // Assert without message 14 | #define Assert(cond) \ 15 | do { \ 16 | if (!(cond)) \ 17 | { \ 18 | char format[1024]; \ 19 | sprintf(format, "Assertion failed at file %s line %d. \n assert: %s\n", __FILE__, __LINE__, #cond); \ 20 | MsgPrinter::err(format); \ 21 | assert(0); \ 22 | } \ 23 | } while(false) 24 | 25 | // Assert with message 26 | #define AssertMsg(cond, msg, ...) \ 27 | do { \ 28 | if (!(cond)) \ 29 | { \ 30 | char prefix[1024]; \ 31 | sprintf(prefix, "Assertion failed at file %s line %d.\n assert: %s\n", __FILE__, __LINE__, #cond); \ 32 | std::string format = std::string(prefix) + " message: " + std::string(msg); \ 33 | MsgPrinter::err(format.c_str(), ##__VA_ARGS__); \ 34 | assert(0); \ 35 | } \ 36 | } while (false) 37 | #else // NDEBUG 38 | #define Assert(cond) \ 39 | do { \ 40 | } while(false) 41 | #define AssertMsg(cond, msg, ...) \ 42 | do { \ 43 | } while (false) 44 | #endif // NDEBUG 45 | #endif // ZKTUIL_ASSERT_H_ 46 | -------------------------------------------------------------------------------- /src/util/BasicTypeSelection.h: -------------------------------------------------------------------------------- 1 | #ifndef __BASICTYPESELECTION_H__ 2 | #define __BASICTYPESELECTION_H__ 3 | 4 | #include 5 | 6 | // This file contains basic type selection utilities using meta-programming features of C++ 7 | // One common case that needs basic type selection is that when we have template function accepts two basic types. 8 | // Considering the following function. 9 | // 10 | // template 11 | // (RETURN_TYPE) add(T1 a, T2 b) 12 | // { 13 | // return a + b; 14 | // } 15 | // 16 | // What should be the RETURN_TYPE here? We cannot tell it should be T1 or T2 without knowing what are they 17 | // For example, if T1 is int and T2 is double, then the return type should be a double (T2). 18 | // However, if T1 is long, T2 is int, the return type should be long (T1). 19 | // 20 | // So we need a way to tell, between two types, which one has larger range, and we should return that type. 21 | // For integer types with same range, like std::int8_t and std::uint8_t, we define the signed version has larger rank 22 | 23 | namespace MetaProg 24 | { 25 | 26 | // Struct to get the rank of a given type T 27 | template 28 | struct TypeRank; 29 | 30 | // Template specialization to rank different types by their range from low to high 31 | template <> struct TypeRank {static constexpr std::uint32_t value = 0; }; 32 | template <> struct TypeRank {static constexpr std::uint32_t value = 1; }; 33 | template <> struct TypeRank {static constexpr std::uint32_t value = 2; }; 34 | template <> struct TypeRank {static constexpr std::uint32_t value = 3; }; 35 | template <> struct TypeRank {static constexpr std::uint32_t value = 4; }; 36 | template <> struct TypeRank {static constexpr std::uint32_t value = 5; }; 37 | template <> struct TypeRank {static constexpr std::uint32_t value = 6; }; 38 | template <> struct TypeRank {static constexpr std::uint32_t value = 7; }; 39 | template <> struct TypeRank {static constexpr std::uint32_t value = 8; }; 40 | template <> struct TypeRank {static constexpr std::uint32_t value = 9; }; 41 | 42 | // Struct to get the type of a given rank R 43 | template 44 | struct RankType; 45 | 46 | // Template specialization to map different ranks back to the corresponding types 47 | template <> struct RankType<0> { using type = std::uint8_t; }; 48 | template <> struct RankType<1> { using type = std::int8_t; }; 49 | template <> struct RankType<2> { using type = std::uint16_t; }; 50 | template <> struct RankType<3> { using type = std::int16_t; }; 51 | template <> struct RankType<4> { using type = std::uint32_t; }; 52 | template <> struct RankType<5> { using type = std::int32_t; }; 53 | template <> struct RankType<6> { using type = std::uint64_t; }; 54 | template <> struct RankType<7> { using type = std::int8_t; }; 55 | template <> struct RankType<8> { using type = float; }; 56 | template <> struct RankType<9> { using type = double; }; 57 | 58 | // Function that returns the max between two ranks 59 | template 60 | constexpr T maxRank(const T &r1, const T &r2) { return (r1 > r2 ? r1 : r2); } 61 | 62 | // Function to choose between two types 63 | // Choose the one with higher rank 64 | template 65 | struct selectBasicType 66 | { 67 | static constexpr std::uint32_t r1 = TypeRank::value; 68 | static constexpr std::uint32_t r2 = TypeRank::value; 69 | static constexpr std::uint32_t max = maxRank(r1, r2); 70 | using type = typename RankType::type; 71 | }; 72 | 73 | } // End of namespace MetaProg 74 | 75 | 76 | #endif // __BASICTYPESELECTION_H__ 77 | -------------------------------------------------------------------------------- /src/util/Box.h: -------------------------------------------------------------------------------- 1 | #ifndef __BOX_H__ 2 | #define __BOX_H__ 3 | 4 | #include 5 | #include 6 | #include "global/namespace.h" 7 | #include "XY.h" 8 | 9 | PROJECT_NAMESPACE_BEGIN 10 | 11 | template 12 | class Box 13 | { 14 | public: 15 | explicit Box() = default; 16 | explicit Box(const XY &p) : _ll(p), _ur(p) {} 17 | explicit Box(const XY &ll, const XY &ur) : _ll(ll), _ur(ur) {} 18 | explicit Box(T xLo, T yLo, T xHi, T yHi) : _ll(xLo, yLo), _ur(xHi, yHi) {} 19 | 20 | // Getters 21 | T xLo() const { return _ll.x(); } 22 | T yLo() const { return _ll.y(); } 23 | T xHi() const { return _ur.x(); } 24 | T yHi() const { return _ur.y(); } 25 | T xLen() const { return _ur.x() - _ll.x(); } 26 | T yLen() const { return _ur.y() - _ll.y(); } 27 | const XY & ll() const { return _ll; } 28 | const XY & ur() const { return _ur; } 29 | const XY center() const { return XY((_ll.x() + _ur.x()) / 2 , (_ll.y() + _ur.y()) / 2); } 30 | bool valid() const { return _ll.x() <= _ur.x() && _ll.y() <= _ur.y(); } 31 | T area() const { return (_ur.x() - _ll.x()) * (_ur.y() - _ll.y()); } 32 | 33 | // Setters 34 | void set(T xLo, T yLo, T xHi, T yHi) { _ll.setXY(xLo, yLo); _ur.setXY(xHi, yHi); } 35 | void setXLo(T xLo) { _ll.setX(xLo); } 36 | void setYLo(T yLo) { _ll.setY(yLo); } 37 | void setXHi(T xHi) { _ur.setX(xHi); } 38 | void setYHi(T yHi) { _ur.setY(yHi); } 39 | void join(const XY &pt); 40 | 41 | // String conversion 42 | operator std::string() const { 43 | std::ostringstream oss; 44 | oss <<"Box"<<":"; 45 | oss << "x = [" << xLo() << "," << xHi() << "];"; 46 | oss << "y = [" << yLo() << "," << yHi() << "]:"; 47 | return oss.str(); 48 | } 49 | 50 | bool operator==(const Box &rhs) const { return this->ll() == rhs.ll() && this->ur() == rhs.ur(); } 51 | 52 | std::string toStr() const { return std::string(*this); } 53 | 54 | // Check if this box contains a given (x, y) coordinate 55 | template 56 | bool contain(U x, U y) const { return ((T)x >= _ll.x() && (T)x <= _ur.x() && (T)y >= _ll.y() && (T)y <= _ur.y()); } 57 | 58 | // Check if this box contains a given point 59 | template 60 | bool contain(const XY &p) const { return ((T)p.x() >= _ll.x() && (T)p.x() <= _ur.x() && (T)p.y() >= _ll.y() && (T)p.y() <= _ur.y()); } 61 | 62 | /// @breif check if this box contain another box 63 | bool contain(const Box &other) const { return this->xLo() <= other.xLo() && this->yLo() <= other.yLo() && this->xHi() >= other.xHi() && this->yHi() >= other.yHi(); } 64 | 65 | T hpwl() const { return xHi() - xLo() + yHi() - yLo(); } 66 | 67 | /// @brief Check if this Box is overlapped with another Box 68 | bool overlap(const Box &other) const; 69 | /// @brief Check if this box is intersected with another box 70 | bool intersect(const Box &other) const; 71 | 72 | /// @brief Check if the box is cover another box 73 | bool cover(const Box &other) const { return this->contain(other.ll()) && this->contain(other.ur()); } 74 | 75 | /// @brief Make this Box become the union of the original and another box 76 | void unionBox(const Box &other); 77 | 78 | /// @brief Return the offset box 79 | Box offsetBox(const XY &origin) const { return Box(xLo() + origin.x(), yLo() + origin.y(), xHi() + origin.x(), yHi() + origin.y()); } 80 | void offsetBy(const XY &origin) { setXLo(xLo() + origin.x()); setYLo(yLo() + origin.y()); setXHi(xHi() + origin.x()); setYHi(yHi() + origin.y()); } 81 | 82 | /// @brief Enlarge the boundary of the box by a number 83 | /// @param The distance for enlarging 84 | void enlargeBy(T dis) { setXLo(xLo() - dis); setYLo(yLo() - dis); setXHi(xHi() + dis); setYHi(yHi() + dis); } 85 | private: 86 | XY _ll; // Lower left corner of this box 87 | XY _ur; // Upper right corner of this box 88 | }; 89 | 90 | /// @brief Extend the box to cover the given point 91 | template 92 | inline void Box::join(const XY &pt) 93 | { 94 | _ll.setX(std::min(pt.x(), _ll.x())); 95 | _ll.setY(std::min(pt.y(), _ll.y())); 96 | _ur.setX(std::max(pt.x(), _ur.x())); 97 | _ur.setY(std::max(pt.y(), _ur.y())); 98 | } 99 | 100 | template 101 | inline bool Box::overlap(const Box &other) const 102 | { 103 | if (_ll.x() >= other.xHi() || other.xLo() >= _ur.x()) 104 | { 105 | return false; 106 | } 107 | if (_ll.y() >= other.yHi() || other.yLo() >= _ur.y()) 108 | { 109 | return false; 110 | } 111 | return true; 112 | } 113 | 114 | template 115 | inline bool Box::intersect(const Box &other) const 116 | { 117 | if (_ll.x() > other.xHi() || other.xLo() > _ur.x()) 118 | { 119 | return false; 120 | } 121 | if (_ll.y() > other.yHi() || other.yLo() > _ur.y()) 122 | { 123 | return false; 124 | } 125 | return true; 126 | } 127 | 128 | template 129 | inline void Box::unionBox(const Box &other) 130 | { 131 | this->setXLo(std::min(this->xLo(), other.xLo())); 132 | this->setYLo(std::min(this->yLo(), other.yLo())); 133 | this->setXHi(std::max(this->xHi(), other.xHi())); 134 | this->setYHi(std::max(this->yHi(), other.yHi())); 135 | } 136 | 137 | namespace klib { 138 | /// @brief determine whether two boxes are parallel overlapped 139 | template 140 | inline bool boxAreParallelOverlap(const Box &lhs, const Box &rhs) 141 | { 142 | if (lhs.xLo() >= rhs.xHi() || rhs.xLo() >= lhs.xHi()) 143 | { 144 | if (lhs.yLo() < rhs.yHi() && rhs.yLo() < lhs.yHi()) 145 | { 146 | return true; 147 | } 148 | } 149 | if (lhs.yLo() >= rhs.yHi() || rhs.yLo() >= lhs.yHi()) 150 | { 151 | if (lhs.xLo() < rhs.xHi() && rhs.xLo() < lhs.xHi()) 152 | { 153 | return true; 154 | } 155 | } 156 | return false; 157 | } 158 | /// @brief compute the parallel run length between two boxes 159 | template 160 | inline T boxParallelRun(const Box &lhs, const Box &rhs) 161 | { 162 | if (lhs.xLo() >= rhs.xHi() || rhs.xLo() >= lhs.xHi()) 163 | { 164 | if (lhs.yLo() < rhs.yHi() && rhs.yLo() < lhs.yHi()) 165 | { 166 | return std::min(lhs.yHi(), rhs.yHi()) - std::max(lhs.yLo(), rhs.yLo()); 167 | } 168 | } 169 | if (lhs.yLo() >= rhs.yHi() || rhs.yLo() >= lhs.yHi()) 170 | { 171 | if (lhs.xLo() < rhs.xHi() && rhs.xLo() < lhs.xHi()) 172 | { 173 | return std::min(lhs.xHi(), rhs.xHi()) - std::max(lhs.xLo(), rhs.xLo()); 174 | } 175 | } 176 | return 0; 177 | } 178 | /// @brief compute the spacing between two boxes 179 | template 180 | inline T boxSpacing(const Box &lhs, const Box &rhs) 181 | { 182 | if (lhs.xLo() >= rhs.xHi() || rhs.xLo() >= lhs.xHi()) 183 | { 184 | if (lhs.yLo() < rhs.yHi() && rhs.yLo() < lhs.yHi()) 185 | { 186 | return std::max(lhs.xLo(), rhs.xLo()) - std::min(lhs.xHi(), rhs.xHi()); 187 | } 188 | else 189 | { 190 | // If no parallel run and two shapes are disjointed, then count the spacing as the distance between one _ur and one _ll 191 | double xDif = std::min(lhs.xHi(), rhs.xHi()) - std::max(lhs.xLo(), rhs.xLo()); 192 | double yDif = std::min(lhs.yHi(), rhs.yHi()) - std::max(lhs.yLo(), rhs.yLo()); 193 | return static_cast(std::hypot(xDif, yDif)); 194 | } 195 | } 196 | if (lhs.yLo() >= rhs.yHi() || rhs.yLo() >= lhs.yHi()) 197 | { 198 | if (lhs.xLo() < rhs.xHi() && rhs.xLo() < lhs.xHi()) 199 | { 200 | return std::max(lhs.yLo(), rhs.yLo()) - std::min(lhs.yHi(), rhs.yHi()); 201 | } 202 | } 203 | // Overlap 204 | return 0; 205 | } 206 | /// @brief compute the width of two box, in y or in x depending on their parallel run overlap 207 | template 208 | inline std::pair boxWidth(const Box &lhs, const Box &rhs) 209 | { 210 | if (lhs.xLo() >= rhs.xHi() || rhs.xLo() >= lhs.xHi()) 211 | { 212 | if (lhs.yLo() < rhs.yHi() && rhs.yLo() < lhs.yHi()) 213 | { 214 | return std::make_pair(lhs.xLen(), rhs.xLen()); 215 | } 216 | } 217 | if (lhs.yLo() >= rhs.yHi() || rhs.yLo() >= lhs.yHi()) 218 | { 219 | if (lhs.xLo() < rhs.xHi() && rhs.xLo() < lhs.xHi()) 220 | { 221 | return std::make_pair(lhs.yLen(), rhs.yLen()); 222 | } 223 | } 224 | return std::make_pair(0, 0); 225 | } 226 | 227 | /// @brief whether the overlapping of the two boxes are still an rectangle 228 | template 229 | inline bool boxUnionRectangle(const Box &lhs, const Box & rhs) 230 | { 231 | // If the union is one of the input, then return true 232 | if (lhs.cover(rhs) || rhs.cover(lhs)) 233 | { 234 | return true; 235 | } 236 | // Otherwise check the two edges of the union 237 | if (lhs.xLo() == rhs.xLo() && lhs.xHi() == rhs.xHi()) 238 | { 239 | if (lhs.yLo() >= rhs.yHi() || rhs.yLo() >= lhs.yHi()) 240 | { 241 | return true; 242 | } 243 | } 244 | if (lhs.yLo() == rhs.yLo() && lhs.yHi() == rhs.yHi()) 245 | { 246 | if (lhs.xLo() >= rhs.xHi() || rhs.xLo() >= lhs.xHi()) 247 | { 248 | return true; 249 | } 250 | } 251 | return false; 252 | } 253 | } 254 | 255 | PROJECT_NAMESPACE_END 256 | 257 | 258 | #endif // __BOX_H__ 259 | -------------------------------------------------------------------------------- /src/util/GdsHelper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file GdsHelper.h 3 | * @brief Use utilities of the Gds db 4 | * @author Keren Zhu 5 | * @date 02/07/2019 6 | */ 7 | 8 | #ifndef ZKUTIL_GDS_HELPER_H_ 9 | #define ZKUTIL_GDS_HELPER_H_ 10 | 11 | #include 12 | #include 13 | #include "global/global.h" 14 | 15 | namespace klib 16 | { 17 | 18 | /// @brief detailed struct for the functions of processing gds shapes 19 | namespace GetSRefNameActionDetails 20 | { 21 | 22 | /// @brief default type 23 | template 24 | inline void getSName(std::string &name, ObjectType *object) 25 | { 26 | } 27 | 28 | /// @brief SREF type 29 | template<> 30 | inline void getSName(std::string &name, ::GdsParser::GdsDB::GdsCellReference *object) 31 | { 32 | name = object->refCell(); 33 | } 34 | } 35 | 36 | /// @brief aution function object to get the cell reference name 37 | struct GetSRefNameAction 38 | { 39 | /// @param A reference to the string to record the name of the sref 40 | GetSRefNameAction(std::string &name) : _name(name) {} 41 | template 42 | void operator()(::GdsParser::GdsRecords::EnumType type, ObjectType* object) 43 | { 44 | GetSRefNameActionDetails::getSName(_name, object); 45 | } 46 | 47 | /// @return a message of action for debug 48 | std::string message() const 49 | { 50 | return "GetSRefNameAction"; 51 | } 52 | 53 | 54 | std::string &_name; ///< The cell reference name 55 | }; 56 | /// @brief find the top cell of the db 57 | /// @param a gds db 58 | /// @return the name of the top cell 59 | inline std::string topCell(const ::GdsParser::GdsDB::GdsDB & db) 60 | { 61 | // Whether each cell is found as the subcell of the other 62 | std::map nameFound; 63 | // Iterate all the cells and record their names 64 | // Add reversely 65 | for (int idx = db.cells().size() - 1; idx >= 0; idx--) 66 | { 67 | nameFound[db.cells().at(idx).name()] = false; 68 | } 69 | for (auto &cell : db.cells()) 70 | { 71 | for (auto obj : cell.objects()) 72 | { 73 | std::string name = ""; 74 | ::GdsParser::GdsDB::GdsObjectHelpers()(obj.first, obj.second, GetSRefNameAction(name)); 75 | if (name != "") 76 | { 77 | nameFound[name] = true; 78 | } 79 | } 80 | } 81 | // Return the name that was not included in the other cells 82 | for (auto pair : nameFound) 83 | { 84 | if (pair.first != "" && !pair.second) 85 | { 86 | return pair.first; 87 | } 88 | } 89 | 90 | AssertMsg(0 ,"Reading Gds::%s: all cells are referenced in other cells! \n", __FUNCTION__); 91 | return ""; 92 | } 93 | } 94 | #endif //ZKUTIL_GDS_HELPER_H_ 95 | -------------------------------------------------------------------------------- /src/util/MsgPrinter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Keren on 04/12/2018 3 | // 4 | 5 | #include "MsgPrinter.h" 6 | #include "Assert.h" 7 | 8 | PROJECT_NAMESPACE_BEGIN 9 | 10 | std::time_t MsgPrinter::_startTime = std::time(nullptr); 11 | FILE* MsgPrinter::_screenOutStream = stderr; 12 | FILE* MsgPrinter::_logOutStream = nullptr; 13 | std::string MsgPrinter::_logFileName = ""; 14 | 15 | /// Converting enum type to std::string 16 | std::string msgTypeToStr(MsgType msgType) 17 | { 18 | switch (msgType) 19 | { 20 | case MsgType::INF: return "INF"; break; 21 | case MsgType::WRN: return "WRN"; break; 22 | case MsgType::ERR: return "ERR"; break; 23 | case MsgType::DBG: return "DBG"; break; 24 | } 25 | AssertMsg(false, "Unknown MsgType. \n"); 26 | } 27 | 28 | /// Open a log file, all output will be stored in the log 29 | void MsgPrinter::openLogFile(const std::string &logFileName) 30 | { 31 | if (_logOutStream != nullptr) 32 | { 33 | fclose(_logOutStream); 34 | wrn("Current log file %s is forcibly closed\n", _logFileName.c_str()); 35 | } 36 | 37 | _logFileName = logFileName; 38 | _logOutStream = fopen(logFileName.c_str(), "w"); 39 | 40 | if (_logOutStream == nullptr) 41 | { 42 | err("Cannot open log file %s\n", logFileName.c_str()); 43 | } 44 | else { 45 | inf("Open log file %s\n", logFileName.c_str()); 46 | } 47 | } 48 | 49 | /// Close current log file 50 | void MsgPrinter::closeLogFile() 51 | { 52 | if (_logOutStream == nullptr) 53 | { 54 | wrn("No log file is opened. Call to %s is ignored.\n", __func__); 55 | } 56 | else 57 | { 58 | inf("Close log file %s.\n", _logFileName.c_str()); 59 | } 60 | } 61 | 62 | /// Print information 63 | void MsgPrinter::inf(const char* rawFormat, ...) 64 | { 65 | va_list args; 66 | va_start(args, rawFormat); 67 | print(MsgType::INF, rawFormat, args); 68 | va_end(args); 69 | } 70 | 71 | /// Print Warnings 72 | void MsgPrinter::wrn(const char* rawFormat, ...) 73 | { 74 | va_list args; 75 | va_start(args, rawFormat); 76 | print(MsgType::WRN, rawFormat, args); 77 | va_end(args); 78 | } 79 | 80 | ///Print errors 81 | void MsgPrinter::err(const char* rawFormat, ...) 82 | { 83 | va_list args; 84 | va_start(args, rawFormat); 85 | print(MsgType::ERR, rawFormat, args); 86 | va_end(args); 87 | } 88 | 89 | /// Print debugging information 90 | void MsgPrinter::dbg(const char* rawFormat, ...) 91 | { 92 | va_list args; 93 | va_start(args, rawFormat); 94 | print(MsgType::DBG, rawFormat, args); 95 | va_end(args); 96 | } 97 | 98 | /// Message printing kernel 99 | void MsgPrinter::print(MsgType msgType, const char* rawFormat, va_list args) 100 | { 101 | /// Get the message type 102 | std::string type = "[" + msgTypeToStr(msgType); 103 | 104 | /// Get local time and elapsed time 105 | struct tm* timeInfo; 106 | std::time_t now = std::time(nullptr); 107 | timeInfo = localtime(&now); 108 | double elapsed = difftime(now, _startTime); 109 | 110 | /// Local time 111 | char locTime[32]; 112 | strftime(locTime, 32, " %F %T ", timeInfo); 113 | 114 | /// Elapsed time 115 | char elpTime[32]; 116 | sprintf(elpTime, "%5.0lf sec] ", elapsed); 117 | 118 | // Combine all the strings together 119 | std::string format = type + std::string(locTime) + std::string(elpTime) + std::string(rawFormat); 120 | 121 | // print to log 122 | if (_logOutStream) 123 | { 124 | va_list args_copy; 125 | va_copy(args_copy, args); 126 | vfprintf(_logOutStream, format.c_str(), args_copy); 127 | va_end(args_copy); 128 | fflush(_logOutStream); 129 | } 130 | 131 | // print to screen 132 | if (_screenOutStream) 133 | { 134 | vfprintf(_screenOutStream, format.c_str(), args); 135 | fflush(_screenOutStream); 136 | } 137 | } 138 | PROJECT_NAMESPACE_END 139 | -------------------------------------------------------------------------------- /src/util/MsgPrinter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Keren on 04/12/2018 3 | // 4 | 5 | 6 | #ifndef ZKUTIL_MSGPRINTER_H_ 7 | #define ZKUTIL_MSGPRINTER_H_ 8 | 9 | #include // FILE *, stderr 10 | #include 11 | #include 12 | #include 13 | #include "global/namespace.h" 14 | 15 | PROJECT_NAMESPACE_BEGIN 16 | 17 | /// ================================================================================ 18 | /// MsgPrinter, used to manage printing message into the log or on the screen 19 | /// Copied and implemented based on Wuxi's 20 | /// ================================================================================ 21 | 22 | 23 | /// Enum type for message printing 24 | enum class MsgType 25 | { 26 | INF, 27 | WRN, 28 | ERR, 29 | DBG 30 | }; 31 | 32 | /// Function converting enum type to std::string 33 | std::string msgTypeToStr(MsgType msgType); 34 | 35 | /// Message printing class 36 | class MsgPrinter 37 | { 38 | public: 39 | static void startTimer() { _startTime = std::time(nullptr); } // Cache start time 40 | static void screenOn() { _screenOutStream = stderr; } // Turn on screen printing 41 | static void screenOff() { _screenOutStream = nullptr; } // Turn off screen printing 42 | 43 | static void openLogFile(const std::string &file); 44 | static void closeLogFile(); 45 | static void inf(const char *rawFormat, ...); 46 | static void wrn(const char *rawFormat, ...); 47 | static void err(const char *rawFormat, ...); 48 | static void dbg(const char *rawFormat, ...); 49 | 50 | private: 51 | static void print(MsgType msgType, const char *rawFormat, va_list args); 52 | 53 | private: 54 | static std::time_t _startTime; 55 | static FILE * _screenOutStream; // Out stream for screen printing 56 | static FILE * _logOutStream; // Out stream for log printing 57 | static std::string _logFileName; // Current log file name 58 | }; 59 | 60 | PROJECT_NAMESPACE_END 61 | 62 | #endif // ZKUTIL_MSGPRINTER_H_ 63 | -------------------------------------------------------------------------------- /src/util/Polygon2Rect.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Polygon2Rect.h 3 | * @author Keren Zhu 4 | * @date 11/06/2018 5 | */ 6 | 7 | #ifndef KLIB_POLYGON2RECT_H_ 8 | #define KLIB_POLYGON2RECT_H_ 9 | 10 | #include "Box.h" 11 | #include 12 | 13 | 14 | //using namespace limbo::geometry; 15 | 16 | 17 | namespace limbo { namespace geometry { 18 | 19 | /** 20 | * @brief if your point class setting is different from that in the default point_traits, please create a specialization 21 | * 22 | * The specialization should be in the same scope as the original template struct 23 | */ 24 | template 25 | struct point_traits> 26 | { 27 | /// point type 28 | typedef PROJECT_NAMESPACE::XY point_type; 29 | /// coordinate type 30 | typedef T coordinate_type; 31 | 32 | /** 33 | * @brief access coordinates 34 | * @param point a data point 35 | * @param orient orientation can be HORIZONTAL(x) or VERTICAL(y) 36 | * @return x or y coordinate 37 | */ 38 | static coordinate_type get(const point_type& point, orientation_2d orient) 39 | { 40 | if (orient == HORIZONTAL) return point.x(); 41 | else if (orient == VERTICAL) return point.y(); 42 | else assert(0); 43 | } 44 | /** 45 | * @brief set coordinates 46 | * @param point a data point 47 | * @param orient orientation can be HORIZONTAL(x) or VERTICAL(y) 48 | * @param value data value 49 | */ 50 | static void set(point_type& point, orientation_2d orient, coordinate_type value) 51 | { 52 | if (orient == HORIZONTAL) return point.setX(value); 53 | else if (orient == VERTICAL) return point.setY(value); 54 | else assert(0); 55 | } 56 | /** 57 | * @brief construct a point object 58 | * @param x x coordinate 59 | * @param y y coordinate 60 | * @return the point object 61 | */ 62 | static point_type construct(coordinate_type x, coordinate_type y) 63 | { 64 | point_type p; 65 | p.setX(x); p.setY(y); 66 | return p; 67 | } 68 | }; 69 | 70 | }} 71 | 72 | 73 | namespace limbo { namespace geometry { 74 | 75 | /** 76 | * @brief if your rectangle class setting is different from that in the default point_traits, please create a specialization 77 | */ 78 | template 79 | struct rectangle_traits> 80 | { 81 | /// rectangle type 82 | typedef PROJECT_NAMESPACE::Box rectangle_type; 83 | /// coordinate type 84 | typedef T coordinate_type; 85 | 86 | /** 87 | * @brief access coordinates 88 | * @param rect a rectangle object 89 | * @param dir can be LEFT (xl), BOTTOM (yl), RIGHT (xh) or TOP (yh) 90 | * @return coordinate 91 | */ 92 | static coordinate_type get(const rectangle_type& rect, direction_2d dir) 93 | { 94 | switch (dir) 95 | { 96 | case LEFT: return rect.xLo(); 97 | case BOTTOM: return rect.yLo(); 98 | case RIGHT: return rect.xHi(); 99 | case TOP: return rect.yHi(); 100 | default: assert(0); 101 | } 102 | } 103 | /** 104 | * @brief set coordinates 105 | * @param rect a rectangle object 106 | * @param dir can be LEFT (xl), BOTTOM (yl), RIGHT (xh) or TOP (yh) 107 | * @param value coordinate value 108 | */ 109 | static void set(rectangle_type& rect, direction_2d dir, coordinate_type value) 110 | { 111 | switch (dir) 112 | { 113 | case LEFT: rect.setXLo(value); break; 114 | case BOTTOM: rect.setYLo(value); break; 115 | case RIGHT: rect.setXHi(value); break; 116 | case TOP: rect.YHi(value); break; 117 | default: assert(0); 118 | } 119 | } 120 | /** 121 | * @brief construct rectangle 122 | * @param xl, yl, xh, yh coordinates of rectangle 123 | * @return a rectangle object 124 | */ 125 | static rectangle_type construct(coordinate_type xl, coordinate_type yl, coordinate_type xh, coordinate_type yh) 126 | { 127 | rectangle_type rect; 128 | rect.setXLo(xl); rect.setYLo(yl); 129 | rect.setXHi(xh); rect.setYHi(yh); 130 | return rect; 131 | } 132 | }; 133 | 134 | }} 135 | 136 | namespace klib 137 | { 138 | template 139 | inline bool convertPolygon2Rects(const std::vector> &pts, std::vector> &rects) 140 | { 141 | typedef typename PROJECT_NAMESPACE::XY PtType; 142 | typedef typename PROJECT_NAMESPACE::Box RectType; 143 | 144 | limbo::geometry::Polygon2Rectangle, std::vector> p2r(rects, pts.begin(), pts.end(), limbo::geometry::HOR_VER_SLICING); 145 | return p2r(); 146 | } 147 | } 148 | 149 | #endif ///KLIB_POLYGON2RECT_H_ 150 | -------------------------------------------------------------------------------- /src/util/Vector2D.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Keren on 04/17/2018 3 | // 4 | 5 | #ifndef ZKUTIL_VECTOR2D_H_ 6 | #define ZKUTIL_VECTOR2D_H_ 7 | 8 | #include 9 | #include 10 | #include "global/namespace.h" 11 | #include "global/type.h" 12 | #include "global/define.h" 13 | #include "XY.h" 14 | 15 | PROJECT_NAMESPACE_BEGIN 16 | 17 | /// ================================================================================ 18 | /// Vector map class 19 | /// A templete for 2D vector using flat vector 20 | /// This is a Y-dominate implementation, that is elements in the 21 | /// same column (with the same Y) are in a piece of consecutive memory. 22 | /// Thus, when iterate through this 2D vector using a two-level for loop, 23 | /// the following pattern is better from the memory/cache hit prespective 24 | /// for (x ...) 25 | /// { 26 | /// for (y ....) 27 | /// { 28 | /// .... 29 | /// } 30 | /// } 31 | /// 32 | /// Update: 10/26/2018 add option to yMajor 33 | /// ================================================================================ 34 | template 35 | class Vector2D 36 | { 37 | public: 38 | /// Class for initializer_list type 39 | enum class InitListType : Byte 40 | { 41 | XMajor, 42 | yMajor 43 | }; 44 | 45 | public: 46 | explicit Vector2D() = default; 47 | explicit Vector2D(IndexType xSize, IndexType ySize) : _vec(xSize * ySize), _xSize(xSize), _ySize(ySize) {} 48 | explicit Vector2D(IndexType xSize, IndexType ySize, const T &v) : _vec(xSize, ySize, v), _xSize(xSize), _ySize(ySize) {} 49 | explicit Vector2D(IndexType xSize, IndexType ySize, InitListType t, std::initializer_list l); 50 | 51 | void clear() { _vec.clear(); _xSize = _ySize = 0; } 52 | void resize(IndexType xSize, IndexType ySize) { _vec.resize(xSize * ySize); _xSize = xSize; _ySize = ySize; } 53 | void resize(IndexType xSize, IndexType ySize, const T &v) { _vec.resize(xSize * ySize, v); _xSize = xSize; _ySize = ySize; } 54 | void setType(InitListType type) { _type = type; } 55 | 56 | IndexType xSize() const { return _xSize; } 57 | IndexType ySize() const { return _ySize; } 58 | IndexType size() const { return _vec.size(); } 59 | InitListType type() const { return _type; } 60 | 61 | const T & at(IndexType x, IndexType y) const { return this->atIdx(xyToIndex(x, y)); } 62 | T & at(IndexType x, IndexType y) { return this->atIdx(xyToIndex(x, y)); } 63 | const T & at(XY xy) const { return this->at(xyToIndex(xy)); } 64 | T & at(XY xy) { return this->at(xyToIndex(xy)); } 65 | const T & atIdx(IndexType idx) const { return AT(_vec, idx); } 66 | T & atIdx(IndexType idx) { return AT(_vec, idx); } 67 | 68 | /// Iterator 69 | typename std::vector::iterator begin() { return _vec.begin(); } 70 | typename std::vector::const_iterator begin() const { return _vec.begin(); } 71 | typename std::vector::iterator end() { return _vec.end(); } 72 | typename std::vector::const_iterator end() const { return _vec.end(); } 73 | 74 | /// Conversion 75 | IndexType xyToIndex(IndexType x, IndexType y) const; 76 | IndexType xyToIndex(XY xy) const { return xyToIndex(xy.x(), xy.y()); } 77 | IndexType idxToX(IndexType idx) const; 78 | IndexType idxToY(IndexType idx) const; 79 | XY idxToXY(IndexType idx) const { return XY(idxToX(idx), idxToY(idx)); } 80 | 81 | private: 82 | std::vector _vec; 83 | IndexType _xSize = 0; 84 | IndexType _ySize = 0; 85 | InitListType _type = InitListType::XMajor; 86 | }; 87 | 88 | template 89 | inline IndexType Vector2D::xyToIndex(IndexType x, IndexType y) const 90 | { 91 | if (_type == InitListType::XMajor) 92 | { 93 | return _ySize * x + y; 94 | } 95 | else 96 | { 97 | /// Y major 98 | return _xSize *y + x; 99 | } 100 | } 101 | 102 | template 103 | inline IndexType Vector2D::idxToX(IndexType idx) const 104 | { 105 | if (_type == InitListType::XMajor) 106 | { 107 | return idx / _ySize; 108 | } 109 | else 110 | { 111 | return idx % _xSize; 112 | } 113 | } 114 | 115 | template 116 | inline IndexType Vector2D::idxToY(IndexType idx) const 117 | { 118 | if (_type == InitListType::XMajor) 119 | { 120 | return idx % _ySize; 121 | } 122 | else 123 | { 124 | return idx / _xSize; 125 | } 126 | } 127 | /// Ctor using initializer_list 128 | template 129 | inline Vector2D::Vector2D(IndexType xSize, IndexType ySize, InitListType t, std::initializer_list l) 130 | : _vec(xSize * ySize), _xSize(xSize), _ySize(ySize) 131 | { 132 | Assert(l.size == size()); 133 | if (t == InitListType::XMajor) 134 | { 135 | /// Data in Vector2D is in X-major manner 136 | /// Just copy the initializer_list to the _vec 137 | std::copy(l.begin, l.end(), _vec.begin()); 138 | } 139 | else 140 | { // t == InitiListType::YMajor 141 | for (IndexType idx = 0; idx < size(); ++idx) 142 | { 143 | IndexType x = idx % _xSize; 144 | IndexType y = idx / _xSize; 145 | at(x, y) = *(l.begin() + idx); 146 | } 147 | } 148 | } 149 | 150 | PROJECT_NAMESPACE_END 151 | 152 | #endif //ZKUTIL_VECTOR2D_H_ 153 | -------------------------------------------------------------------------------- /src/util/XY.h: -------------------------------------------------------------------------------- 1 | #ifndef __XY_H__ 2 | #define __XY_H__ 3 | 4 | #include "global/namespace.h" 5 | #include "BasicTypeSelection.h" 6 | #include "MsgPrinter.h" 7 | #include 8 | #include 9 | 10 | PROJECT_NAMESPACE_BEGIN 11 | 12 | /// enum type for position of segments versus points 13 | enum class PtPosType : std::uint8_t 14 | { 15 | NORTH = 0, 16 | SOUTH = 1, 17 | WEST = 2, 18 | EAST = 3, 19 | TOP = 4, 20 | BOTTOM = 5, 21 | NOT_NEIGHBOR = 6 22 | }; 23 | 24 | template 25 | class XY 26 | { 27 | typedef boost::geometry::model::point boostPointType; 28 | public: 29 | 30 | template 31 | friend XY::type> operator*(U lhs, const XY &rhs) { return XY::type>(lhs * rhs.x(), lhs * rhs.y()); } 32 | 33 | template 34 | friend XY::type> operator*(const XY &lhs, U rhs) { return XY::type>(lhs.x() * rhs, lhs.y() * rhs); } 35 | 36 | template 37 | friend XY::type> operator/(const XY &lhs, U rhs) { return XY::type>(lhs.x() / rhs, lhs.y() / rhs); } 38 | 39 | public: 40 | explicit XY() = default; 41 | explicit XY(T x, T y) : _x(x), _y(y) {} 42 | //explicit XY(T x, T y, T z) : _x(x), _y(y) {MsgPrinter::wrn("%s, ignore z for XY(x,y,z) \n", __PRETTY_FUNCTION__);} 43 | 44 | template 45 | explicit XY(const XY &other) : _x((T)other.x()), _y((T)other.y()) {} 46 | 47 | // Getters 48 | T x() const { return _x; } 49 | T y() const { return _y; } 50 | XY left() const { return XY(_x - 1, _y); } 51 | XY right() const { return XY(_x + 1, _y); } 52 | XY bottom() const { return XY(_x, _y - 1); } 53 | XY top() const { return XY(_x, _y + 1); } 54 | 55 | // Setters 56 | void setX(T x) { _x = x; } 57 | void setY(T y) { _y = y; } 58 | void setXY(T x, T y) { _x = x; _y = y; } 59 | 60 | bool operator==(const XY &rhs) const { return _x == rhs.x() && _y == rhs.y(); } 61 | bool operator!=(const XY &rhs) const { return ! (*this == rhs); } 62 | bool operator<(const XY &rhs) const { return _y == rhs.y() ? _x < rhs.x() : _y < rhs.y(); } 63 | 64 | template 65 | XY::type> operator+(const XY &rhs) const { return XY::type>(_x + rhs.x(), _y + rhs.y()); } 66 | 67 | template 68 | XY::type> operator-(const XY &rhs) const { return XY::type>(_x - rhs.x(), _y - rhs.y()); } 69 | 70 | template 71 | XY & operator+=(const XY &rhs) { _x += rhs.x(); _y += rhs.y(); return *this; } 72 | 73 | template 74 | XY & operator-=(const XY &rhs) { _x -= rhs.x(); _y -= rhs.y(); return *this; } 75 | 76 | template 77 | XY & operator*=(U rhs) { _x *= rhs; _y *= rhs; return *this; } 78 | 79 | template 80 | XY & operator/=(U rhs) { _x /= rhs; _y /= rhs; return *this; } 81 | 82 | /// Find the relative position of the rhs TO this 83 | PtPosType ptPos(const XY &rhs) const 84 | { 85 | if (top() == rhs) 86 | { 87 | return PtPosType::NORTH; 88 | } 89 | else if (bottom() == rhs) 90 | { 91 | return PtPosType::SOUTH; 92 | } 93 | else if (right() == rhs) 94 | { 95 | return PtPosType::EAST; 96 | } 97 | else if (left() == rhs) 98 | { 99 | return PtPosType::WEST; 100 | } 101 | else 102 | { 103 | return PtPosType::NOT_NEIGHBOR; 104 | } 105 | } 106 | 107 | boostPointType toBoost() const 108 | { 109 | return boostPointType(_x, _y); 110 | } 111 | void fromBoost(const boostPointType &rtV) 112 | { 113 | T x = boost::geometry::get<0>(rtV); 114 | T y = boost::geometry::get<1>(rtV); 115 | setXY(x,y); 116 | } 117 | 118 | /// Misc. 119 | std::string toStr() const 120 | { 121 | std::ostringstream oss; 122 | oss << " ("<<_x<<","<<_y<<") "; 123 | return oss.str(); 124 | } 125 | private: 126 | T _x = 0; 127 | T _y = 0; 128 | }; 129 | 130 | template 131 | struct XYHashFunc 132 | { 133 | std::size_t operator() (const XY &xy) const 134 | { 135 | std::size_t seed = 0; 136 | boost::hash_combine(seed, xy.x()); 137 | boost::hash_combine(seed, xy.y()); 138 | return seed; 139 | }; 140 | }; 141 | 142 | PROJECT_NAMESPACE_END 143 | 144 | /// Hash function 145 | namespace std 146 | { 147 | template 148 | struct hash > 149 | { 150 | typedef PROJECT_NAMESPACE::XY argumentType; 151 | typedef std::size_t resultType; 152 | resultType operator()(argumentType const& s) const noexcept 153 | { 154 | resultType seed = 0; 155 | boost::hash_combine(seed, s.x()); 156 | boost::hash_combine(seed, s.y()); 157 | return seed; 158 | } 159 | }; 160 | } 161 | 162 | #endif // __XY_H__ 163 | -------------------------------------------------------------------------------- /src/util/XYZ.h: -------------------------------------------------------------------------------- 1 | #ifndef __XYZ_H__ 2 | #define __XYZ_H__ 3 | 4 | #include "global/namespace.h" 5 | #include "MsgPrinter.h" 6 | #include "XY.h" 7 | 8 | PROJECT_NAMESPACE_BEGIN 9 | 10 | 11 | inline PtPosType revertPtPos(PtPosType init) 12 | { 13 | if (init == PtPosType::NORTH) 14 | { 15 | return PtPosType::SOUTH; 16 | } 17 | else if (init == PtPosType::SOUTH) 18 | { 19 | return PtPosType::NORTH; 20 | } 21 | else if (init == PtPosType::WEST) 22 | { 23 | return PtPosType::EAST; 24 | } 25 | else if (init == PtPosType::EAST) 26 | { 27 | return PtPosType::WEST; 28 | } 29 | else if (init == PtPosType::TOP) 30 | { 31 | return PtPosType::BOTTOM; 32 | } 33 | else if (init == PtPosType::BOTTOM) 34 | { 35 | return PtPosType::TOP; 36 | } 37 | return PtPosType::NOT_NEIGHBOR; 38 | } 39 | 40 | 41 | template 42 | class XYZ 43 | { 44 | typedef boost::geometry::model::point boostPointType; 45 | public: 46 | explicit XYZ() = default; 47 | explicit XYZ(XYType x, XYType y, ZType z) : _xy(x, y), _z(z) {} 48 | explicit XYZ(XY xy, ZType z) : _xy(xy), _z(z) {} 49 | explicit XYZ(XYType x, XYType y) :_xy(x,y), _z(0) { MsgPrinter::wrn("%s, filling in 0 as z for XYZ(x,y) \n", __PRETTY_FUNCTION__);} 50 | 51 | /// Getters 52 | XYType x() const { return _xy.x(); } 53 | XYType y() const { return _xy.y(); } 54 | ZType z() const { return _z; } 55 | const XY & xy() const { return _xy; } 56 | XYZ north() const { return XYZ(_xy.x(), _xy.y() + 1, _z); } 57 | XYZ south() const { return XYZ(_xy.x(), _xy.y() - 1, _z); } 58 | XYZ east() const { return XYZ(_xy.x() + 1, _xy.y(), _z); } 59 | XYZ west() const { return XYZ(_xy.x() - 1, _xy.y(), _z); } 60 | XYZ top() const { return XYZ(_xy.x(), _xy.y(), _z + 1); } 61 | XYZ bottom() const { return XYZ(_xy.x(), _xy.y(), _z - 1); } 62 | 63 | /// Setters 64 | void setX(XYType x) { _xy.setX(x); } 65 | void setY(XYType y) { _xy.setY(y); } 66 | void setZ(ZType z) { _z = z; } 67 | void setXY(const XY &xy) { _xy = xy; } 68 | void setXY(XYType x, XYType y) { _xy.setXY(x, y); } 69 | void setXYZ(XYType x, XYType y, ZType z) { _xy.setXY(x, y); _z = z; } 70 | 71 | bool operator==(const XYZ &rhs) const { return _xy == rhs.xy() && _z == rhs.z(); } 72 | bool operator!=(const XYZ &rhs) const { return ! (*this == rhs); } 73 | bool operator<(const XYZ &rhs) const { return _z == rhs.z() ? _xy < rhs.xy() : _z < rhs.z(); } 74 | 75 | 76 | /// Find the relative position of the rhs TO this 77 | PtPosType ptPos(const XYZ &rhs) const 78 | { 79 | if (north() == rhs) 80 | { 81 | return PtPosType::NORTH; 82 | } 83 | else if (south() == rhs) 84 | { 85 | return PtPosType::SOUTH; 86 | } 87 | else if (east() == rhs) 88 | { 89 | return PtPosType::EAST; 90 | } 91 | else if (west() == rhs) 92 | { 93 | return PtPosType::WEST; 94 | } 95 | else if (bottom() == rhs) 96 | { 97 | return PtPosType::BOTTOM; 98 | } 99 | else if (top() == rhs) 100 | { 101 | return PtPosType::TOP; 102 | } 103 | else 104 | { 105 | return PtPosType::NOT_NEIGHBOR; 106 | } 107 | } 108 | 109 | boostPointType toBoost() const 110 | { 111 | return boostPointType(_xy.x(), _xy.y(), _z); 112 | } 113 | 114 | void fromBoost(const boostPointType &rtV) 115 | { 116 | XYType x = boost::geometry::get<0>(rtV); 117 | XYType y = boost::geometry::get<1>(rtV); 118 | ZType z = boost::geometry::get<2>(rtV); 119 | setXYZ(x,y,z); 120 | } 121 | 122 | /// Misc. 123 | std::string toStr() const 124 | { 125 | return std::string(*this); 126 | } 127 | 128 | operator std::string() const 129 | { 130 | std::ostringstream oss; 131 | oss << " ("<<_xy.x()<<","<<_xy.y()<<","<<_z<<") "; 132 | return oss.str(); 133 | } 134 | private: 135 | XY _xy; 136 | ZType _z = 0; 137 | }; 138 | 139 | template 140 | struct XYZHashFunc 141 | { 142 | std::size_t operator() (const XYZ &xyz) const 143 | { 144 | std::size_t seed = 0; 145 | boost::hash_combine(seed, xyz.x()); 146 | boost::hash_combine(seed, xyz.y()); 147 | boost::hash_combine(seed, xyz.z()); 148 | return seed; 149 | }; 150 | }; 151 | 152 | PROJECT_NAMESPACE_END 153 | 154 | 155 | /// Hash function 156 | namespace std 157 | { 158 | template 159 | struct hash > 160 | { 161 | typedef PROJECT_NAMESPACE::XYZ argumentType; 162 | typedef std::size_t resultType; 163 | resultType operator()(argumentType const& s) const noexcept 164 | { 165 | resultType seed = 0; 166 | boost::hash_combine(seed, s.x()); 167 | boost::hash_combine(seed, s.y()); 168 | boost::hash_combine(seed, s.z()); 169 | return seed; 170 | } 171 | }; 172 | template<> 173 | struct hash > 174 | { 175 | typedef PROJECT_NAMESPACE::XYZ argumentType; 176 | typedef std::size_t resultType; 177 | resultType operator()(argumentType const& s) const noexcept 178 | { 179 | resultType seed = 0; 180 | boost::hash_combine(seed, s.x()); 181 | boost::hash_combine(seed, s.y()); 182 | boost::hash_combine(seed, s.z()); 183 | return seed; 184 | } 185 | }; 186 | } 187 | 188 | #endif // __XYZ_H__ 189 | -------------------------------------------------------------------------------- /src/util/kLibBase.h: -------------------------------------------------------------------------------- 1 | #ifndef ZKUTIL_BASIC_LIB_H_ 2 | #define ZKUTIL_BASIC_LIB_H_ 3 | // ================================================================================ 4 | // Basic and commonly used function lib 5 | // by Keren Zhu 6 | // Data: 04/28/2018 7 | // ================================================================================ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include // ceilf(), floorf() 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "MsgPrinter.h" 20 | #include "Assert.h" 21 | #include "XYZ.h" 22 | #include "util/thirdparty/AlmostEquals.h" 23 | #include "global/namespace.h" 24 | #include "global/type.h" 25 | 26 | namespace klib 27 | { 28 | using IndexType = PROJECT_NAMESPACE::IndexType; 29 | using IntType = PROJECT_NAMESPACE::IntType; 30 | using RealType = PROJECT_NAMESPACE::RealType; 31 | using MsgPrinter = PROJECT_NAMESPACE::MsgPrinter; 32 | // ================================================================================ 33 | // STL extension 34 | // ================================================================================ 35 | /// @breif Erase an element in the vector by swapping with the last element and resize 36 | /// @brief Ordering is not kept 37 | template 38 | inline bool eraseElementInVec(std::vector &vec, IndexType idx) 39 | { 40 | IndexType vectorSize = vec.size(); 41 | if (idx >= vectorSize) 42 | { 43 | MsgPrinter::err("eraseElementInVec error: idx out of bound \n"); 44 | Assert(false); 45 | return false; 46 | } 47 | // idx < vectorSize 48 | vec[idx] = std::move(vec.back()); 49 | vec.resize(vectorSize - 1); 50 | return true; 51 | } 52 | 53 | /// @breif Erase an element in the vector by swapping with the last element and resize 54 | /// @brief Ordering is not kept 55 | template 56 | inline bool eraseElementInVec(std::vector &vec, IntType idx) 57 | { 58 | if (idx < 0) 59 | { 60 | MsgPrinter::err("eraseElementInVec error: idx out of bound \n"); 61 | Assert(false); 62 | return false; 63 | } 64 | return eraseElementInVec(vec, static_cast(idx)); 65 | } 66 | 67 | 68 | /// @brief Swap t1 and t2 if t1 > t2. Ie. put t1 and t2 in order 69 | template 70 | inline void putTwoInOrder(T &t1, T &t2) 71 | { 72 | if (t1 > t2) 73 | { 74 | std::swap(t1, t2); 75 | } 76 | } 77 | 78 | 79 | template 80 | inline void putTwoInOrder(T &t1, T &t2, std::function const compare) 81 | { 82 | if (compare(t1, t2)) 83 | { 84 | std::swap(t1, t2); 85 | } 86 | } 87 | 88 | /// Calculate two usigned int difference 89 | inline IndexType idxDif(IndexType i1, IndexType i2) 90 | { 91 | return i1 > i2? i1 - i2 : i2 - i1; 92 | } 93 | 94 | template 95 | inline T absDif(T t1, T t2) 96 | { 97 | return t1 > t2? t1 - t2 : t2 - t1; 98 | } 99 | 100 | inline bool idxAddOverflow(IndexType idx1, IndexType idx2) 101 | { 102 | IndexType sum = idx1 + idx2; 103 | return sum < idx1 || sum < idx2; 104 | } 105 | 106 | /// @brief move v1 elements into v2, and erase v1 107 | /// @param two reference to two vectors containing the same type of elements 108 | template 109 | inline void moveVector(std::vector &vFrom, std::vector &vTo) 110 | { 111 | vTo.insert(vTo.end(), std::make_move_iterator(vFrom.begin()), 112 | std::make_move_iterator(vFrom.end())); 113 | vFrom.clear(); 114 | } 115 | 116 | /// @brief Stuct for null deleter 117 | namespace createSharedPtrDetails 118 | { 119 | struct null_deleter 120 | { 121 | void operator()(void const *) const 122 | { 123 | } 124 | }; 125 | } 126 | 127 | /// @brief create a shared_ptr to a existing target 128 | template 129 | inline std::shared_ptr createSharedPtr(T &t) 130 | { 131 | return std::shared_ptr(&t, createSharedPtrDetails::null_deleter()); 132 | } 133 | 134 | // ================================================================================ 135 | // Numerical functions 136 | // ================================================================================ 137 | 138 | /// @breif Automatically decide whether to round a float/double. depending on the returning type. 139 | template 140 | inline T autoRound(RealType rValue) 141 | { 142 | return static_cast(std::round(rValue)); 143 | } 144 | 145 | template<> 146 | inline float autoRound(RealType rValue) 147 | { 148 | return rValue; 149 | } 150 | 151 | template<> 152 | inline double autoRound(RealType rValue) 153 | { 154 | return rValue; 155 | } 156 | 157 | 158 | template 159 | inline T manhattanDistance(const PROJECT_NAMESPACE::XY &t1, const PROJECT_NAMESPACE::XY &t2) 160 | { 161 | return absDif(t1.x(), t2.x()) + absDif(t1.y(), t2.y()); 162 | } 163 | 164 | template 165 | inline T manhattanDistance(const PROJECT_NAMESPACE::XYZ &t1, PROJECT_NAMESPACE::XYZ &t2) 166 | { 167 | return absDif(t1.z(), t2.z()) + manhattanDistance(t1.xy(), t2.xy()); 168 | } 169 | 170 | template 171 | inline T manhattanDistance(const PROJECT_NAMESPACE::XYZ t1, PROJECT_NAMESPACE::XYZ t2) 172 | { 173 | return absDif(t1.z(), t2.z()) + manhattanDistance(t1.xy(), t2.xy()); 174 | } 175 | 176 | /// @brief determine whether another XYZ is adjacent to this 177 | /// @param another XYZ 178 | /// @return true if two are adjacent to each other, false if not 179 | template 180 | bool xyzAdjacent(const PROJECT_NAMESPACE::XYZ &lhs, const PROJECT_NAMESPACE::XYZ &rhs) 181 | { 182 | return (klib::absDif(lhs.x(), rhs.x()) + klib::absDif(lhs.y(), rhs.y()) + klib::absDif(lhs.z(), rhs.z())) == 1; 183 | } 184 | // ================================================================================ 185 | // Parser string 186 | // ================================================================================ 187 | /// @brief trim left space of a string 188 | inline std::string & ltrim(std::string &s) 189 | { 190 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); 191 | return s; 192 | } 193 | 194 | /// @brief trim right space of a string 195 | inline std::string & rtrim(std::string &s) 196 | { 197 | s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); 198 | return s; 199 | } 200 | 201 | /// @brief trim spaces from both ends 202 | inline std::string & trim(std::string &s) 203 | { 204 | return ltrim(rtrim(s)); 205 | } 206 | 207 | /// @brief given path in str_path, output the design name 208 | /// @brief for example: with input "path1/path2/sparc_exu_alu", output "sparc_exu_alu" 209 | inline std::string parsePath2Name(std::string str_path) 210 | { 211 | size_t pos = str_path.rfind('/'); 212 | if (pos == std::string::npos) return str_path; 213 | 214 | return str_path.substr(pos+1); 215 | } 216 | 217 | /// @brief given path in str_path, output the design name 218 | /// @brief for example: with input "path1/path2/sparc_exu_alu", output "path1/path2" 219 | inline std::string parseFile2Path(std::string str_path) 220 | { 221 | size_t pos = str_path.rfind('/'); 222 | if (pos == std::string::npos) return str_path; 223 | 224 | return str_path.substr(0, pos); 225 | } 226 | 227 | /// @brief parse filename suffix 228 | inline std::string parseSuffix(std::string str) 229 | { 230 | size_t pos = str.rfind('.'); 231 | if (pos == std::string::npos) return str; 232 | return str.substr(pos+1); 233 | } 234 | 235 | /// @brief trim filename suffix 236 | inline std::string trimSuffix(std::string str) 237 | { 238 | size_t pos = str.rfind('.'); 239 | return str.substr(0, pos); 240 | } 241 | 242 | // ================================================================================ 243 | // File-reading functions 244 | // ================================================================================ 245 | 246 | /// @brief Check whether the file exists 247 | inline bool isFileExist(const char *fileName) 248 | { 249 | std::ifstream infile(fileName); 250 | return infile.good(); 251 | } 252 | 253 | /// @brief read from ifstream in, search target1 & target2, 254 | /// @brief if found, return true, and the whole line is stored in "str_line" 255 | /// @brief if reach EOF, return false 256 | inline bool readSearchUntil(std::ifstream &in, std::string &str_line, const std::string &target1, const std::string &target2="") 257 | { 258 | bool skipTarget2 = target2.length() <= 0; 259 | while (!in.eof()) 260 | { 261 | getline( in, str_line ); if (in.eof()) return false; 262 | if (std::string::npos != str_line.find(target1)) return true; 263 | if (skipTarget2) continue; 264 | if (std::string::npos != str_line.find(target2)) return true; 265 | } // while 266 | return false; 267 | } 268 | 269 | } // namespce klib 270 | #endif // ZKUTIL_BASIC_LIB_H_ 271 | -------------------------------------------------------------------------------- /src/util/thirdparty/AlmostEquals.h: -------------------------------------------------------------------------------- 1 | // Copyright 2005, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) 31 | // 32 | // The Google C++ Testing Framework (Google Test) 33 | // 34 | // This header file declares functions and macros used internally by 35 | // Google Test. They are subject to change without notice. 36 | 37 | #pragma once 38 | 39 | #include 40 | #include 41 | 42 | namespace klib 43 | { 44 | 45 | // This template class serves as a compile-time function from size to 46 | // type. It maps a size in bytes to a primitive type with that 47 | // size. e.g. 48 | // 49 | // TypeWithSize<4>::UInt 50 | // 51 | // is typedef-ed to be unsigned int (unsigned integer made up of 4 52 | // bytes). 53 | // 54 | // Such functionality should belong to STL, but I cannot find it 55 | // there. 56 | // 57 | // Google Test uses this class in the implementation of floating-point 58 | // comparison. 59 | // 60 | // For now it only handles UInt (unsigned int) as that's all Google Test 61 | // needs. Other types can be easily added in the future if need 62 | // arises. 63 | template 64 | class TypeWithSize { 65 | public: 66 | // This prevents the user from using TypeWithSize with incorrect 67 | // values of N. 68 | typedef void UInt; 69 | }; 70 | 71 | // The specialization for size 4. 72 | template <> 73 | class TypeWithSize<4> { 74 | public: 75 | // unsigned int has size 4 in both gcc and MSVC. 76 | // 77 | // As base/basictypes.h doesn't compile on Windows, we cannot use 78 | // uint32, uint64, and etc here. 79 | typedef int Int; 80 | typedef unsigned int UInt; 81 | }; 82 | 83 | // The specialization for size 8. 84 | template <> 85 | class TypeWithSize<8> { 86 | public: 87 | #if _MSC_VER 88 | typedef __int64 Int; 89 | typedef unsigned __int64 UInt; 90 | #else 91 | typedef long long Int; // NOLINT 92 | typedef unsigned long long UInt; // NOLINT 93 | #endif // _MSC_VER 94 | }; 95 | 96 | // This template class represents an IEEE floating-point number 97 | // (either single-precision or double-precision, depending on the 98 | // template parameters). 99 | // 100 | // The purpose of this class is to do more sophisticated number 101 | // comparison. (Due to round-off error, etc, it's very unlikely that 102 | // two floating-points will be equal exactly. Hence a naive 103 | // comparison by the == operation often doesn't work.) 104 | // 105 | // Format of IEEE floating-point: 106 | // 107 | // The most-significant bit being the leftmost, an IEEE 108 | // floating-point looks like 109 | // 110 | // sign_bit exponent_bits fraction_bits 111 | // 112 | // Here, sign_bit is a single bit that designates the sign of the 113 | // number. 114 | // 115 | // For float, there are 8 exponent bits and 23 fraction bits. 116 | // 117 | // For double, there are 11 exponent bits and 52 fraction bits. 118 | // 119 | // More details can be found at 120 | // http://en.wikipedia.org/wiki/IEEE_floating-point_standard. 121 | // 122 | // Template parameter: 123 | // 124 | // RawType: the raw floating-point type (either float or double) 125 | template 126 | class FloatingPoint { 127 | public: 128 | // Defines the unsigned integer type that has the same size as the 129 | // floating point number. 130 | typedef typename TypeWithSize::UInt Bits; 131 | 132 | // Constants. 133 | 134 | // # of bits in a number. 135 | static const std::size_t kBitCount = 8 * sizeof(RawType); 136 | 137 | // # of fraction bits in a number. 138 | static const std::size_t kFractionBitCount = 139 | std::numeric_limits::digits - 1; 140 | 141 | // # of exponent bits in a number. 142 | static const std::size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; 143 | 144 | // The mask for the sign bit. 145 | static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); 146 | 147 | // The mask for the fraction bits. 148 | static const Bits kFractionBitMask = 149 | ~static_cast(0) >> (kExponentBitCount + 1); 150 | 151 | // The mask for the exponent bits. 152 | static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); 153 | 154 | // How many ULP's (Units in the Last Place) we want to tolerate when 155 | // comparing two numbers. The larger the value, the more error we 156 | // allow. A 0 value means that two numbers must be exactly the same 157 | // to be considered equal. 158 | // 159 | // The maximum error of a single floating-point operation is 0.5 160 | // units in the last place. On Intel CPU's, all floating-point 161 | // calculations are done with 80-bit precision, while double has 64 162 | // bits. Therefore, 4 should be enough for ordinary use. 163 | // 164 | // See the following article for more details on ULP: 165 | // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ 166 | static const std::size_t kMaxUlps = 4; 167 | 168 | // Constructs a FloatingPoint from a raw floating-point number. 169 | // 170 | // On an Intel CPU, passing a non-normalized NAN (Not a Number) 171 | // around may change its bits, although the new value is guaranteed 172 | // to be also a NAN. Therefore, don't expect this constructor to 173 | // preserve the bits in x when x is a NAN. 174 | explicit FloatingPoint(const RawType& x) { u_.value_ = x; } 175 | 176 | // Static methods 177 | 178 | // Reinterprets a bit pattern as a floating-point number. 179 | // 180 | // This function is needed to test the AlmostEquals() method. 181 | static RawType ReinterpretBits(const Bits bits) { 182 | FloatingPoint fp(0); 183 | fp.u_.bits_ = bits; 184 | return fp.u_.value_; 185 | } 186 | 187 | // Returns the floating-point number that represent positive infinity. 188 | static RawType Infinity() { 189 | return ReinterpretBits(kExponentBitMask); 190 | } 191 | 192 | // Returns the maximum representable finite floating-point number. 193 | static RawType Max(); 194 | 195 | // Non-static methods 196 | 197 | // Returns the bits that represents this number. 198 | const Bits &bits() const { return u_.bits_; } 199 | 200 | // Returns the exponent bits of this number. 201 | Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } 202 | 203 | // Returns the fraction bits of this number. 204 | Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } 205 | 206 | // Returns the sign bit of this number. 207 | Bits sign_bit() const { return kSignBitMask & u_.bits_; } 208 | 209 | // Returns true iff this is NAN (not a number). 210 | bool is_nan() const { 211 | // It's a NAN if the exponent bits are all ones and the fraction 212 | // bits are not entirely zeros. 213 | return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); 214 | } 215 | 216 | // Returns true iff this number is at most kMaxUlps ULP's away from 217 | // rhs. In particular, this function: 218 | // 219 | // - returns false if either number is (or both are) NAN. 220 | // - treats really large numbers as almost equal to infinity. 221 | // - thinks +0.0 and -0.0 are 0 DLP's apart. 222 | bool AlmostEquals(const FloatingPoint& rhs) const { 223 | // The IEEE standard says that any comparison operation involving 224 | // a NAN must return false. 225 | if (is_nan() || rhs.is_nan()) return false; 226 | 227 | return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) 228 | <= kMaxUlps; 229 | } 230 | 231 | private: 232 | // The data type used to store the actual floating-point number. 233 | union FloatingPointUnion { 234 | RawType value_; // The raw floating-point number. 235 | Bits bits_; // The bits that represent the number. 236 | }; 237 | 238 | // Converts an integer from the sign-and-magnitude representation to 239 | // the biased representation. More precisely, let N be 2 to the 240 | // power of (kBitCount - 1), an integer x is represented by the 241 | // unsigned number x + N. 242 | // 243 | // For instance, 244 | // 245 | // -N + 1 (the most negative number representable using 246 | // sign-and-magnitude) is represented by 1; 247 | // 0 is represented by N; and 248 | // N - 1 (the biggest number representable using 249 | // sign-and-magnitude) is represented by 2N - 1. 250 | // 251 | // Read http://en.wikipedia.org/wiki/Signed_number_representations 252 | // for more details on signed number representations. 253 | static Bits SignAndMagnitudeToBiased(const Bits &sam) { 254 | if (kSignBitMask & sam) { 255 | // sam represents a negative number. 256 | return ~sam + 1; 257 | } 258 | else { 259 | // sam represents a positive number. 260 | return kSignBitMask | sam; 261 | } 262 | } 263 | 264 | // Given two numbers in the sign-and-magnitude representation, 265 | // returns the distance between them as an unsigned number. 266 | static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, 267 | const Bits &sam2) { 268 | const Bits biased1 = SignAndMagnitudeToBiased(sam1); 269 | const Bits biased2 = SignAndMagnitudeToBiased(sam2); 270 | return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); 271 | } 272 | 273 | FloatingPointUnion u_; 274 | }; 275 | 276 | // We cannot use std::numeric_limits::max() as it clashes with the max() 277 | // macro defined by . 278 | template <> 279 | inline float FloatingPoint::Max() { return FLT_MAX; } 280 | template <> 281 | inline double FloatingPoint::Max() { return DBL_MAX; } 282 | 283 | template 284 | bool AlmostEquals(T first, T second) 285 | { 286 | FloatingPoint firstAsFloatingPoint(first); 287 | FloatingPoint secondAsFloatingPoint(second); 288 | 289 | return firstAsFloatingPoint.AlmostEquals(secondAsFloatingPoint); 290 | } 291 | 292 | } /// namespace klib 293 | -------------------------------------------------------------------------------- /src/util/thirdparty/RTree.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * License 4 | * https://github.com/nushoin/RTree 5 | * Oiriginal code was taken from http://www.superliminal.com/sources/sources.htm and is stored as git revision 0. This revision is entirely free for all uses. Enjoy! 6 | 7 | * Due to restrictions on public domain in certain jurisdictions, code contributed by Yariv Barkan is released in these jurisdictions under the BSD, MIT or the GPL - you may choose one or more, whichever that suits you best. 8 | 9 | * In jurisdictions where public domain property is recognized, the user of this software may choose to accept it either 1) as public domain, 2) under the conditions of the BSD, MIT or GPL or 3) any combination of public domain and one or more of these licenses. 10 | 11 | * Thanks Baptiste Lepilleur for the licensing idea. 12 | */ 13 | 14 | 15 | #ifndef RTREE_H 16 | #define RTREE_H 17 | 18 | // NOTE This file compiles under MSVC 6 SP5 and MSVC .Net 2003 it may not work on other compilers without modification. 19 | 20 | // NOTE These next few lines may be win32 specific, you may need to modify them to compile on other platform 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | //#define assert assert // RTree uses assert( condition ) 30 | //#ifndef std::min 31 | // #define std::min std::min 32 | //#endif //std::min 33 | //#ifndef std::max 34 | // #define std::max std::max 35 | //#endif //std::max 36 | 37 | // 38 | // RTree.h 39 | // 40 | 41 | #define RTREE_TEMPLATE template 42 | #define RTREE_QUAL RTree 43 | 44 | #define RTREE_DONT_USE_MEMPOOLS // This version does not contain a fixed memory allocator, fill in lines with EXAMPLE to implement one. 45 | #define RTREE_USE_SPHERICAL_VOLUME // Better split classification, may be slower on some systems 46 | 47 | namespace klib 48 | { 49 | namespace thirdRtree 50 | { 51 | // Fwd decl 52 | class RTFileStream; // File I/O helper class, look below for implementation and notes. 53 | 54 | 55 | /// \class RTree 56 | /// Implementation of RTree, a multidimensional bounding rectangle tree. 57 | /// Example usage: For a 3-dimensional tree use RTree myTree; 58 | /// 59 | /// This modified, templated C++ version by Greg Douglas at Auran (http://www.auran.com) 60 | /// 61 | /// DATATYPE Referenced data, should be int, void*, obj* etc. no larger than sizeof and simple type 62 | /// ELEMTYPE Type of element such as int or float 63 | /// NUMDIMS Number of dimensions such as 2 or 3 64 | /// ELEMTYPEREAL Type of element that allows fractional and large values such as float or double, for use in volume calcs 65 | /// 66 | /// NOTES: Inserting and removing data requires the knowledge of its constant std::minimal Bounding Rectangle. 67 | /// This version uses new/delete for nodes, I recommend using a fixed size allocator for efficiency. 68 | /// Instead of using a callback function for returned results, I recommend and efficient pre-sized, grow-only memory 69 | /// array similar to MFC CArray or STL Vector for returning search query result. 70 | /// 71 | template 73 | class RTree 74 | { 75 | protected: 76 | 77 | struct Node; // Fwd decl. Used by other internal structs and iterator 78 | 79 | public: 80 | 81 | // These constant must be declared after Branch and before Node struct 82 | // Stuck up here for MSVC 6 compiler. NSVC .NET 2003 is much happier. 83 | enum 84 | { 85 | MAXNODES = TMAXNODES, ///< std::max elements in node 86 | MINNODES = TMINNODES, ///< std::min elements in node 87 | }; 88 | 89 | public: 90 | 91 | RTree(); 92 | RTree(const RTree& other); 93 | virtual ~RTree(); 94 | 95 | /// Insert entry 96 | /// \param a_min std::min of bounding rect 97 | /// \param a_max std::max of bounding rect 98 | /// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed. 99 | void Insert(const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], const DATATYPE& a_dataId); 100 | 101 | /// Remove entry 102 | /// \param a_min std::min of bounding rect 103 | /// \param a_max std::max of bounding rect 104 | /// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed. 105 | void Remove(const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], const DATATYPE& a_dataId); 106 | 107 | /// Find all within search rectangle 108 | /// \param a_min std::min of search bounding rect 109 | /// \param a_max std::max of search bounding rect 110 | /// \param a_searchResult Search result array. Caller should set grow size. Function will reset, not append to array. 111 | /// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching 112 | /// \param a_context User context to pass as parameter to a_resultCallback 113 | /// \return Returns the number of entries found 114 | int Search(const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], std::function callback) const; 115 | 116 | /// Remove all entries from tree 117 | void RemoveAll(); 118 | 119 | /// Count the data elements in this container. This is slow as no internal counter is maintained. 120 | int Count(); 121 | 122 | /// Load tree contents from file 123 | bool Load(const char* a_fileName); 124 | /// Load tree contents from stream 125 | bool Load(RTFileStream& a_stream); 126 | 127 | 128 | /// Save tree contents to file 129 | bool Save(const char* a_fileName); 130 | /// Save tree contents to stream 131 | bool Save(RTFileStream& a_stream); 132 | 133 | /// Iterator is not remove safe. 134 | class Iterator 135 | { 136 | private: 137 | 138 | enum { MAX_STACK = 32 }; // std::max stack size. Allows almost n^32 where n is number of branches in node 139 | 140 | struct StackElement 141 | { 142 | Node* m_node; 143 | int m_branchIndex; 144 | }; 145 | 146 | public: 147 | 148 | Iterator() { Init(); } 149 | 150 | ~Iterator() { } 151 | 152 | /// Is iterator invalid 153 | bool IsNull() { return (m_tos <= 0); } 154 | 155 | /// Is iterator pointing to valid data 156 | bool IsNotNull() { return (m_tos > 0); } 157 | 158 | /// Access the current data element. Caller must be sure iterator is not NULL first. 159 | DATATYPE& operator*() 160 | { 161 | assert(IsNotNull()); 162 | StackElement& curTos = m_stack[m_tos - 1]; 163 | return curTos.m_node->m_branch[curTos.m_branchIndex].m_data; 164 | } 165 | 166 | /// Access the current data element. Caller must be sure iterator is not NULL first. 167 | const DATATYPE& operator*() const 168 | { 169 | assert(IsNotNull()); 170 | StackElement& curTos = m_stack[m_tos - 1]; 171 | return curTos.m_node->m_branch[curTos.m_branchIndex].m_data; 172 | } 173 | 174 | /// Find the next data element 175 | bool operator++() { return FindNextData(); } 176 | 177 | /// Get the bounds for this node 178 | void GetBounds(ELEMTYPE a_min[NUMDIMS], ELEMTYPE a_max[NUMDIMS]) 179 | { 180 | assert(IsNotNull()); 181 | StackElement& curTos = m_stack[m_tos - 1]; 182 | Branch& curBranch = curTos.m_node->m_branch[curTos.m_branchIndex]; 183 | 184 | for(int index = 0; index < NUMDIMS; ++index) 185 | { 186 | a_min[index] = curBranch.m_rect.m_min[index]; 187 | a_max[index] = curBranch.m_rect.m_max[index]; 188 | } 189 | } 190 | 191 | private: 192 | 193 | /// Reset iterator 194 | void Init() { m_tos = 0; } 195 | 196 | /// Find the next data element in the tree (For internal use only) 197 | bool FindNextData() 198 | { 199 | for(;;) 200 | { 201 | if(m_tos <= 0) 202 | { 203 | return false; 204 | } 205 | StackElement curTos = Pop(); // Copy stack top cause it may change as we use it 206 | 207 | if(curTos.m_node->IsLeaf()) 208 | { 209 | // Keep walking through data while we can 210 | if(curTos.m_branchIndex+1 < curTos.m_node->m_count) 211 | { 212 | // There is more data, just point to the next one 213 | Push(curTos.m_node, curTos.m_branchIndex + 1); 214 | return true; 215 | } 216 | // No more data, so it will fall back to previous level 217 | } 218 | else 219 | { 220 | if(curTos.m_branchIndex+1 < curTos.m_node->m_count) 221 | { 222 | // Push sibling on for future tree walk 223 | // This is the 'fall back' node when we finish with the current level 224 | Push(curTos.m_node, curTos.m_branchIndex + 1); 225 | } 226 | // Since cur node is not a leaf, push first of next level to get deeper into the tree 227 | Node* nextLevelnode = curTos.m_node->m_branch[curTos.m_branchIndex].m_child; 228 | Push(nextLevelnode, 0); 229 | 230 | // If we pushed on a new leaf, exit as the data is ready at TOS 231 | if(nextLevelnode->IsLeaf()) 232 | { 233 | return true; 234 | } 235 | } 236 | } 237 | } 238 | 239 | /// Push node and branch onto iteration stack (For internal use only) 240 | void Push(Node* a_node, int a_branchIndex) 241 | { 242 | m_stack[m_tos].m_node = a_node; 243 | m_stack[m_tos].m_branchIndex = a_branchIndex; 244 | ++m_tos; 245 | assert(m_tos <= MAX_STACK); 246 | } 247 | 248 | /// Pop element off iteration stack (For internal use only) 249 | StackElement& Pop() 250 | { 251 | assert(m_tos > 0); 252 | --m_tos; 253 | return m_stack[m_tos]; 254 | } 255 | 256 | StackElement m_stack[MAX_STACK]; ///< Stack as we are doing iteration instead of recursion 257 | int m_tos; ///< Top Of Stack index 258 | 259 | friend class RTree; // Allow hiding of non-public functions while allowing manipulation by logical owner 260 | }; 261 | 262 | /// Get 'first' for iteration 263 | void GetFirst(Iterator& a_it) 264 | { 265 | a_it.Init(); 266 | Node* first = m_root; 267 | while(first) 268 | { 269 | if(first->IsInternalNode() && first->m_count > 1) 270 | { 271 | a_it.Push(first, 1); // Descend sibling branch later 272 | } 273 | else if(first->IsLeaf()) 274 | { 275 | if(first->m_count) 276 | { 277 | a_it.Push(first, 0); 278 | } 279 | break; 280 | } 281 | first = first->m_branch[0].m_child; 282 | } 283 | } 284 | 285 | /// Get Next for iteration 286 | void GetNext(Iterator& a_it) { ++a_it; } 287 | 288 | /// Is iterator NULL, or at end? 289 | bool IsNull(Iterator& a_it) { return a_it.IsNull(); } 290 | 291 | /// Get object at iterator position 292 | DATATYPE& GetAt(Iterator& a_it) { return *a_it; } 293 | 294 | protected: 295 | 296 | /// std::minimal bounding rectangle (n-dimensional) 297 | struct Rect 298 | { 299 | ELEMTYPE m_min[NUMDIMS]; ///< std::min dimensions of bounding box 300 | ELEMTYPE m_max[NUMDIMS]; ///< std::max dimensions of bounding box 301 | }; 302 | 303 | /// May be data or may be another subtree 304 | /// The parents level determines this. 305 | /// If the parents level is 0, then this is data 306 | struct Branch 307 | { 308 | Rect m_rect; ///< Bounds 309 | Node* m_child; ///< Child node 310 | DATATYPE m_data; ///< Data Id 311 | }; 312 | 313 | /// Node for each branch level 314 | struct Node 315 | { 316 | bool IsInternalNode() { return (m_level > 0); } // Not a leaf, but a internal node 317 | bool IsLeaf() { return (m_level == 0); } // A leaf, contains data 318 | 319 | int m_count; ///< Count 320 | int m_level; ///< Leaf is zero, others positive 321 | Branch m_branch[MAXNODES]; ///< Branch 322 | }; 323 | 324 | /// A link list of nodes for reinsertion after a delete operation 325 | struct ListNode 326 | { 327 | ListNode* m_next; ///< Next in list 328 | Node* m_node; ///< Node 329 | }; 330 | 331 | /// Variables for finding a split partition 332 | struct PartitionVars 333 | { 334 | enum { NOT_TAKEN = -1 }; // indicates that position 335 | 336 | int m_partition[MAXNODES+1]; 337 | int m_total; 338 | int m_minFill; 339 | int m_count[2]; 340 | Rect m_cover[2]; 341 | ELEMTYPEREAL m_area[2]; 342 | 343 | Branch m_branchBuf[MAXNODES+1]; 344 | int m_branchCount; 345 | Rect m_coverSplit; 346 | ELEMTYPEREAL m_coverSplitArea; 347 | }; 348 | 349 | Node* AllocNode(); 350 | void FreeNode(Node* a_node); 351 | void InitNode(Node* a_node); 352 | void InitRect(Rect* a_rect); 353 | bool InsertRectRec(const Branch& a_branch, Node* a_node, Node** a_newNode, int a_level); 354 | bool InsertRect(const Branch& a_branch, Node** a_root, int a_level); 355 | Rect NodeCover(Node* a_node); 356 | bool AddBranch(const Branch* a_branch, Node* a_node, Node** a_newNode); 357 | void DisconnectBranch(Node* a_node, int a_index); 358 | int PickBranch(const Rect* a_rect, Node* a_node); 359 | Rect CombineRect(const Rect* a_rectA, const Rect* a_rectB); 360 | void SplitNode(Node* a_node, const Branch* a_branch, Node** a_newNode); 361 | ELEMTYPEREAL RectSphericalVolume(Rect* a_rect); 362 | ELEMTYPEREAL RectVolume(Rect* a_rect); 363 | ELEMTYPEREAL CalcRectVolume(Rect* a_rect); 364 | void GetBranches(Node* a_node, const Branch* a_branch, PartitionVars* a_parVars); 365 | void ChoosePartition(PartitionVars* a_parVars, int a_minFill); 366 | void LoadNodes(Node* a_nodeA, Node* a_nodeB, PartitionVars* a_parVars); 367 | void InitParVars(PartitionVars* a_parVars, int a_maxRects, int a_minFill); 368 | void PickSeeds(PartitionVars* a_parVars); 369 | void Classify(int a_index, int a_group, PartitionVars* a_parVars); 370 | bool RemoveRect(Rect* a_rect, const DATATYPE& a_id, Node** a_root); 371 | bool RemoveRectRec(Rect* a_rect, const DATATYPE& a_id, Node* a_node, ListNode** a_listNode); 372 | ListNode* AllocListNode(); 373 | void FreeListNode(ListNode* a_listNode); 374 | bool Overlap(Rect* a_rectA, Rect* a_rectB) const; 375 | void ReInsert(Node* a_node, ListNode** a_listNode); 376 | bool Search(Node* a_node, Rect* a_rect, int& a_foundCount, std::function callback) const; 377 | void RemoveAllRec(Node* a_node); 378 | void Reset(); 379 | void CountRec(Node* a_node, int& a_count); 380 | 381 | bool SaveRec(Node* a_node, RTFileStream& a_stream); 382 | bool LoadRec(Node* a_node, RTFileStream& a_stream); 383 | void CopyRec(Node* current, Node* other); 384 | 385 | Node* m_root; ///< Root of tree 386 | ELEMTYPEREAL m_unitSphereVolume; ///< Unit sphere constant for required number of dimensions 387 | }; 388 | 389 | 390 | // Because there is not stream support, this is a quick and dirty file I/O helper. 391 | // Users will likely replace its usage with a Stream implementation from their favorite API. 392 | class RTFileStream 393 | { 394 | FILE* m_file; 395 | 396 | public: 397 | 398 | 399 | RTFileStream() 400 | { 401 | m_file = NULL; 402 | } 403 | 404 | ~RTFileStream() 405 | { 406 | Close(); 407 | } 408 | 409 | bool OpenRead(const char* a_fileName) 410 | { 411 | m_file = fopen(a_fileName, "rb"); 412 | if(!m_file) 413 | { 414 | return false; 415 | } 416 | return true; 417 | } 418 | 419 | bool OpenWrite(const char* a_fileName) 420 | { 421 | m_file = fopen(a_fileName, "wb"); 422 | if(!m_file) 423 | { 424 | return false; 425 | } 426 | return true; 427 | } 428 | 429 | void Close() 430 | { 431 | if(m_file) 432 | { 433 | fclose(m_file); 434 | m_file = NULL; 435 | } 436 | } 437 | 438 | template< typename TYPE > 439 | size_t Write(const TYPE& a_value) 440 | { 441 | assert(m_file); 442 | return fwrite((void*)&a_value, sizeof(a_value), 1, m_file); 443 | } 444 | 445 | template< typename TYPE > 446 | size_t WriteArray(const TYPE* a_array, int a_count) 447 | { 448 | assert(m_file); 449 | return fwrite((void*)a_array, sizeof(TYPE) * a_count, 1, m_file); 450 | } 451 | 452 | template< typename TYPE > 453 | size_t Read(TYPE& a_value) 454 | { 455 | assert(m_file); 456 | return fread((void*)&a_value, sizeof(a_value), 1, m_file); 457 | } 458 | 459 | template< typename TYPE > 460 | size_t ReadArray(TYPE* a_array, int a_count) 461 | { 462 | assert(m_file); 463 | return fread((void*)a_array, sizeof(TYPE) * a_count, 1, m_file); 464 | } 465 | }; 466 | 467 | 468 | RTREE_TEMPLATE 469 | RTREE_QUAL::RTree() 470 | { 471 | assert(MAXNODES > MINNODES); 472 | assert(MINNODES > 0); 473 | 474 | // Precomputed volumes of the unit spheres for the first few dimensions 475 | const float UNIT_SPHERE_VOLUMES[] = { 476 | 0.000000f, 2.000000f, 3.141593f, // Dimension 0,1,2 477 | 4.188790f, 4.934802f, 5.263789f, // Dimension 3,4,5 478 | 5.167713f, 4.724766f, 4.058712f, // Dimension 6,7,8 479 | 3.298509f, 2.550164f, 1.884104f, // Dimension 9,10,11 480 | 1.335263f, 0.910629f, 0.599265f, // Dimension 12,13,14 481 | 0.381443f, 0.235331f, 0.140981f, // Dimension 15,16,17 482 | 0.082146f, 0.046622f, 0.025807f, // Dimension 18,19,20 483 | }; 484 | 485 | m_root = AllocNode(); 486 | m_root->m_level = 0; 487 | m_unitSphereVolume = (ELEMTYPEREAL)UNIT_SPHERE_VOLUMES[NUMDIMS]; 488 | } 489 | 490 | 491 | RTREE_TEMPLATE 492 | RTREE_QUAL::RTree(const RTree& other) : RTree() 493 | { 494 | CopyRec(m_root, other.m_root); 495 | } 496 | 497 | 498 | RTREE_TEMPLATE 499 | RTREE_QUAL::~RTree() 500 | { 501 | Reset(); // Free, or reset node memory 502 | } 503 | 504 | 505 | RTREE_TEMPLATE 506 | void RTREE_QUAL::Insert(const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], const DATATYPE& a_dataId) 507 | { 508 | #ifdef _DEBUG 509 | for(int index=0; index callback) const 553 | { 554 | #ifdef _DEBUG 555 | for(int index=0; indexIsInternalNode()) // not a leaf node 593 | { 594 | for(int index = 0; index < a_node->m_count; ++index) 595 | { 596 | CountRec(a_node->m_branch[index].m_child, a_count); 597 | } 598 | } 599 | else // A leaf node 600 | { 601 | a_count += a_node->m_count; 602 | } 603 | } 604 | 605 | 606 | RTREE_TEMPLATE 607 | bool RTREE_QUAL::Load(const char* a_fileName) 608 | { 609 | RemoveAll(); // Clear existing tree 610 | 611 | RTFileStream stream; 612 | if(!stream.OpenRead(a_fileName)) 613 | { 614 | return false; 615 | } 616 | 617 | bool result = Load(stream); 618 | 619 | stream.Close(); 620 | 621 | return result; 622 | } 623 | 624 | 625 | 626 | RTREE_TEMPLATE 627 | bool RTREE_QUAL::Load(RTFileStream& a_stream) 628 | { 629 | // Write some kind of header 630 | int _dataFileId = ('R'<<0)|('T'<<8)|('R'<<16)|('E'<<24); 631 | int _dataSize = sizeof(DATATYPE); 632 | int _dataNumDims = NUMDIMS; 633 | int _dataElemSize = sizeof(ELEMTYPE); 634 | int _dataElemRealSize = sizeof(ELEMTYPEREAL); 635 | int _dataMaxNodes = TMAXNODES; 636 | int _dataMinNodes = TMINNODES; 637 | 638 | int dataFileId = 0; 639 | int dataSize = 0; 640 | int dataNumDims = 0; 641 | int dataElemSize = 0; 642 | int dataElemRealSize = 0; 643 | int dataMaxNodes = 0; 644 | int dataMinNodes = 0; 645 | 646 | a_stream.Read(dataFileId); 647 | a_stream.Read(dataSize); 648 | a_stream.Read(dataNumDims); 649 | a_stream.Read(dataElemSize); 650 | a_stream.Read(dataElemRealSize); 651 | a_stream.Read(dataMaxNodes); 652 | a_stream.Read(dataMinNodes); 653 | 654 | bool result = false; 655 | 656 | // Test if header was valid and compatible 657 | if( (dataFileId == _dataFileId) 658 | && (dataSize == _dataSize) 659 | && (dataNumDims == _dataNumDims) 660 | && (dataElemSize == _dataElemSize) 661 | && (dataElemRealSize == _dataElemRealSize) 662 | && (dataMaxNodes == _dataMaxNodes) 663 | && (dataMinNodes == _dataMinNodes) 664 | ) 665 | { 666 | // Recursively load tree 667 | result = LoadRec(m_root, a_stream); 668 | } 669 | 670 | return result; 671 | } 672 | 673 | 674 | RTREE_TEMPLATE 675 | bool RTREE_QUAL::LoadRec(Node* a_node, RTFileStream& a_stream) 676 | { 677 | a_stream.Read(a_node->m_level); 678 | a_stream.Read(a_node->m_count); 679 | 680 | if(a_node->IsInternalNode()) // not a leaf node 681 | { 682 | for(int index = 0; index < a_node->m_count; ++index) 683 | { 684 | Branch* curBranch = &a_node->m_branch[index]; 685 | 686 | a_stream.ReadArray(curBranch->m_rect.m_min, NUMDIMS); 687 | a_stream.ReadArray(curBranch->m_rect.m_max, NUMDIMS); 688 | 689 | curBranch->m_child = AllocNode(); 690 | LoadRec(curBranch->m_child, a_stream); 691 | } 692 | } 693 | else // A leaf node 694 | { 695 | for(int index = 0; index < a_node->m_count; ++index) 696 | { 697 | Branch* curBranch = &a_node->m_branch[index]; 698 | 699 | a_stream.ReadArray(curBranch->m_rect.m_min, NUMDIMS); 700 | a_stream.ReadArray(curBranch->m_rect.m_max, NUMDIMS); 701 | 702 | a_stream.Read(curBranch->m_data); 703 | } 704 | } 705 | 706 | return true; // Should do more error checking on I/O operations 707 | } 708 | 709 | 710 | RTREE_TEMPLATE 711 | void RTREE_QUAL::CopyRec(Node* current, Node* other) 712 | { 713 | current->m_level = other->m_level; 714 | current->m_count = other->m_count; 715 | 716 | if(current->IsInternalNode()) // not a leaf node 717 | { 718 | for(int index = 0; index < current->m_count; ++index) 719 | { 720 | Branch* currentBranch = ¤t->m_branch[index]; 721 | Branch* otherBranch = &other->m_branch[index]; 722 | 723 | std::copy(otherBranch->m_rect.m_min, 724 | otherBranch->m_rect.m_min + NUMDIMS, 725 | currentBranch->m_rect.m_min); 726 | 727 | std::copy(otherBranch->m_rect.m_max, 728 | otherBranch->m_rect.m_max + NUMDIMS, 729 | currentBranch->m_rect.m_max); 730 | 731 | currentBranch->m_child = AllocNode(); 732 | CopyRec(currentBranch->m_child, otherBranch->m_child); 733 | } 734 | } 735 | else // A leaf node 736 | { 737 | for(int index = 0; index < current->m_count; ++index) 738 | { 739 | Branch* currentBranch = ¤t->m_branch[index]; 740 | Branch* otherBranch = &other->m_branch[index]; 741 | 742 | std::copy(otherBranch->m_rect.m_min, 743 | otherBranch->m_rect.m_min + NUMDIMS, 744 | currentBranch->m_rect.m_min); 745 | 746 | std::copy(otherBranch->m_rect.m_max, 747 | otherBranch->m_rect.m_max + NUMDIMS, 748 | currentBranch->m_rect.m_max); 749 | 750 | currentBranch->m_data = otherBranch->m_data; 751 | } 752 | } 753 | } 754 | 755 | 756 | RTREE_TEMPLATE 757 | bool RTREE_QUAL::Save(const char* a_fileName) 758 | { 759 | RTFileStream stream; 760 | if(!stream.OpenWrite(a_fileName)) 761 | { 762 | return false; 763 | } 764 | 765 | bool result = Save(stream); 766 | 767 | stream.Close(); 768 | 769 | return result; 770 | } 771 | 772 | 773 | RTREE_TEMPLATE 774 | bool RTREE_QUAL::Save(RTFileStream& a_stream) 775 | { 776 | // Write some kind of header 777 | int dataFileId = ('R'<<0)|('T'<<8)|('R'<<16)|('E'<<24); 778 | int dataSize = sizeof(DATATYPE); 779 | int dataNumDims = NUMDIMS; 780 | int dataElemSize = sizeof(ELEMTYPE); 781 | int dataElemRealSize = sizeof(ELEMTYPEREAL); 782 | int dataMaxNodes = TMAXNODES; 783 | int dataMinNodes = TMINNODES; 784 | 785 | a_stream.Write(dataFileId); 786 | a_stream.Write(dataSize); 787 | a_stream.Write(dataNumDims); 788 | a_stream.Write(dataElemSize); 789 | a_stream.Write(dataElemRealSize); 790 | a_stream.Write(dataMaxNodes); 791 | a_stream.Write(dataMinNodes); 792 | 793 | // Recursively save tree 794 | bool result = SaveRec(m_root, a_stream); 795 | 796 | return result; 797 | } 798 | 799 | 800 | RTREE_TEMPLATE 801 | bool RTREE_QUAL::SaveRec(Node* a_node, RTFileStream& a_stream) 802 | { 803 | a_stream.Write(a_node->m_level); 804 | a_stream.Write(a_node->m_count); 805 | 806 | if(a_node->IsInternalNode()) // not a leaf node 807 | { 808 | for(int index = 0; index < a_node->m_count; ++index) 809 | { 810 | Branch* curBranch = &a_node->m_branch[index]; 811 | 812 | a_stream.WriteArray(curBranch->m_rect.m_min, NUMDIMS); 813 | a_stream.WriteArray(curBranch->m_rect.m_max, NUMDIMS); 814 | 815 | SaveRec(curBranch->m_child, a_stream); 816 | } 817 | } 818 | else // A leaf node 819 | { 820 | for(int index = 0; index < a_node->m_count; ++index) 821 | { 822 | Branch* curBranch = &a_node->m_branch[index]; 823 | 824 | a_stream.WriteArray(curBranch->m_rect.m_min, NUMDIMS); 825 | a_stream.WriteArray(curBranch->m_rect.m_max, NUMDIMS); 826 | 827 | a_stream.Write(curBranch->m_data); 828 | } 829 | } 830 | 831 | return true; // Should do more error checking on I/O operations 832 | } 833 | 834 | 835 | RTREE_TEMPLATE 836 | void RTREE_QUAL::RemoveAll() 837 | { 838 | // Delete all existing nodes 839 | Reset(); 840 | 841 | m_root = AllocNode(); 842 | m_root->m_level = 0; 843 | } 844 | 845 | 846 | RTREE_TEMPLATE 847 | void RTREE_QUAL::Reset() 848 | { 849 | #ifdef RTREE_DONT_USE_MEMPOOLS 850 | // Delete all existing nodes 851 | RemoveAllRec(m_root); 852 | #else // RTREE_DONT_USE_MEMPOOLS 853 | // Just reset memory pools. We are not using complex types 854 | // EXAMPLE 855 | #endif // RTREE_DONT_USE_MEMPOOLS 856 | } 857 | 858 | 859 | RTREE_TEMPLATE 860 | void RTREE_QUAL::RemoveAllRec(Node* a_node) 861 | { 862 | assert(a_node); 863 | assert(a_node->m_level >= 0); 864 | 865 | if(a_node->IsInternalNode()) // This is an internal node in the tree 866 | { 867 | for(int index=0; index < a_node->m_count; ++index) 868 | { 869 | RemoveAllRec(a_node->m_branch[index].m_child); 870 | } 871 | } 872 | FreeNode(a_node); 873 | } 874 | 875 | 876 | RTREE_TEMPLATE 877 | typename RTREE_QUAL::Node* RTREE_QUAL::AllocNode() 878 | { 879 | Node* newNode; 880 | #ifdef RTREE_DONT_USE_MEMPOOLS 881 | newNode = new Node; 882 | #else // RTREE_DONT_USE_MEMPOOLS 883 | // EXAMPLE 884 | #endif // RTREE_DONT_USE_MEMPOOLS 885 | InitNode(newNode); 886 | return newNode; 887 | } 888 | 889 | 890 | RTREE_TEMPLATE 891 | void RTREE_QUAL::FreeNode(Node* a_node) 892 | { 893 | assert(a_node); 894 | 895 | #ifdef RTREE_DONT_USE_MEMPOOLS 896 | delete a_node; 897 | #else // RTREE_DONT_USE_MEMPOOLS 898 | // EXAMPLE 899 | #endif // RTREE_DONT_USE_MEMPOOLS 900 | } 901 | 902 | 903 | // Allocate space for a node in the list used in DeletRect to 904 | // store Nodes that are too empty. 905 | RTREE_TEMPLATE 906 | typename RTREE_QUAL::ListNode* RTREE_QUAL::AllocListNode() 907 | { 908 | #ifdef RTREE_DONT_USE_MEMPOOLS 909 | return new ListNode; 910 | #else // RTREE_DONT_USE_MEMPOOLS 911 | // EXAMPLE 912 | #endif // RTREE_DONT_USE_MEMPOOLS 913 | } 914 | 915 | 916 | RTREE_TEMPLATE 917 | void RTREE_QUAL::FreeListNode(ListNode* a_listNode) 918 | { 919 | #ifdef RTREE_DONT_USE_MEMPOOLS 920 | delete a_listNode; 921 | #else // RTREE_DONT_USE_MEMPOOLS 922 | // EXAMPLE 923 | #endif // RTREE_DONT_USE_MEMPOOLS 924 | } 925 | 926 | 927 | RTREE_TEMPLATE 928 | void RTREE_QUAL::InitNode(Node* a_node) 929 | { 930 | a_node->m_count = 0; 931 | a_node->m_level = -1; 932 | } 933 | 934 | 935 | RTREE_TEMPLATE 936 | void RTREE_QUAL::InitRect(Rect* a_rect) 937 | { 938 | for(int index = 0; index < NUMDIMS; ++index) 939 | { 940 | a_rect->m_min[index] = (ELEMTYPE)0; 941 | a_rect->m_max[index] = (ELEMTYPE)0; 942 | } 943 | } 944 | 945 | 946 | // Inserts a new data rectangle into the index structure. 947 | // Recursively descends tree, propagates splits back up. 948 | // Returns 0 if node was not split. Old node updated. 949 | // If node was split, returns 1 and sets the pointer pointed to by 950 | // new_node to point to the new node. Old node updated to become one of two. 951 | // The level argument specifies the number of steps up from the leaf 952 | // level to insert; e.g. a data rectangle goes in at level = 0. 953 | RTREE_TEMPLATE 954 | bool RTREE_QUAL::InsertRectRec(const Branch& a_branch, Node* a_node, Node** a_newNode, int a_level) 955 | { 956 | assert(a_node && a_newNode); 957 | assert(a_level >= 0 && a_level <= a_node->m_level); 958 | 959 | // recurse until we reach the correct level for the new record. data records 960 | // will always be called with a_level == 0 (leaf) 961 | if(a_node->m_level > a_level) 962 | { 963 | // Still above level for insertion, go down tree recursively 964 | Node* otherNode; 965 | 966 | // find the optimal branch for this record 967 | int index = PickBranch(&a_branch.m_rect, a_node); 968 | 969 | // recursively insert this record into the picked branch 970 | bool childWasSplit = InsertRectRec(a_branch, a_node->m_branch[index].m_child, &otherNode, a_level); 971 | 972 | if (!childWasSplit) 973 | { 974 | // Child was not split. Merge the bounding box of the new record with the 975 | // existing bounding box 976 | a_node->m_branch[index].m_rect = CombineRect(&a_branch.m_rect, &(a_node->m_branch[index].m_rect)); 977 | return false; 978 | } 979 | else 980 | { 981 | // Child was split. The old branches are now re-partitioned to two nodes 982 | // so we have to re-calculate the bounding boxes of each node 983 | a_node->m_branch[index].m_rect = NodeCover(a_node->m_branch[index].m_child); 984 | Branch branch; 985 | branch.m_child = otherNode; 986 | branch.m_rect = NodeCover(otherNode); 987 | 988 | // The old node is already a child of a_node. Now add the newly-created 989 | // node to a_node as well. a_node might be split because of that. 990 | return AddBranch(&branch, a_node, a_newNode); 991 | } 992 | } 993 | else if(a_node->m_level == a_level) 994 | { 995 | // We have reached level for insertion. Add rect, split if necessary 996 | return AddBranch(&a_branch, a_node, a_newNode); 997 | } 998 | else 999 | { 1000 | // Should never occur 1001 | assert(0); 1002 | return false; 1003 | } 1004 | } 1005 | 1006 | 1007 | // Insert a data rectangle into an index structure. 1008 | // InsertRect provides for splitting the root; 1009 | // returns 1 if root was split, 0 if it was not. 1010 | // The level argument specifies the number of steps up from the leaf 1011 | // level to insert; e.g. a data rectangle goes in at level = 0. 1012 | // InsertRect2 does the recursion. 1013 | // 1014 | RTREE_TEMPLATE 1015 | bool RTREE_QUAL::InsertRect(const Branch& a_branch, Node** a_root, int a_level) 1016 | { 1017 | assert(a_root); 1018 | assert(a_level >= 0 && a_level <= (*a_root)->m_level); 1019 | #ifdef _DEBUG 1020 | for(int index=0; index < NUMDIMS; ++index) 1021 | { 1022 | assert(a_branch.m_rect.m_min[index] <= a_branch.m_rect.m_max[index]); 1023 | } 1024 | #endif //_DEBUG 1025 | 1026 | Node* newNode; 1027 | 1028 | if(InsertRectRec(a_branch, *a_root, &newNode, a_level)) // Root split 1029 | { 1030 | // Grow tree taller and new root 1031 | Node* newRoot = AllocNode(); 1032 | newRoot->m_level = (*a_root)->m_level + 1; 1033 | 1034 | Branch branch; 1035 | 1036 | // add old root node as a child of the new root 1037 | branch.m_rect = NodeCover(*a_root); 1038 | branch.m_child = *a_root; 1039 | AddBranch(&branch, newRoot, NULL); 1040 | 1041 | // add the split node as a child of the new root 1042 | branch.m_rect = NodeCover(newNode); 1043 | branch.m_child = newNode; 1044 | AddBranch(&branch, newRoot, NULL); 1045 | 1046 | // set the new root as the root node 1047 | *a_root = newRoot; 1048 | 1049 | return true; 1050 | } 1051 | 1052 | return false; 1053 | } 1054 | 1055 | 1056 | // Find the smallest rectangle that includes all rectangles in branches of a node. 1057 | RTREE_TEMPLATE 1058 | typename RTREE_QUAL::Rect RTREE_QUAL::NodeCover(Node* a_node) 1059 | { 1060 | assert(a_node); 1061 | 1062 | Rect rect = a_node->m_branch[0].m_rect; 1063 | for(int index = 1; index < a_node->m_count; ++index) 1064 | { 1065 | rect = CombineRect(&rect, &(a_node->m_branch[index].m_rect)); 1066 | } 1067 | 1068 | return rect; 1069 | } 1070 | 1071 | 1072 | // Add a branch to a node. Split the node if necessary. 1073 | // Returns 0 if node not split. Old node updated. 1074 | // Returns 1 if node split, sets *new_node to address of new node. 1075 | // Old node updated, becomes one of two. 1076 | RTREE_TEMPLATE 1077 | bool RTREE_QUAL::AddBranch(const Branch* a_branch, Node* a_node, Node** a_newNode) 1078 | { 1079 | assert(a_branch); 1080 | assert(a_node); 1081 | 1082 | if(a_node->m_count < MAXNODES) // Split won't be necessary 1083 | { 1084 | a_node->m_branch[a_node->m_count] = *a_branch; 1085 | ++a_node->m_count; 1086 | 1087 | return false; 1088 | } 1089 | else 1090 | { 1091 | assert(a_newNode); 1092 | 1093 | SplitNode(a_node, a_branch, a_newNode); 1094 | return true; 1095 | } 1096 | } 1097 | 1098 | 1099 | // Disconnect a dependent node. 1100 | // Caller must return (or stop using iteration index) after this as count has changed 1101 | RTREE_TEMPLATE 1102 | void RTREE_QUAL::DisconnectBranch(Node* a_node, int a_index) 1103 | { 1104 | assert(a_node && (a_index >= 0) && (a_index < MAXNODES)); 1105 | assert(a_node->m_count > 0); 1106 | 1107 | // Remove element by swapping with the last element to prevent gaps in array 1108 | a_node->m_branch[a_index] = a_node->m_branch[a_node->m_count - 1]; 1109 | 1110 | --a_node->m_count; 1111 | } 1112 | 1113 | 1114 | // Pick a branch. Pick the one that will need the smallest increase 1115 | // in area to accomodate the new rectangle. This will result in the 1116 | // least total area for the covering rectangles in the current node. 1117 | // In case of a tie, pick the one which was smaller before, to get 1118 | // the best resolution when searching. 1119 | RTREE_TEMPLATE 1120 | int RTREE_QUAL::PickBranch(const Rect* a_rect, Node* a_node) 1121 | { 1122 | assert(a_rect && a_node); 1123 | 1124 | bool firstTime = true; 1125 | ELEMTYPEREAL increase; 1126 | ELEMTYPEREAL bestIncr = (ELEMTYPEREAL)-1; 1127 | ELEMTYPEREAL area; 1128 | ELEMTYPEREAL bestArea; 1129 | int best = 0; 1130 | Rect tempRect; 1131 | 1132 | for(int index=0; index < a_node->m_count; ++index) 1133 | { 1134 | Rect* curRect = &a_node->m_branch[index].m_rect; 1135 | area = CalcRectVolume(curRect); 1136 | tempRect = CombineRect(a_rect, curRect); 1137 | increase = CalcRectVolume(&tempRect) - area; 1138 | if((increase < bestIncr) || firstTime) 1139 | { 1140 | best = index; 1141 | bestArea = area; 1142 | bestIncr = increase; 1143 | firstTime = false; 1144 | } 1145 | else if((increase == bestIncr) && (area < bestArea)) 1146 | { 1147 | best = index; 1148 | bestArea = area; 1149 | bestIncr = increase; 1150 | } 1151 | } 1152 | return best; 1153 | } 1154 | 1155 | 1156 | // Combine two rectangles into larger one containing both 1157 | RTREE_TEMPLATE 1158 | typename RTREE_QUAL::Rect RTREE_QUAL::CombineRect(const Rect* a_rectA, const Rect* a_rectB) 1159 | { 1160 | assert(a_rectA && a_rectB); 1161 | 1162 | Rect newRect; 1163 | 1164 | for(int index = 0; index < NUMDIMS; ++index) 1165 | { 1166 | newRect.m_min[index] = std::min(a_rectA->m_min[index], a_rectB->m_min[index]); 1167 | newRect.m_max[index] = std::max(a_rectA->m_max[index], a_rectB->m_max[index]); 1168 | } 1169 | 1170 | return newRect; 1171 | } 1172 | 1173 | 1174 | 1175 | // Split a node. 1176 | // Divides the nodes branches and the extra one between two nodes. 1177 | // Old node is one of the new ones, and one really new one is created. 1178 | // Tries more than one method for choosing a partition, uses best result. 1179 | RTREE_TEMPLATE 1180 | void RTREE_QUAL::SplitNode(Node* a_node, const Branch* a_branch, Node** a_newNode) 1181 | { 1182 | assert(a_node); 1183 | assert(a_branch); 1184 | 1185 | // Could just use local here, but member or external is faster since it is reused 1186 | PartitionVars localVars; 1187 | PartitionVars* parVars = &localVars; 1188 | 1189 | // Load all the branches into a buffer, initialize old node 1190 | GetBranches(a_node, a_branch, parVars); 1191 | 1192 | // Find partition 1193 | ChoosePartition(parVars, MINNODES); 1194 | 1195 | // Create a new node to hold (about) half of the branches 1196 | *a_newNode = AllocNode(); 1197 | (*a_newNode)->m_level = a_node->m_level; 1198 | 1199 | // Put branches from buffer into 2 nodes according to the chosen partition 1200 | a_node->m_count = 0; 1201 | LoadNodes(a_node, *a_newNode, parVars); 1202 | 1203 | assert((a_node->m_count + (*a_newNode)->m_count) == parVars->m_total); 1204 | } 1205 | 1206 | 1207 | // Calculate the n-dimensional volume of a rectangle 1208 | RTREE_TEMPLATE 1209 | ELEMTYPEREAL RTREE_QUAL::RectVolume(Rect* a_rect) 1210 | { 1211 | assert(a_rect); 1212 | 1213 | ELEMTYPEREAL volume = (ELEMTYPEREAL)1; 1214 | 1215 | for(int index=0; indexm_max[index] - a_rect->m_min[index]; 1218 | } 1219 | 1220 | assert(volume >= (ELEMTYPEREAL)0); 1221 | 1222 | return volume; 1223 | } 1224 | 1225 | 1226 | // The exact volume of the bounding sphere for the given Rect 1227 | RTREE_TEMPLATE 1228 | ELEMTYPEREAL RTREE_QUAL::RectSphericalVolume(Rect* a_rect) 1229 | { 1230 | assert(a_rect); 1231 | 1232 | ELEMTYPEREAL sumOfSquares = (ELEMTYPEREAL)0; 1233 | ELEMTYPEREAL radius; 1234 | 1235 | for(int index=0; index < NUMDIMS; ++index) 1236 | { 1237 | ELEMTYPEREAL halfExtent = ((ELEMTYPEREAL)a_rect->m_max[index] - (ELEMTYPEREAL)a_rect->m_min[index]) * 0.5f; 1238 | sumOfSquares += halfExtent * halfExtent; 1239 | } 1240 | 1241 | radius = (ELEMTYPEREAL)sqrt(sumOfSquares); 1242 | 1243 | // Pow maybe slow, so test for common dims like 2,3 and just use x*x, x*x*x. 1244 | if(NUMDIMS == 3) 1245 | { 1246 | return (radius * radius * radius * m_unitSphereVolume); 1247 | } 1248 | else if(NUMDIMS == 2) 1249 | { 1250 | return (radius * radius * m_unitSphereVolume); 1251 | } 1252 | else 1253 | { 1254 | return (ELEMTYPEREAL)(pow(radius, NUMDIMS) * m_unitSphereVolume); 1255 | } 1256 | } 1257 | 1258 | 1259 | // Use one of the methods to calculate retangle volume 1260 | RTREE_TEMPLATE 1261 | ELEMTYPEREAL RTREE_QUAL::CalcRectVolume(Rect* a_rect) 1262 | { 1263 | #ifdef RTREE_USE_SPHERICAL_VOLUME 1264 | return RectSphericalVolume(a_rect); // Slower but helps certain merge cases 1265 | #else // RTREE_USE_SPHERICAL_VOLUME 1266 | return RectVolume(a_rect); // Faster but can cause poor merges 1267 | #endif // RTREE_USE_SPHERICAL_VOLUME 1268 | } 1269 | 1270 | 1271 | // Load branch buffer with branches from full node plus the extra branch. 1272 | RTREE_TEMPLATE 1273 | void RTREE_QUAL::GetBranches(Node* a_node, const Branch* a_branch, PartitionVars* a_parVars) 1274 | { 1275 | assert(a_node); 1276 | assert(a_branch); 1277 | 1278 | assert(a_node->m_count == MAXNODES); 1279 | 1280 | // Load the branch buffer 1281 | for(int index=0; index < MAXNODES; ++index) 1282 | { 1283 | a_parVars->m_branchBuf[index] = a_node->m_branch[index]; 1284 | } 1285 | a_parVars->m_branchBuf[MAXNODES] = *a_branch; 1286 | a_parVars->m_branchCount = MAXNODES + 1; 1287 | 1288 | // Calculate rect containing all in the set 1289 | a_parVars->m_coverSplit = a_parVars->m_branchBuf[0].m_rect; 1290 | for(int index=1; index < MAXNODES+1; ++index) 1291 | { 1292 | a_parVars->m_coverSplit = CombineRect(&a_parVars->m_coverSplit, &a_parVars->m_branchBuf[index].m_rect); 1293 | } 1294 | a_parVars->m_coverSplitArea = CalcRectVolume(&a_parVars->m_coverSplit); 1295 | } 1296 | 1297 | 1298 | // Method #0 for choosing a partition: 1299 | // As the seeds for the two groups, pick the two rects that would waste the 1300 | // most area if covered by a single rectangle, i.e. evidently the worst pair 1301 | // to have in the same group. 1302 | // Of the remaining, one at a time is chosen to be put in one of the two groups. 1303 | // The one chosen is the one with the greatest difference in area expansion 1304 | // depending on which group - the rect most strongly attracted to one group 1305 | // and repelled from the other. 1306 | // If one group gets too full (more would force other group to violate min 1307 | // fill requirement) then other group gets the rest. 1308 | // These last are the ones that can go in either group most easily. 1309 | RTREE_TEMPLATE 1310 | void RTREE_QUAL::ChoosePartition(PartitionVars* a_parVars, int a_minFill) 1311 | { 1312 | assert(a_parVars); 1313 | 1314 | ELEMTYPEREAL biggestDiff; 1315 | int group, chosen = 0, betterGroup = 0; 1316 | 1317 | InitParVars(a_parVars, a_parVars->m_branchCount, a_minFill); 1318 | PickSeeds(a_parVars); 1319 | 1320 | while (((a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total) 1321 | && (a_parVars->m_count[0] < (a_parVars->m_total - a_parVars->m_minFill)) 1322 | && (a_parVars->m_count[1] < (a_parVars->m_total - a_parVars->m_minFill))) 1323 | { 1324 | biggestDiff = (ELEMTYPEREAL) -1; 1325 | for(int index=0; indexm_total; ++index) 1326 | { 1327 | if(PartitionVars::NOT_TAKEN == a_parVars->m_partition[index]) 1328 | { 1329 | Rect* curRect = &a_parVars->m_branchBuf[index].m_rect; 1330 | Rect rect0 = CombineRect(curRect, &a_parVars->m_cover[0]); 1331 | Rect rect1 = CombineRect(curRect, &a_parVars->m_cover[1]); 1332 | ELEMTYPEREAL growth0 = CalcRectVolume(&rect0) - a_parVars->m_area[0]; 1333 | ELEMTYPEREAL growth1 = CalcRectVolume(&rect1) - a_parVars->m_area[1]; 1334 | ELEMTYPEREAL diff = growth1 - growth0; 1335 | if(diff >= 0) 1336 | { 1337 | group = 0; 1338 | } 1339 | else 1340 | { 1341 | group = 1; 1342 | diff = -diff; 1343 | } 1344 | 1345 | if(diff > biggestDiff) 1346 | { 1347 | biggestDiff = diff; 1348 | chosen = index; 1349 | betterGroup = group; 1350 | } 1351 | else if((diff == biggestDiff) && (a_parVars->m_count[group] < a_parVars->m_count[betterGroup])) 1352 | { 1353 | chosen = index; 1354 | betterGroup = group; 1355 | } 1356 | } 1357 | } 1358 | Classify(chosen, betterGroup, a_parVars); 1359 | } 1360 | 1361 | // If one group too full, put remaining rects in the other 1362 | if((a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total) 1363 | { 1364 | if(a_parVars->m_count[0] >= a_parVars->m_total - a_parVars->m_minFill) 1365 | { 1366 | group = 1; 1367 | } 1368 | else 1369 | { 1370 | group = 0; 1371 | } 1372 | for(int index=0; indexm_total; ++index) 1373 | { 1374 | if(PartitionVars::NOT_TAKEN == a_parVars->m_partition[index]) 1375 | { 1376 | Classify(index, group, a_parVars); 1377 | } 1378 | } 1379 | } 1380 | 1381 | assert((a_parVars->m_count[0] + a_parVars->m_count[1]) == a_parVars->m_total); 1382 | assert((a_parVars->m_count[0] >= a_parVars->m_minFill) && 1383 | (a_parVars->m_count[1] >= a_parVars->m_minFill)); 1384 | } 1385 | 1386 | 1387 | // Copy branches from the buffer into two nodes according to the partition. 1388 | RTREE_TEMPLATE 1389 | void RTREE_QUAL::LoadNodes(Node* a_nodeA, Node* a_nodeB, PartitionVars* a_parVars) 1390 | { 1391 | assert(a_nodeA); 1392 | assert(a_nodeB); 1393 | assert(a_parVars); 1394 | 1395 | for(int index=0; index < a_parVars->m_total; ++index) 1396 | { 1397 | assert(a_parVars->m_partition[index] == 0 || a_parVars->m_partition[index] == 1); 1398 | 1399 | int targetNodeIndex = a_parVars->m_partition[index]; 1400 | Node* targetNodes[] = {a_nodeA, a_nodeB}; 1401 | 1402 | // It is assured that AddBranch here will not cause a node split. 1403 | bool nodeWasSplit = AddBranch(&a_parVars->m_branchBuf[index], targetNodes[targetNodeIndex], NULL); 1404 | assert(!nodeWasSplit); 1405 | } 1406 | } 1407 | 1408 | 1409 | // Initialize a PartitionVars structure. 1410 | RTREE_TEMPLATE 1411 | void RTREE_QUAL::InitParVars(PartitionVars* a_parVars, int a_maxRects, int a_minFill) 1412 | { 1413 | assert(a_parVars); 1414 | 1415 | a_parVars->m_count[0] = a_parVars->m_count[1] = 0; 1416 | a_parVars->m_area[0] = a_parVars->m_area[1] = (ELEMTYPEREAL)0; 1417 | a_parVars->m_total = a_maxRects; 1418 | a_parVars->m_minFill = a_minFill; 1419 | for(int index=0; index < a_maxRects; ++index) 1420 | { 1421 | a_parVars->m_partition[index] = PartitionVars::NOT_TAKEN; 1422 | } 1423 | } 1424 | 1425 | 1426 | RTREE_TEMPLATE 1427 | void RTREE_QUAL::PickSeeds(PartitionVars* a_parVars) 1428 | { 1429 | int seed0 = 0, seed1 = 0; 1430 | ELEMTYPEREAL worst, waste; 1431 | ELEMTYPEREAL area[MAXNODES+1]; 1432 | 1433 | for(int index=0; indexm_total; ++index) 1434 | { 1435 | area[index] = CalcRectVolume(&a_parVars->m_branchBuf[index].m_rect); 1436 | } 1437 | 1438 | worst = -a_parVars->m_coverSplitArea - 1; 1439 | for(int indexA=0; indexA < a_parVars->m_total-1; ++indexA) 1440 | { 1441 | for(int indexB = indexA+1; indexB < a_parVars->m_total; ++indexB) 1442 | { 1443 | Rect oneRect = CombineRect(&a_parVars->m_branchBuf[indexA].m_rect, &a_parVars->m_branchBuf[indexB].m_rect); 1444 | waste = CalcRectVolume(&oneRect) - area[indexA] - area[indexB]; 1445 | if(waste > worst) 1446 | { 1447 | worst = waste; 1448 | seed0 = indexA; 1449 | seed1 = indexB; 1450 | } 1451 | } 1452 | } 1453 | 1454 | Classify(seed0, 0, a_parVars); 1455 | Classify(seed1, 1, a_parVars); 1456 | } 1457 | 1458 | 1459 | // Put a branch in one of the groups. 1460 | RTREE_TEMPLATE 1461 | void RTREE_QUAL::Classify(int a_index, int a_group, PartitionVars* a_parVars) 1462 | { 1463 | assert(a_parVars); 1464 | assert(PartitionVars::NOT_TAKEN == a_parVars->m_partition[a_index]); 1465 | 1466 | a_parVars->m_partition[a_index] = a_group; 1467 | 1468 | // Calculate combined rect 1469 | if (a_parVars->m_count[a_group] == 0) 1470 | { 1471 | a_parVars->m_cover[a_group] = a_parVars->m_branchBuf[a_index].m_rect; 1472 | } 1473 | else 1474 | { 1475 | a_parVars->m_cover[a_group] = CombineRect(&a_parVars->m_branchBuf[a_index].m_rect, &a_parVars->m_cover[a_group]); 1476 | } 1477 | 1478 | // Calculate volume of combined rect 1479 | a_parVars->m_area[a_group] = CalcRectVolume(&a_parVars->m_cover[a_group]); 1480 | 1481 | ++a_parVars->m_count[a_group]; 1482 | } 1483 | 1484 | 1485 | // Delete a data rectangle from an index structure. 1486 | // Pass in a pointer to a Rect, the tid of the record, ptr to ptr to root node. 1487 | // Returns 1 if record not found, 0 if success. 1488 | // RemoveRect provides for eliminating the root. 1489 | RTREE_TEMPLATE 1490 | bool RTREE_QUAL::RemoveRect(Rect* a_rect, const DATATYPE& a_id, Node** a_root) 1491 | { 1492 | assert(a_rect && a_root); 1493 | assert(*a_root); 1494 | 1495 | ListNode* reInsertList = NULL; 1496 | 1497 | if(!RemoveRectRec(a_rect, a_id, *a_root, &reInsertList)) 1498 | { 1499 | // Found and deleted a data item 1500 | // Reinsert any branches from eliminated nodes 1501 | while(reInsertList) 1502 | { 1503 | Node* tempNode = reInsertList->m_node; 1504 | 1505 | for(int index = 0; index < tempNode->m_count; ++index) 1506 | { 1507 | // TODO go over this code. should I use (tempNode->m_level - 1)? 1508 | InsertRect(tempNode->m_branch[index], 1509 | a_root, 1510 | tempNode->m_level); 1511 | } 1512 | 1513 | ListNode* remLNode = reInsertList; 1514 | reInsertList = reInsertList->m_next; 1515 | 1516 | FreeNode(remLNode->m_node); 1517 | FreeListNode(remLNode); 1518 | } 1519 | 1520 | // Check for redundant root (not leaf, 1 child) and eliminate TODO replace 1521 | // if with while? In case there is a whole branch of redundant roots... 1522 | if((*a_root)->m_count == 1 && (*a_root)->IsInternalNode()) 1523 | { 1524 | Node* tempNode = (*a_root)->m_branch[0].m_child; 1525 | 1526 | assert(tempNode); 1527 | FreeNode(*a_root); 1528 | *a_root = tempNode; 1529 | } 1530 | return false; 1531 | } 1532 | else 1533 | { 1534 | return true; 1535 | } 1536 | } 1537 | 1538 | 1539 | // Delete a rectangle from non-root part of an index structure. 1540 | // Called by RemoveRect. Descends tree recursively, 1541 | // merges branches on the way back up. 1542 | // Returns 1 if record not found, 0 if success. 1543 | RTREE_TEMPLATE 1544 | bool RTREE_QUAL::RemoveRectRec(Rect* a_rect, const DATATYPE& a_id, Node* a_node, ListNode** a_listNode) 1545 | { 1546 | assert(a_rect && a_node && a_listNode); 1547 | assert(a_node->m_level >= 0); 1548 | 1549 | if(a_node->IsInternalNode()) // not a leaf node 1550 | { 1551 | for(int index = 0; index < a_node->m_count; ++index) 1552 | { 1553 | if(Overlap(a_rect, &(a_node->m_branch[index].m_rect))) 1554 | { 1555 | if(!RemoveRectRec(a_rect, a_id, a_node->m_branch[index].m_child, a_listNode)) 1556 | { 1557 | if(a_node->m_branch[index].m_child->m_count >= MINNODES) 1558 | { 1559 | // child removed, just resize parent rect 1560 | a_node->m_branch[index].m_rect = NodeCover(a_node->m_branch[index].m_child); 1561 | } 1562 | else 1563 | { 1564 | // child removed, not enough entries in node, eliminate node 1565 | ReInsert(a_node->m_branch[index].m_child, a_listNode); 1566 | DisconnectBranch(a_node, index); // Must return after this call as count has changed 1567 | } 1568 | return false; 1569 | } 1570 | } 1571 | } 1572 | return true; 1573 | } 1574 | else // A leaf node 1575 | { 1576 | for(int index = 0; index < a_node->m_count; ++index) 1577 | { 1578 | if(a_node->m_branch[index].m_data == a_id) 1579 | { 1580 | DisconnectBranch(a_node, index); // Must return after this call as count has changed 1581 | return false; 1582 | } 1583 | } 1584 | return true; 1585 | } 1586 | } 1587 | 1588 | 1589 | // Decide whether two rectangles overlap. 1590 | RTREE_TEMPLATE 1591 | bool RTREE_QUAL::Overlap(Rect* a_rectA, Rect* a_rectB) const 1592 | { 1593 | assert(a_rectA && a_rectB); 1594 | 1595 | for(int index=0; index < NUMDIMS; ++index) 1596 | { 1597 | if (a_rectA->m_min[index] > a_rectB->m_max[index] || 1598 | a_rectB->m_min[index] > a_rectA->m_max[index]) 1599 | { 1600 | return false; 1601 | } 1602 | } 1603 | return true; 1604 | } 1605 | 1606 | 1607 | // Add a node to the reinsertion list. All its branches will later 1608 | // be reinserted into the index structure. 1609 | RTREE_TEMPLATE 1610 | void RTREE_QUAL::ReInsert(Node* a_node, ListNode** a_listNode) 1611 | { 1612 | ListNode* newListNode; 1613 | 1614 | newListNode = AllocListNode(); 1615 | newListNode->m_node = a_node; 1616 | newListNode->m_next = *a_listNode; 1617 | *a_listNode = newListNode; 1618 | } 1619 | 1620 | 1621 | // Search in an index tree or subtree for all data retangles that overlap the argument rectangle. 1622 | RTREE_TEMPLATE 1623 | bool RTREE_QUAL::Search(Node* a_node, Rect* a_rect, int& a_foundCount, std::function callback) const 1624 | { 1625 | assert(a_node); 1626 | assert(a_node->m_level >= 0); 1627 | assert(a_rect); 1628 | 1629 | if(a_node->IsInternalNode()) 1630 | { 1631 | // This is an internal node in the tree 1632 | for(int index=0; index < a_node->m_count; ++index) 1633 | { 1634 | if(Overlap(a_rect, &a_node->m_branch[index].m_rect)) 1635 | { 1636 | if(!Search(a_node->m_branch[index].m_child, a_rect, a_foundCount, callback)) 1637 | { 1638 | // The callback indicated to stop searching 1639 | return false; 1640 | } 1641 | } 1642 | } 1643 | } 1644 | else 1645 | { 1646 | // This is a leaf node 1647 | for(int index=0; index < a_node->m_count; ++index) 1648 | { 1649 | if(Overlap(a_rect, &a_node->m_branch[index].m_rect)) 1650 | { 1651 | DATATYPE& id = a_node->m_branch[index].m_data; 1652 | ++a_foundCount; 1653 | 1654 | if(callback && !callback(id)) 1655 | { 1656 | return false; // Don't continue searching 1657 | } 1658 | } 1659 | } 1660 | } 1661 | 1662 | return true; // Continue searching 1663 | } 1664 | 1665 | }}/// end of namespace klib { end of namespace thirdRtree 1666 | 1667 | #undef RTREE_TEMPLATE 1668 | #undef RTREE_QUAL 1669 | 1670 | #endif //RTREE_H 1671 | 1672 | -------------------------------------------------------------------------------- /src/util/thirdparty/StdCapture.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @file StdCapture.h 3 | * @brief utility to catch the stdout into string 4 | * From https://stackoverflow.com/questions/5419356/redirect-stdout-stderr-to-a-string 5 | * 6 | */ 7 | 8 | #ifndef KLIB_STD_CAPTURE_H_ 9 | #define KLIB_STD_CAPTURE_H_ 10 | #include 11 | #include 12 | #include 13 | #include 14 | namespace klib { 15 | 16 | 17 | } //namespace klib 18 | 19 | #endif //KLIB_STD_CAPTURE_H_ 20 | -------------------------------------------------------------------------------- /src/util/thirdparty/cmdline.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009, Hideyuki Tanaka 3 | All rights reserved. 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY 15 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | // https://github.com/tanakh/cmdline 27 | 28 | #pragma once 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | namespace cmdline{ 43 | 44 | namespace detail{ 45 | 46 | template 47 | class lexical_cast_t{ 48 | public: 49 | static Target cast(const Source &arg){ 50 | Target ret; 51 | std::stringstream ss; 52 | if (!(ss<>ret && ss.eof())) 53 | throw std::bad_cast(); 54 | 55 | return ret; 56 | } 57 | }; 58 | 59 | template 60 | class lexical_cast_t{ 61 | public: 62 | static Target cast(const Source &arg){ 63 | return arg; 64 | } 65 | }; 66 | 67 | template 68 | class lexical_cast_t{ 69 | public: 70 | static std::string cast(const Source &arg){ 71 | std::ostringstream ss; 72 | ss< 78 | class lexical_cast_t{ 79 | public: 80 | static Target cast(const std::string &arg){ 81 | Target ret; 82 | std::istringstream ss(arg); 83 | if (!(ss>>ret && ss.eof())) 84 | throw std::bad_cast(); 85 | return ret; 86 | } 87 | }; 88 | 89 | template 90 | struct is_same { 91 | static const bool value = false; 92 | }; 93 | 94 | template 95 | struct is_same{ 96 | static const bool value = true; 97 | }; 98 | 99 | template 100 | Target lexical_cast(const Source &arg) 101 | { 102 | return lexical_cast_t::value>::cast(arg); 103 | } 104 | 105 | static inline std::string demangle(const std::string &name) 106 | { 107 | int status=0; 108 | char *p=abi::__cxa_demangle(name.c_str(), 0, 0, &status); 109 | std::string ret(p); 110 | free(p); 111 | return ret; 112 | } 113 | 114 | template 115 | std::string readable_typename() 116 | { 117 | return demangle(typeid(T).name()); 118 | } 119 | 120 | template 121 | std::string default_value(T def) 122 | { 123 | return detail::lexical_cast(def); 124 | } 125 | 126 | template <> 127 | inline std::string readable_typename() 128 | { 129 | return "string"; 130 | } 131 | 132 | } // detail 133 | 134 | //----- 135 | 136 | class cmdline_error : public std::exception { 137 | public: 138 | cmdline_error(const std::string &msg): msg(msg){} 139 | ~cmdline_error() throw() {} 140 | const char *what() const throw() { return msg.c_str(); } 141 | private: 142 | std::string msg; 143 | }; 144 | 145 | template 146 | struct default_reader{ 147 | T operator()(const std::string &str){ 148 | return detail::lexical_cast(str); 149 | } 150 | }; 151 | 152 | template 153 | struct range_reader{ 154 | range_reader(const T &low, const T &high): low(low), high(high) {} 155 | T operator()(const std::string &s) const { 156 | T ret=default_reader()(s); 157 | if (!(ret>=low && ret<=high)) throw cmdline::cmdline_error("range_error"); 158 | return ret; 159 | } 160 | private: 161 | T low, high; 162 | }; 163 | 164 | template 165 | range_reader range(const T &low, const T &high) 166 | { 167 | return range_reader(low, high); 168 | } 169 | 170 | template 171 | struct oneof_reader{ 172 | T operator()(const std::string &s){ 173 | T ret=default_reader()(s); 174 | if (std::find(alt.begin(), alt.end(), ret)==alt.end()) 175 | throw cmdline_error(""); 176 | return ret; 177 | } 178 | void add(const T &v){ alt.push_back(v); } 179 | private: 180 | std::vector alt; 181 | }; 182 | 183 | template 184 | oneof_reader oneof(T a1) 185 | { 186 | oneof_reader ret; 187 | ret.add(a1); 188 | return ret; 189 | } 190 | 191 | template 192 | oneof_reader oneof(T a1, T a2) 193 | { 194 | oneof_reader ret; 195 | ret.add(a1); 196 | ret.add(a2); 197 | return ret; 198 | } 199 | 200 | template 201 | oneof_reader oneof(T a1, T a2, T a3) 202 | { 203 | oneof_reader ret; 204 | ret.add(a1); 205 | ret.add(a2); 206 | ret.add(a3); 207 | return ret; 208 | } 209 | 210 | template 211 | oneof_reader oneof(T a1, T a2, T a3, T a4) 212 | { 213 | oneof_reader ret; 214 | ret.add(a1); 215 | ret.add(a2); 216 | ret.add(a3); 217 | ret.add(a4); 218 | return ret; 219 | } 220 | 221 | template 222 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5) 223 | { 224 | oneof_reader ret; 225 | ret.add(a1); 226 | ret.add(a2); 227 | ret.add(a3); 228 | ret.add(a4); 229 | ret.add(a5); 230 | return ret; 231 | } 232 | 233 | template 234 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6) 235 | { 236 | oneof_reader ret; 237 | ret.add(a1); 238 | ret.add(a2); 239 | ret.add(a3); 240 | ret.add(a4); 241 | ret.add(a5); 242 | ret.add(a6); 243 | return ret; 244 | } 245 | 246 | template 247 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) 248 | { 249 | oneof_reader ret; 250 | ret.add(a1); 251 | ret.add(a2); 252 | ret.add(a3); 253 | ret.add(a4); 254 | ret.add(a5); 255 | ret.add(a6); 256 | ret.add(a7); 257 | return ret; 258 | } 259 | 260 | template 261 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) 262 | { 263 | oneof_reader ret; 264 | ret.add(a1); 265 | ret.add(a2); 266 | ret.add(a3); 267 | ret.add(a4); 268 | ret.add(a5); 269 | ret.add(a6); 270 | ret.add(a7); 271 | ret.add(a8); 272 | return ret; 273 | } 274 | 275 | template 276 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) 277 | { 278 | oneof_reader ret; 279 | ret.add(a1); 280 | ret.add(a2); 281 | ret.add(a3); 282 | ret.add(a4); 283 | ret.add(a5); 284 | ret.add(a6); 285 | ret.add(a7); 286 | ret.add(a8); 287 | ret.add(a9); 288 | return ret; 289 | } 290 | 291 | template 292 | oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10) 293 | { 294 | oneof_reader ret; 295 | ret.add(a1); 296 | ret.add(a2); 297 | ret.add(a3); 298 | ret.add(a4); 299 | ret.add(a5); 300 | ret.add(a6); 301 | ret.add(a7); 302 | ret.add(a8); 303 | ret.add(a9); 304 | ret.add(a10); 305 | return ret; 306 | } 307 | 308 | //----- 309 | 310 | class parser{ 311 | public: 312 | parser(){ 313 | } 314 | ~parser(){ 315 | for (std::map::iterator p=options.begin(); 316 | p!=options.end(); p++) 317 | delete p->second; 318 | } 319 | 320 | void add(const std::string &name, 321 | char short_name=0, 322 | const std::string &desc=""){ 323 | if (options.count(name)) throw cmdline_error("multiple definition: "+name); 324 | options[name]=new option_without_value(name, short_name, desc); 325 | ordered.push_back(options[name]); 326 | } 327 | 328 | template 329 | void add(const std::string &name, 330 | char short_name=0, 331 | const std::string &desc="", 332 | bool need=true, 333 | const T def=T()){ 334 | add(name, short_name, desc, need, def, default_reader()); 335 | } 336 | 337 | template 338 | void add(const std::string &name, 339 | char short_name=0, 340 | const std::string &desc="", 341 | bool need=true, 342 | const T def=T(), 343 | F reader=F()){ 344 | if (options.count(name)) throw cmdline_error("multiple definition: "+name); 345 | options[name]=new option_with_value_with_reader(name, short_name, need, def, desc, reader); 346 | ordered.push_back(options[name]); 347 | } 348 | 349 | void footer(const std::string &f){ 350 | ftr=f; 351 | } 352 | 353 | void set_program_name(const std::string &name){ 354 | prog_name=name; 355 | } 356 | 357 | bool exist(const std::string &name) const { 358 | if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); 359 | return options.find(name)->second->has_set(); 360 | } 361 | 362 | template 363 | const T &get(const std::string &name) const { 364 | if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); 365 | const option_with_value *p=dynamic_cast*>(options.find(name)->second); 366 | if (p==NULL) throw cmdline_error("type mismatch flag '"+name+"'"); 367 | return p->get(); 368 | } 369 | 370 | const std::vector &rest() const { 371 | return others; 372 | } 373 | 374 | bool parse(const std::string &arg){ 375 | std::vector args; 376 | 377 | std::string buf; 378 | bool in_quote=false; 379 | for (std::string::size_type i=0; i=arg.length()){ 394 | errors.push_back("unexpected occurrence of '\\' at end of string"); 395 | return false; 396 | } 397 | } 398 | 399 | buf+=arg[i]; 400 | } 401 | 402 | if (in_quote){ 403 | errors.push_back("quote is not closed"); 404 | return false; 405 | } 406 | 407 | if (buf.length()>0) 408 | args.push_back(buf); 409 | 410 | for (size_t i=0; i &args){ 417 | int argc=static_cast(args.size()); 418 | std::vector argv(argc); 419 | 420 | for (int i=0; i lookup; 438 | for (std::map::iterator p=options.begin(); 439 | p!=options.end(); p++){ 440 | if (p->first.length()==0) continue; 441 | char initial=p->second->short_name(); 442 | if (initial){ 443 | if (lookup.count(initial)>0){ 444 | lookup[initial]=""; 445 | errors.push_back(std::string("short option '")+initial+"' is ambiguous"); 446 | return false; 447 | } 448 | else lookup[initial]=p->first; 449 | } 450 | } 451 | 452 | for (int i=1; i &args){ 534 | if (!options.count("help")) 535 | add("help", '?', "print this message"); 536 | check(args.size(), parse(args)); 537 | } 538 | 539 | void parse_check(int argc, char *argv[]){ 540 | if (!options.count("help")) 541 | add("help", '?', "print this message"); 542 | check(argc, parse(argc, argv)); 543 | } 544 | 545 | std::string error() const{ 546 | return errors.size()>0?errors[0]:""; 547 | } 548 | 549 | std::string error_full() const{ 550 | std::ostringstream oss; 551 | for (size_t i=0; imust()) 561 | oss<short_description()<<" "; 562 | } 563 | 564 | oss<<"[options] ... "<name().length()); 570 | } 571 | for (size_t i=0; ishort_name()){ 573 | oss<<" -"<short_name()<<", "; 574 | } 575 | else{ 576 | oss<<" "; 577 | } 578 | 579 | oss<<"--"<name(); 580 | for (size_t j=ordered[i]->name().length(); jdescription()<set()){ 607 | errors.push_back("option needs value: --"+name); 608 | return; 609 | } 610 | } 611 | 612 | void set_option(const std::string &name, const std::string &value){ 613 | if (options.count(name)==0){ 614 | errors.push_back("undefined option: --"+name); 615 | return; 616 | } 617 | if (!options[name]->set(value)){ 618 | errors.push_back("option value is invalid: --"+name+"="+value); 619 | return; 620 | } 621 | } 622 | 623 | class option_base{ 624 | public: 625 | virtual ~option_base(){} 626 | 627 | virtual bool has_value() const=0; 628 | virtual bool set()=0; 629 | virtual bool set(const std::string &value)=0; 630 | virtual bool has_set() const=0; 631 | virtual bool valid() const=0; 632 | virtual bool must() const=0; 633 | 634 | virtual const std::string &name() const=0; 635 | virtual char short_name() const=0; 636 | virtual const std::string &description() const=0; 637 | virtual std::string short_description() const=0; 638 | }; 639 | 640 | class option_without_value : public option_base { 641 | public: 642 | option_without_value(const std::string &name, 643 | char short_name, 644 | const std::string &desc) 645 | :nam(name), snam(short_name), desc(desc), has(false){ 646 | } 647 | ~option_without_value(){} 648 | 649 | bool has_value() const { return false; } 650 | 651 | bool set(){ 652 | has=true; 653 | return true; 654 | } 655 | 656 | bool set(const std::string &){ 657 | return false; 658 | } 659 | 660 | bool has_set() const { 661 | return has; 662 | } 663 | 664 | bool valid() const{ 665 | return true; 666 | } 667 | 668 | bool must() const{ 669 | return false; 670 | } 671 | 672 | const std::string &name() const{ 673 | return nam; 674 | } 675 | 676 | char short_name() const{ 677 | return snam; 678 | } 679 | 680 | const std::string &description() const { 681 | return desc; 682 | } 683 | 684 | std::string short_description() const{ 685 | return "--"+nam; 686 | } 687 | 688 | private: 689 | std::string nam; 690 | char snam; 691 | std::string desc; 692 | bool has; 693 | }; 694 | 695 | template 696 | class option_with_value : public option_base { 697 | public: 698 | option_with_value(const std::string &name, 699 | char short_name, 700 | bool need, 701 | const T &def, 702 | const std::string &desc) 703 | : nam(name), snam(short_name), need(need), has(false) 704 | , def(def), actual(def) { 705 | this->desc=full_description(desc); 706 | } 707 | ~option_with_value(){} 708 | 709 | const T &get() const { 710 | return actual; 711 | } 712 | 713 | bool has_value() const { return true; } 714 | 715 | bool set(){ 716 | return false; 717 | } 718 | 719 | bool set(const std::string &value){ 720 | try{ 721 | actual=read(value); 722 | has=true; 723 | } 724 | catch(const std::exception &e){ 725 | return false; 726 | } 727 | return true; 728 | } 729 | 730 | bool has_set() const{ 731 | return has; 732 | } 733 | 734 | bool valid() const{ 735 | if (need && !has) return false; 736 | return true; 737 | } 738 | 739 | bool must() const{ 740 | return need; 741 | } 742 | 743 | const std::string &name() const{ 744 | return nam; 745 | } 746 | 747 | char short_name() const{ 748 | return snam; 749 | } 750 | 751 | const std::string &description() const { 752 | return desc; 753 | } 754 | 755 | std::string short_description() const{ 756 | return "--"+nam+"="+detail::readable_typename(); 757 | } 758 | 759 | protected: 760 | std::string full_description(const std::string &desc){ 761 | return 762 | desc+" ("+detail::readable_typename()+ 763 | (need?"":" [="+detail::default_value(def)+"]") 764 | +")"; 765 | } 766 | 767 | virtual T read(const std::string &s)=0; 768 | 769 | std::string nam; 770 | char snam; 771 | bool need; 772 | std::string desc; 773 | 774 | bool has; 775 | T def; 776 | T actual; 777 | }; 778 | 779 | template 780 | class option_with_value_with_reader : public option_with_value { 781 | public: 782 | option_with_value_with_reader(const std::string &name, 783 | char short_name, 784 | bool need, 785 | const T def, 786 | const std::string &desc, 787 | F reader) 788 | : option_with_value(name, short_name, need, def, desc), reader(reader){ 789 | } 790 | 791 | private: 792 | T read(const std::string &s){ 793 | return reader(s); 794 | } 795 | 796 | F reader; 797 | }; 798 | 799 | std::map options; 800 | std::vector ordered; 801 | std::string ftr; 802 | 803 | std::string prog_name; 804 | std::vector others; 805 | 806 | std::vector errors; 807 | }; 808 | 809 | } // cmdline 810 | --------------------------------------------------------------------------------