diff --git a/src/core_ffi/CMakeLists.txt b/src/core_ffi/CMakeLists.txt index 95e0a87..424715e 100644 --- a/src/core_ffi/CMakeLists.txt +++ b/src/core_ffi/CMakeLists.txt @@ -15,6 +15,7 @@ if (KLSK_PYTHON_FFI) py_ffi/binder.cc py_ffi/codec/short_code.cc py_ffi/codec/common_codec.cc + py_ffi/py_cases.cc ) target_include_directories(klotski_py PRIVATE py_ffi/include) target_link_libraries(klotski_py PRIVATE klotski::core) diff --git a/src/core_ffi/py_ffi/binder.cc b/src/core_ffi/py_ffi/binder.cc index 79492da..e04bd9f 100644 --- a/src/core_ffi/py_ffi/binder.cc +++ b/src/core_ffi/py_ffi/binder.cc @@ -3,9 +3,13 @@ #include "py_exps.h" #include "py_codec.h" +#include "py_cases.h" namespace py = pybind11; +using klotski::ffi::PyCases; +using klotski::ffi::PyCasesIter; + using klotski::ffi::PyCodecExp; using klotski::ffi::PyShortCode; using klotski::ffi::PyCommonCode; @@ -64,9 +68,35 @@ void bind_short_code(const py::module_ &m) { .def_static("speed_up", &PyShortCode::speed_up, py::arg("fast_mode") = false); } +#include "group/group.h" +#include "all_cases/all_cases.h" + +static PyCases group_demo() { + auto group_union = klotski::cases::GroupUnion::unsafe_create(169); + auto cases = PyCases::from(group_union.cases()); + return cases; +} + +static PyCases all_cases() { + return PyCases::from_ref(klotski::cases::AllCases::instance().fetch()); +} + PYBIND11_MODULE(klotski, m) { py::register_exception(m, "CodecExp", PyExc_ValueError); + m.def("all_cases", &all_cases); + m.def("group_demo", &group_demo); + + py::class_(m, "PyCases") + .def("size", &PyCases::size) + .def("__iter__", [](const PyCases &self) { + return PyCasesIter(self); + }, py::keep_alive<0, 1>()); + + py::class_(m, "PyCasesIter") + .def("__iter__", [](PyCasesIter &it) -> PyCasesIter& { return it; }) + .def("__next__", &PyCasesIter::next); + bind_short_code(m); bind_common_code(m); diff --git a/src/core_ffi/py_ffi/include/py_cases.h b/src/core_ffi/py_ffi/include/py_cases.h new file mode 100644 index 0000000..fb53bd3 --- /dev/null +++ b/src/core_ffi/py_ffi/include/py_cases.h @@ -0,0 +1,129 @@ +/// Klotski Engine Python FFI by Dnomd343 @2024 + +#pragma once + +#include + +#include + +#include "ranges/ranges.h" + +#include "py_codec.h" + +namespace klotski::ffi { + +using klotski::cases::RangesUnion; + +//class PyCases { +//public: +// PyCases() = delete; +// +// PyCases(PyCases &&cases) noexcept : real_data_(std::move(cases.real_data_)), data_(real_data_) { +// std::cout << "PyCases(PyCases &&) called" << std::endl; +// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; +// } +// +// PyCases(const PyCases &cases) : real_data_(cases.real_data_), data_(real_data_) { +// std::cout << "PyCases(const PyCases &) called" << std::endl; +// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; +// } +// +// PyCases& operator==(PyCases &&cases) { +// std::cout << "PyCases& operator==(PyCases &&) called" << std::endl; +// if (this != &cases) { +// real_data_ = std::move(cases.real_data_); +// } +// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; +// return *this; +// } +// +// PyCases& operator==(const PyCases &&cases) { +// std::cout << "PyCases& operator==(const PyCases &&) called" << std::endl; +// if (this != &cases) { +// real_data_ = cases.real_data_; +// } +// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; +// return *this; +// } +// +// [[nodiscard]] size_t size() const; +// +// static PyCases from(RangesUnion &&data); +// +// static PyCases from_ref(const RangesUnion &data); +// +// [[nodiscard]] const RangesUnion& data_ref() const; +// +//private: +// explicit PyCases(RangesUnion &&data) : real_data_(std::move(data)), data_(real_data_) { +// std::cout << "PyCases init from r-value" << std::endl; +// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; +// } +// +// explicit PyCases(const RangesUnion &data) : data_(data) { +// std::cout << "PyCases init from l-value" << std::endl; +// } +// +// RangesUnion real_data_; +// const RangesUnion &data_; +//}; + +class PyCases { +public: + using Cases = RangesUnion; + using CasesRef = std::reference_wrapper; + + static PyCases from(RangesUnion &&data) { + return PyCases(std::move(data)); + } + + static PyCases from_ref(const RangesUnion &data) { + return PyCases(data); + } + + [[nodiscard]] size_t size() const; + + [[nodiscard]] const Cases& ref() const { + + // if (std::holds_alternative(data_)) { + // return std::get(data_); + // } else { + // return std::get(data_); + // } + + return std::visit([](T &&arg) -> const Cases& { + if constexpr (std::is_same_v, Cases>) { + return arg; + } else { + return arg.get(); + } + }, data_); + } + +private: + std::variant data_; + + explicit PyCases(const RangesUnion &data) : data_(std::cref(data)) { + std::cout << "PyCases(const RangesUnion &) called" << std::endl; + std::cout << "variant data index: " << data_.index() << std::endl; + } + + explicit PyCases(RangesUnion &&data) : data_(std::move(data)) { + std::cout << "PyCases(RangesUnion &&) called" << std::endl; + std::cout << "variant data index: " << data_.index() << std::endl; + } +}; + +class PyCasesIter { +public: + explicit PyCasesIter(const PyCases &data) : data_(data) {} + + PyCommonCode next(); + +private: + uint64_t head_ {0}; + size_t index_ {0}; + const PyCases &data_; +}; + +} // namespace klotski::ffi diff --git a/src/core_ffi/py_ffi/py_cases.cc b/src/core_ffi/py_ffi/py_cases.cc new file mode 100644 index 0000000..6b56ff9 --- /dev/null +++ b/src/core_ffi/py_ffi/py_cases.cc @@ -0,0 +1,44 @@ +#include + +#include "include/py_cases.h" + +namespace py = pybind11; + +using klotski::ffi::PyCases; +using klotski::ffi::PyCasesIter; +using klotski::ffi::PyCommonCode; + +using klotski::cases::RangesUnion; + +size_t PyCases::size() const { + size_t num = 0; + for (const auto &x : ref()) { + num += x.size(); + } + return num; +} + +//PyCases PyCases::from(RangesUnion &&data) { +// return PyCases(std::move(data)); +//} +// +//PyCases PyCases::from_ref(const RangesUnion &data) { +// return PyCases(data); +//} +// +//const RangesUnion& PyCases::data_ref() const { +// return data_; +//} + +PyCommonCode PyCasesIter::next() { + while (head_ < 16) { + const auto &ranges = data_.ref()[head_]; + if (index_ < ranges.size()) { + auto code = (head_ << 32) | ranges[index_++]; + return std::bit_cast(code); + } + ++head_; + index_ = 0; + } + throw py::stop_iteration(); +}