#pragma once #if defined _CPPUNWIND || defined __cpp_exceptions #define QC_HASH_EXCEPTIONS_ENABLED #endif #include #include #include #include #include #include #include #ifdef QC_HASH_EXCEPTIONS_ENABLED #include #endif #include #include #include #include namespace qc::hash { using u64 = uint64_t; using s64 = int64_t; using f64 = double; using u32 = uint32_t; using s32 = int32_t; using f32 = float; using u16 = uint16_t; using s16 = int16_t; using u8 = uint8_t; using s8 = int8_t; static_assert(std::is_same_v && std::is_same_v, "Unsupported architecture"); inline namespace config { inline constexpr u64 minMapCapacity{16u}; } template concept SignedInteger = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; template concept UnsignedInteger = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; template concept Enum = std::is_enum_v; template concept Pointer = std::is_pointer_v; namespace _private { template struct UnsignedHelper; template<> struct UnsignedHelper<1u> { using type = u8; }; template<> struct UnsignedHelper<2u> { using type = u16; }; template<> struct UnsignedHelper<4u> { using type = u32; }; template<> struct UnsignedHelper<8u> { using type = u64; }; } template using Unsigned = typename _private::UnsignedHelper::type; template struct UnsignedMulti { using Element = Unsigned; Element elements[elementN]; constexpr bool operator==(const UnsignedMulti &) const = default; constexpr UnsignedMulti operator~() const; }; template struct IsUniquelyRepresentable : std::bool_constant> {}; template struct IsUniquelyRepresentable> : std::true_type {}; template struct IsUniquelyRepresentable> : std::true_type {}; template struct IsUniquelyRepresentable> : std::bool_constant::value && IsUniquelyRepresentable::value> {}; template struct IsUniquelyRepresentable> : std::false_type {}; template concept Rawable = IsUniquelyRepresentable::value; namespace _private { template struct RawTypeHelper { using type = Unsigned; }; template requires (alignof(T) != sizeof(T) || sizeof(T) > sizeof(uintmax_t)) struct RawTypeHelper { static constexpr u64 align{ alignof(T) > alignof(uintmax_t) ? alignof(uintmax_t) : alignof(T)}; using type = UnsignedMulti; }; } template using RawType = typename _private::RawTypeHelper::type; template struct IdentityHash; template struct IdentityHash; template struct IdentityHash>; template struct IdentityHash>; template struct FastHash; template struct FastHash; template struct FastHash>; template struct FastHash>; template<> struct FastHash; template<> struct FastHash; namespace fastHash { template [[nodiscard]] constexpr H mix(H v); template [[nodiscard]] constexpr H hash(const T &v); template [[nodiscard]] H hash(const void *data, u64 length); } template struct IsCompatible : std::bool_constant, std::decay_t>> { }; template struct IsCompatible : std::bool_constant {}; template struct IsCompatible : std::bool_constant {}; template struct IsCompatible : std::bool_constant {}; template struct IsCompatible : std::false_type {}; template requires (std::is_same_v, std::decay_t> || std::is_base_of_v) struct IsCompatible : std::true_type {}; template requires (std::is_same_v, std::decay_t> || std::is_base_of_v) struct IsCompatible, TOther *> : std::true_type {}; template requires (std::is_same_v, std::decay_t> || std::is_base_of_v) struct IsCompatible, TOther *> : std::true_type {}; template concept Compatible = Rawable && Rawable && IsCompatible::value; struct RawFriend; template, typename A = std::allocator>> class RawMap; template, typename A = std::allocator> using RawSet = RawMap; template class RawMap { inline static constexpr bool _isSet{std::is_same_v}; inline static constexpr bool _isMap{! _isSet}; using E = std::conditional_t<_isSet, K, std::pair>; template class _Iterator; friend ::qc::hash::RawFriend; public: static_assert(std::is_move_constructible_v); static_assert(std::is_move_assignable_v); static_assert(std::is_swappable_v); static_assert(requires(const H h, const K k) { u64{h(k)}; }); static_assert(std::is_move_constructible_v); static_assert(std::is_move_assignable_v); static_assert(std::is_swappable_v); static_assert(std::is_move_constructible_v); static_assert(std::is_move_assignable_v || ! std::allocator_traits< A>::propagate_on_container_move_assignment::value); static_assert( std::is_swappable_v || ! std::allocator_traits::propagate_on_container_swap::value); using key_type = K; using mapped_type = V; using value_type = E; using hasher = H; using allocator_type = A; using reference = E &; using const_reference = const E &; using pointer = E *; using const_pointer = const E *; using size_type = u64; using difference_type = s64; using iterator = _Iterator; using const_iterator = _Iterator; explicit RawMap(u64 capacity = minMapCapacity, const H &hash = {}, const A &alloc = {}); RawMap(u64 capacity, const A &alloc); explicit RawMap(const A &alloc); template RawMap(It first, It last, u64 capacity = {}, const H &hash = {}, const A &alloc = {}); template RawMap(It first, It last, u64 capacity, const A &alloc); RawMap(std::initializer_list elements, u64 capacity = {}, const H &hash = {}, const A &alloc = {}); RawMap(std::initializer_list elements, u64 capacity, const A &alloc); RawMap(const RawMap &other); RawMap(RawMap &&other); RawMap &operator=(std::initializer_list elements); RawMap &operator=(const RawMap &other); RawMap &operator=(RawMap &&other); ~RawMap(); std::pair insert(const E &element); std::pair insert(E &&element); template void insert(It first, It last); void insert(std::initializer_list elements); std::pair emplace(const E &element); std::pair emplace(E &&element); template std::pair emplace(K_ &&key, V_ &&value) requires (! std::is_same_v); template std::pair emplace(KArgs &&...keyArgs) requires (std::is_same_v); template std::pair emplace(std::piecewise_construct_t, std::tuple &&keyArgs, std::tuple &&valueArgs) requires (! std::is_same_v); template std::pair try_emplace(K_ &&key, VArgs &&...valueArgs); template K_> bool erase(const K_ &key); void erase(iterator position); void clear(); template K_> [[nodiscard]] bool contains(const K_ &key) const; template K_> [[nodiscard]] u64 count(const K_ &key) const; #ifdef QC_HASH_EXCEPTIONS_ENABLED template K_> [[nodiscard]] std::add_lvalue_reference_t at(const K_ &key) requires (! std::is_same_v); template K_> [[nodiscard]] std::add_lvalue_reference_t at(const K_ &key) const requires (! std::is_same_v); #endif template K_> [[nodiscard]] std::add_lvalue_reference_t operator[](const K_ &key) requires (! std::is_same_v); template K_> [[nodiscard]] std::add_lvalue_reference_t operator[](K_ &&key) requires (! std::is_same_v); [[nodiscard]] iterator begin(); [[nodiscard]] const_iterator begin() const; [[nodiscard]] const_iterator cbegin() const; [[nodiscard]] iterator end(); [[nodiscard]] const_iterator end() const; [[nodiscard]] const_iterator cend() const; template K_> [[nodiscard]] iterator find(const K_ &key); template K_> [[nodiscard]] const_iterator find(const K_ &key) const; template K_> [[nodiscard]] u64 slot(const K_ &key) const; void reserve(u64 capacity); void rehash(u64 slotN); void swap(RawMap &other); [[nodiscard]] u64 size() const; [[nodiscard]] bool empty() const; [[nodiscard]] u64 capacity() const; [[nodiscard]] u64 slot_n() const; [[nodiscard]] u64 max_size() const; [[nodiscard]] u64 max_slot_n() const; [[nodiscard]] f32 load_factor() const; [[nodiscard]] f32 max_load_factor() const; [[nodiscard]] const H &hash_function() const; [[nodiscard]] const A &get_allocator() const; private: using _RawKey = RawType; inline static constexpr _RawKey _vacantKey{_RawKey(~_RawKey{})}; inline static constexpr _RawKey _graveKey{_RawKey(~_RawKey{1u})}; inline static constexpr _RawKey _specialKeys[2]{_graveKey, _vacantKey}; inline static constexpr _RawKey _vacantGraveKey{_vacantKey}; inline static constexpr _RawKey _vacantVacantKey{_graveKey}; inline static constexpr _RawKey _vacantSpecialKeys[2]{_vacantGraveKey, _vacantVacantKey}; inline static constexpr _RawKey _terminalKey{0u}; static K &_key(E &element); static const K &_key(const E &element); static bool _isPresent(const _RawKey &key); static bool _isSpecial(const _RawKey &key); u64 _size; u64 _slotN; E *_elements; bool _haveSpecial[2]; H _hash; A _alloc; template std::pair _emplace(KTuple &&kTuple, VTuple &&vTuple, std::index_sequence, std::index_sequence); template void _clear(); template K_> u64 _slot(const K_ &key) const; void _rehash(u64 slotN); template void _allocate(); void _deallocate(); void _clearKeys(); template void _forwardData(std::conditional_t &other); struct _FindKeyResult1 { E *element; bool isPresent; }; struct _FindKeyResult2 { E *element; bool isPresent; bool isSpecial; unsigned char specialI; }; template using _FindKeyResult = std::conditional_t; template K_> _FindKeyResult _findKey(const K_ &key) const; }; template bool operator==(const RawMap &m1, const RawMap &m2); template template class RawMap::_Iterator { friend ::qc::hash::RawMap; friend ::qc::hash::RawFriend; using E = std::conditional_t; public: using iterator_category = std::forward_iterator_tag; using value_type = E; using difference_type = ptrdiff_t; using pointer = E *; using reference = E &; constexpr _Iterator() = default; constexpr _Iterator(const _Iterator &other) = default; template requires (constant && ! constant_) constexpr _Iterator(const _Iterator &other); _Iterator &operator=(const _Iterator &other) = default; template requires (constant && ! constant_) _Iterator &operator=(const _Iterator &other); [[nodiscard]] E &operator*() const; [[nodiscard]] E *operator->() const; _Iterator &operator++(); _Iterator operator++(int); template [[nodiscard]] bool operator==(const _Iterator &other) const; private: E *_element; constexpr _Iterator(E *element); }; } namespace std { template void swap(qc::hash::RawMap &a, qc::hash::RawMap &b); } namespace qc::hash { namespace _private { inline constexpr u64 minMapSlotN{minMapCapacity * 2u}; template inline constexpr U getLowBytes(const T &v) { if constexpr(alignof(T) >= sizeof(U)) { return reinterpret_cast(v); } else if constexpr(alignof(T) == sizeof(T)) { return reinterpret_cast &>(v); } else { U result{0u}; using Block = Unsigned; constexpr u64 n{(sizeof(T) < sizeof(U) ? sizeof(T) : sizeof(U)) / sizeof(Block)}; const Block *src{reinterpret_cast(&v)}; Block *dst{reinterpret_cast(&result)}; if constexpr(std::endian::native == std::endian::big) { constexpr u64 srcBlocks{sizeof(T) / sizeof(Block)}; constexpr u64 dstBlocks{sizeof(U) / sizeof(Block)}; if constexpr(srcBlocks > n) { src += srcBlocks - n; } if constexpr(dstBlocks > n) { dst += dstBlocks - n; } } if constexpr(n >= 1u) dst[0] = src[0]; if constexpr(n >= 2u) dst[1] = src[1]; if constexpr(n >= 3u) dst[2] = src[2]; if constexpr(n >= 4u) dst[3] = src[3]; if constexpr(n >= 5u) dst[4] = src[4]; if constexpr(n >= 6u) dst[5] = src[5]; if constexpr(n >= 7u) dst[6] = src[6]; if constexpr(n >= 8u) dst[7] = src[7]; return result; } } } template inline constexpr auto UnsignedMulti::operator~() const -> UnsignedMulti { UnsignedMulti res; for(u64 i{0u}; i < elementN; ++i) { res.elements[i] = Element(~elements[i]); } return res; } template struct IdentityHash { [[nodiscard]] constexpr u64 operator()(const T &v) const { return _private::getLowBytes(v); } }; template struct IdentityHash { [[nodiscard]] constexpr u64 operator()(const T *const v) const { constexpr s32 shift{s32(std::bit_width(alignof(T)) - 1u)}; return std::bit_cast(v) >> shift; } }; template struct IdentityHash> : IdentityHash { using IdentityHash::operator(); [[nodiscard]] constexpr u64 operator()(const std::unique_ptr &v) const { return (*this)(v.get()); } }; template struct IdentityHash> : IdentityHash { using IdentityHash::operator(); [[nodiscard]] constexpr u64 operator()(const std::shared_ptr &v) const { return (*this)(v.get()); } }; template struct FastHash { [[nodiscard]] constexpr u64 operator()(const T &v) const { return fastHash::hash(v); } }; template struct FastHash { [[nodiscard]] constexpr u64 operator()(const T *const v) const { return fastHash::hash(v); } }; template struct FastHash> : FastHash { using FastHash::operator(); [[nodiscard]] constexpr u64 operator()(const std::unique_ptr &v) const { return (*this)(v.get()); } }; template struct FastHash> : FastHash { using FastHash::operator(); [[nodiscard]] constexpr u64 operator()(const std::shared_ptr &v) const { return (*this)(v.get()); } }; template<> struct FastHash { [[nodiscard]] u64 operator()(const std::string &v) const { return fastHash::hash(v.c_str(), v.length()); } [[nodiscard]] u64 operator()(const std::string_view &v) const { return fastHash::hash(v.data(), v.length()); } [[nodiscard]] u64 operator()(const char *v) const { return fastHash::hash(v, std::strlen(v)); } }; template<> struct FastHash : FastHash {}; namespace fastHash { template struct Constants; template<> struct Constants { inline static constexpr u64 m{0xC6A4A7935BD1E995u}; inline static constexpr s32 r{47}; }; template<> struct Constants { inline static constexpr u32 m{0x5BD1E995u}; inline static constexpr s32 r{24}; }; template inline constexpr H m{Constants::m}; template inline constexpr s32 r{Constants::r}; template inline constexpr H mix(H v) { v *= m; v ^= v >> r; return v * m; } template inline constexpr H hash(const T &v) { if constexpr(sizeof(T) <= sizeof(H)) { return (H(sizeof(T)) * m)^mix(_private::getLowBytes(v)); } else { return hash(&v, sizeof(T)); } } template inline H hash(const void *const data, u64 length) { const std::byte *bytes{static_cast(data)}; H h{H(length)}; while(length >= sizeof(H)) { H w; std::memcpy(&w, bytes, sizeof(H)); h *= m; h ^= mix(w); bytes += sizeof(H); length -= sizeof(H); }; if(length) { H w{0u}; std::memcpy(&w, bytes, length); h *= m; h ^= mix(w); } return h; } } template inline RawType &_raw(K &key) { return reinterpret_cast &>(key); } template inline const RawType &_raw(const K &key) { return reinterpret_cast &>(key); } template inline RawMap::RawMap(const u64 capacity, const H &hash, const A &alloc) : _size{}, _slotN{capacity <= minMapCapacity ? _private::minMapSlotN : std::bit_ceil(capacity << 1)}, _elements{}, _haveSpecial{}, _hash{hash}, _alloc{alloc} {} template inline RawMap::RawMap(const u64 capacity, const A &alloc) : RawMap{capacity, H{}, alloc} {} template inline RawMap::RawMap(const A &alloc) : RawMap{minMapCapacity, H{}, alloc} {} template template inline RawMap::RawMap(const It first, const It last, const u64 capacity, const H &hash, const A &alloc) : RawMap{capacity, hash, alloc} { u64 n{}; for(It it{first}; it != last; ++it, ++n); reserve(n); insert(first, last); } template template inline RawMap::RawMap(const It first, const It last, const u64 capacity, const A &alloc) : RawMap{first, last, capacity, H{}, alloc} {} template inline RawMap::RawMap(const std::initializer_list elements, u64 capacity, const H &hash, const A &alloc) : RawMap{capacity ? capacity : elements.size(), hash, alloc} { insert(elements); } template inline RawMap::RawMap(const std::initializer_list elements, const u64 capacity, const A &alloc) : RawMap{elements, capacity, H{}, alloc} {} template inline RawMap::RawMap(const RawMap &other) : _size{other._size}, _slotN{other._slotN}, _elements{}, _haveSpecial{other._haveSpecial[0], other._haveSpecial[1]}, _hash{other._hash}, _alloc{std::allocator_traits::select_on_container_copy_construction( other._alloc)} { if(_size) { _allocate(); _forwardData(other); } } template inline RawMap::RawMap(RawMap &&other) : _size{std::exchange(other._size, 0u)}, _slotN{std::exchange(other._slotN, _private::minMapSlotN)}, _elements{std::exchange(other._elements, nullptr)}, _haveSpecial{std::exchange(other._haveSpecial[0], false), std::exchange(other._haveSpecial[1], false)}, _hash{std::move(other._hash)}, _alloc{std::move(other._alloc)} {} template inline RawMap &RawMap::operator=( const std::initializer_list elements) { return *this = RawMap(elements); } template inline RawMap &RawMap::operator=(const RawMap &other) { if(&other == this) { return *this; } if(_elements) { _clear(); if(! other._size || _slotN != other._slotN || _alloc != other._alloc) { _deallocate(); } } _size = other._size; _slotN = other._slotN; _haveSpecial[0] = other._haveSpecial[0]; _haveSpecial[1] = other._haveSpecial[1]; _hash = other._hash; if constexpr(std::allocator_traits< A>::propagate_on_container_copy_assignment::value) { _alloc = std::allocator_traits::select_on_container_copy_construction( other._alloc); } if(_size) { if(! _elements) { _allocate(); } _forwardData(other); } return *this; } template inline RawMap &RawMap::operator=(RawMap &&other) { if(&other == this) { return *this; } if(_elements) { _clear(); _deallocate(); } _size = other._size; _slotN = other._slotN; _haveSpecial[0] = other._haveSpecial[0]; _haveSpecial[1] = other._haveSpecial[1]; _hash = std::move(other._hash); if constexpr(std::allocator_traits< A>::propagate_on_container_move_assignment::value) { _alloc = std::move(other._alloc); } if(_alloc == other._alloc || std::allocator_traits< A>::propagate_on_container_move_assignment::value) { _elements = std::exchange(other._elements, nullptr); other._size = {}; } else { if(_size) { _allocate(); _forwardData(other); other._clear(); other._size = 0u; } if(other._elements) { other._deallocate(); } } other._slotN = _private::minMapSlotN; other._haveSpecial[0] = false; other._haveSpecial[1] = false; return *this; } template inline RawMap::~RawMap() { if(_elements) { _clear(); _deallocate(); } } template inline auto RawMap::insert(const E &element) -> std::pair { static_assert(std::is_copy_constructible_v); return emplace(element); } template inline auto RawMap::insert(E &&element) -> std::pair { return emplace(std::move(element)); } template template inline void RawMap::insert(It first, const It last) { while(first != last) { emplace(*first); ++first; } } template inline void RawMap::insert( const std::initializer_list elements) { for(const E &element : elements) { emplace(element); } } template inline auto RawMap::emplace(const E &element) -> std::pair { static_assert(std::is_copy_constructible_v); if constexpr(_isSet) { return try_emplace(element); } else { return try_emplace(element.first, element.second); } } template inline auto RawMap::emplace(E &&element) -> std::pair { if constexpr(_isSet) { return try_emplace(std::move(element)); } else { return try_emplace(std::move(element.first), std::move(element.second)); } } template template inline auto RawMap::emplace(K_ &&key, V_ &&value) -> std::pair requires (! std::is_same_v) { return try_emplace(std::forward(key), std::forward(value)); } template template inline auto RawMap::emplace(KArgs &&...keyArgs) -> std::pair requires (std::is_same_v) { return try_emplace(K{std::forward(keyArgs)...}); } template template inline auto RawMap::emplace(const std::piecewise_construct_t, std::tuple &&keyArgs, std::tuple &&valueArgs) -> std::pair requires (! std::is_same_v) { return _emplace(std::move(keyArgs), std::move(valueArgs), std::index_sequence_for(), std::index_sequence_for()); } template template inline auto RawMap::_emplace(KTuple &&kTuple, VTuple &&vTuple, const std::index_sequence, const std::index_sequence) -> std::pair { return try_emplace(K{std::move(std::get(kTuple))...}, std::move(std::get(vTuple))...); } template template inline auto RawMap::try_emplace(K_ &&key, VArgs &&...vArgs) -> std::pair { static_assert( ! (_isMap && ! sizeof...(VArgs) && ! std::is_default_constructible_v), "The value type must be default constructible in order to pass no value " "arguments"); static_assert(! (_isSet && sizeof...(VArgs)), "Sets do not have values"); if(! _elements) { _allocate(); } _FindKeyResult findResult{_findKey(key)}; if(findResult.isPresent) { return {iterator{findResult.element}, false}; } if(findResult.isSpecial) [[unlikely]] { _haveSpecial[findResult.specialI] = true; } else { if((_size - _haveSpecial[0] - _haveSpecial[1]) >= (_slotN >> 1)) [[unlikely]] { _rehash(_slotN << 1); findResult = _findKey(key); } } if constexpr(_isSet) { std::allocator_traits::construct(_alloc, findResult.element, std::forward(key)); } else { std::allocator_traits::construct(_alloc, &findResult.element->first, std::forward(key)); std::allocator_traits::construct(_alloc, &findResult.element->second, std::forward(vArgs)...); } ++_size; return {iterator{findResult.element}, true}; } template template K_> inline bool RawMap::erase(const K_ &key) { if(! _size) { return false; } const auto [element, isPresent]{_findKey(key)}; if(isPresent) { erase(iterator{element}); return true; } else { return false; } } template inline void RawMap::erase(const iterator position) { E *const eraseElement{position._element}; _RawKey &rawKey{_raw(_key(*eraseElement))}; E *const specialElements{_elements + _slotN}; std::allocator_traits::destroy(_alloc, eraseElement); if(eraseElement < specialElements) { rawKey = _graveKey; } else [[unlikely]] { const auto specialI{eraseElement - specialElements}; _raw(_key(specialElements[specialI])) = _vacantSpecialKeys[specialI]; _haveSpecial[specialI] = false; } --_size; } template inline void RawMap::clear() { _clear(); } template template inline void RawMap::_clear() { if constexpr(std::is_trivially_destructible_v) { if constexpr(preserveInvariants) { if(_size) { _clearKeys(); _size = {}; _haveSpecial[0] = false; _haveSpecial[1] = false; } } } else { if(_size) { E *element{_elements}; u64 n{}; const u64 regularElementN{_size - _haveSpecial[0] - _haveSpecial[1]}; for(; n < regularElementN; ++element) { _RawKey &rawKey{_raw(_key(*element))}; if(_isPresent(rawKey)) { std::allocator_traits::destroy(_alloc, element); ++n; } if constexpr(preserveInvariants) { rawKey = _vacantKey; } } if constexpr(preserveInvariants) { const E *const endRegularElement{_elements + _slotN}; for(; element < endRegularElement; ++element) { _raw(_key(*element)) = _vacantKey; } } if(_haveSpecial[0]) [[unlikely]] { element = _elements + _slotN; std::allocator_traits::destroy(_alloc, element); if constexpr(preserveInvariants) { _raw(_key(*element)) = _vacantGraveKey; _haveSpecial[0] = false; } } if(_haveSpecial[1]) [[unlikely]] { element = _elements + _slotN + 1; std::allocator_traits::destroy(_alloc, element); if constexpr(preserveInvariants) { _raw(_key(*element)) = _vacantVacantKey; _haveSpecial[1] = false; } } if constexpr(preserveInvariants) { _size = {}; } } } } template template K_> inline bool RawMap::contains(const K_ &key) const { return _size ? _findKey(key).isPresent : false; } template template K_> inline u64 RawMap::count(const K_ &key) const { return contains(key); } #ifdef QC_HASH_EXCEPTIONS_ENABLED template template K_> inline std::add_lvalue_reference_t RawMap::at(const K_ &key) requires (! std::is_same_v) { return const_cast(static_cast(this)->at(key)); } template template K_> inline std::add_lvalue_reference_t RawMap::at( const K_ &key) const requires (! std::is_same_v) { if(! _size) { throw std::out_of_range{"Map is empty"}; } const auto [element, isPresent]{_findKey(key)}; if(! isPresent) { throw std::out_of_range{"Element not found"}; } return element->second; } #endif template template K_> inline std::add_lvalue_reference_t RawMap::operator[]( const K_ &key) requires (! std::is_same_v) { return try_emplace(key).first->second; } template template K_> inline std::add_lvalue_reference_t RawMap::operator[](K_ &&key) requires (! std::is_same_v) { return try_emplace(std::move(key)).first->second; } template inline auto RawMap::begin() -> iterator { return const_cast(static_cast(this)->begin()._element); } template inline auto RawMap::begin() const -> const_iterator { if(_size - _haveSpecial[0] - _haveSpecial[1]) [[likely]] { for(const E *element{_elements};; ++element) { if(_isPresent(_raw(_key(*element)))) { return const_iterator{element}; } } } if(_haveSpecial[0]) [[unlikely]] { return const_iterator{_elements + _slotN}; } if(_haveSpecial[1]) [[unlikely]] { return const_iterator{_elements + _slotN + 1}; } return end(); } template inline auto RawMap::cbegin() const -> const_iterator { return begin(); } template inline typename RawMap::iterator RawMap::end() { return iterator{}; } template inline auto RawMap::end() const -> const_iterator { return const_iterator{}; } template inline auto RawMap::cend() const -> const_iterator { return const_iterator{}; } template template K_> inline auto RawMap::find(const K_ &key) -> iterator { return const_cast(static_cast(this)->find(key)._element); } template template K_> inline auto RawMap::find(const K_ &key) const -> const_iterator { if(! _size) { return cend(); } const auto [element, isPresent]{_findKey(key)}; return isPresent ? const_iterator{element} : cend(); } template template K_> inline u64 RawMap::slot(const K_ &key) const { const _RawKey &rawKey{_raw(key)}; if(_isSpecial(rawKey)) [[unlikely]] { return _slotN + (rawKey == _vacantKey); } else { return _slot(key); } } template template K_> inline u64 RawMap::_slot(const K_ &key) const { return _hash(key) & (_slotN - 1u); } template inline void RawMap::reserve(const u64 capacity) { rehash(capacity << 1); } template inline void RawMap::rehash(u64 slotN) { const u64 currentMinSlotN{ _size <= minMapCapacity ? _private::minMapSlotN : ((_size - _haveSpecial[0] - _haveSpecial[1]) << 1)}; if(slotN < currentMinSlotN) { slotN = currentMinSlotN; } else { slotN = std::bit_ceil(slotN); } if(slotN != _slotN) { if(_elements) { _rehash(slotN); } else { _slotN = slotN; } } } template inline void RawMap::_rehash(const u64 slotN) { const u64 oldSize{_size}; const u64 oldSlotN{_slotN}; E *const oldElements{_elements}; const bool oldHaveSpecial[2]{_haveSpecial[0], _haveSpecial[1]}; _size = {}; _slotN = slotN; _allocate(); _haveSpecial[0] = false; _haveSpecial[1] = false; u64 n{}; const u64 regularElementN{oldSize - oldHaveSpecial[0] - oldHaveSpecial[1]}; for(E *element{oldElements}; n < regularElementN; ++element) { if(_isPresent(_raw(_key(*element)))) { emplace(std::move(*element)); std::allocator_traits::destroy(_alloc, element); ++n; } } if(oldHaveSpecial[0]) [[unlikely]] { E *const oldElement{oldElements + oldSlotN}; std::allocator_traits::construct(_alloc, _elements + _slotN, std::move(*oldElement)); std::allocator_traits::destroy(_alloc, oldElement); ++_size; _haveSpecial[0] = true; } if(oldHaveSpecial[1]) [[unlikely]] { E *const oldElement{oldElements + oldSlotN + 1}; std::allocator_traits::construct(_alloc, _elements + _slotN + 1, std::move(*oldElement)); std::allocator_traits::destroy(_alloc, oldElement); ++_size; _haveSpecial[1] = true; } std::allocator_traits::deallocate(_alloc, oldElements, oldSlotN + 4u); } template inline void RawMap::swap(RawMap &other) { std::swap(_size, other._size); std::swap(_slotN, other._slotN); std::swap(_elements, other._elements); std::swap(_haveSpecial, other._haveSpecial); std::swap(_hash, other._hash); if constexpr(std::allocator_traits::propagate_on_container_swap::value) { std::swap(_alloc, other._alloc); } } template inline u64 RawMap::size() const { return _size; } template inline bool RawMap::empty() const { return ! _size; } template inline u64 RawMap::capacity() const { return _slotN >> 1; } template inline u64 RawMap::slot_n() const { return _slotN; } template inline u64 RawMap::max_size() const { return (max_slot_n() >> 1) + 2u; } template inline u64 RawMap::max_slot_n() const { return u64{1u} << 63; } template inline f32 RawMap::load_factor() const { return f32(_size) / f32(_slotN); } template inline f32 RawMap::max_load_factor() const { return f32(minMapCapacity) / f32(_private::minMapSlotN); } template inline const H &RawMap::hash_function() const { return _hash; } template inline const A &RawMap::get_allocator() const { return _alloc; } template inline K &RawMap::_key(E &element) { if constexpr(_isSet) return element; else return element.first; } template inline const K &RawMap::_key(const E &element) { if constexpr(_isSet) return element; else return element.first; } template inline bool RawMap::_isPresent(const _RawKey &key) { return ! _isSpecial(key); } template inline bool RawMap::_isSpecial(const _RawKey &key) { return key == _vacantKey || key == _graveKey; } template template inline void RawMap::_allocate() { _elements = std::allocator_traits::allocate(_alloc, _slotN + 4u); if constexpr(zeroKeys) { _clearKeys(); } _raw(_key(_elements[_slotN + 2])) = _terminalKey; _raw(_key(_elements[_slotN + 3])) = _terminalKey; } template inline void RawMap::_deallocate() { std::allocator_traits::deallocate(_alloc, _elements, _slotN + 4u); _elements = nullptr; } template inline void RawMap::_clearKeys() { E *const specialElements{_elements + _slotN}; for(E *element{_elements}; element < specialElements; ++element) { _raw(_key(*element)) = _vacantKey; } _raw(_key(specialElements[0])) = _vacantGraveKey; _raw(_key(specialElements[1])) = _vacantVacantKey; } template template inline void RawMap::_forwardData( std::conditional_t &other) { if constexpr(std::is_trivially_copyable_v) { std::memcpy(_elements, other._elements, (_slotN + 2u) * sizeof(E)); } else { using ElementForwardType = std::conditional_t; std::conditional_t *srcElement{other._elements}; const E *const srcEndElement{other._elements + _slotN}; E *dstElement{_elements}; for(; srcElement < srcEndElement; ++srcElement, ++dstElement) { const _RawKey &rawSrcKey{_raw(_key(*srcElement))}; if(_isPresent(rawSrcKey)) { std::allocator_traits::construct( _alloc, dstElement, static_cast(*srcElement)); } else { _raw(_key(*dstElement)) = rawSrcKey; } } if(_haveSpecial[0]) { std::allocator_traits::construct( _alloc, _elements + _slotN, static_cast(other._elements[_slotN])); } else { _raw(_key(_elements[_slotN])) = _vacantGraveKey; } if(_haveSpecial[1]) { std::allocator_traits::construct( _alloc, _elements + _slotN + 1, static_cast(other._elements[_slotN + 1])); } else { _raw(_key(_elements[_slotN + 1])) = _vacantVacantKey; } } } template template K_> inline auto RawMap::_findKey(const K_ &key) const -> _FindKeyResult { const _RawKey &rawKey{_raw(key)}; if(_isSpecial(rawKey)) [[unlikely]] { const unsigned char specialI{rawKey == _vacantKey}; if constexpr(insertionForm) { return _FindKeyResult{ .element = _elements + _slotN + specialI, .isPresent = _haveSpecial[specialI], .isSpecial = true, .specialI = specialI}; } else { return _FindKeyResult{ .element = _elements + _slotN + specialI, .isPresent = _haveSpecial[specialI]}; } } const E *const lastElement{_elements + _slotN}; E *element{_elements + _slot(key)}; E *grave{}; while(true) { const _RawKey &rawSlotKey{_raw(_key(*element))}; if(rawSlotKey == rawKey) { if constexpr(insertionForm) { return {.element = element, .isPresent = true, .isSpecial = false, .specialI = 0u}; } else { return {.element = element, .isPresent = true}; } } if(rawSlotKey == _vacantKey) { if constexpr(insertionForm) { return {.element = grave ? grave : element, .isPresent = false, .isSpecial = false, .specialI = 0u}; } else { return {.element = element, .isPresent = false}; } } if constexpr(insertionForm) { if(rawSlotKey == _graveKey) { grave = element; } } ++element; if(element == lastElement) [[unlikely]] { element = _elements; } } } template inline bool operator==(const RawMap &m1, const RawMap &m2) { if(m1.size() != m2.size()) { return false; } if(&m1 == &m2) { return true; } const auto endIt{m2.cend()}; for(const auto &element : m1) { if constexpr(std::is_same_v) { if(! m2.contains(element)) { return false; } } else { const auto it{m2.find(element.first)}; if(it == endIt || it->second != element.second) { return false; } } } return true; } template template template requires (constant && ! constant_) inline constexpr RawMap::_Iterator::_Iterator( const _Iterator &other) : _element{other._element} {} template template inline constexpr RawMap::_Iterator::_Iterator( E *const element) : _element{element} {} template template template requires (constant && ! constant_) inline auto RawMap::_Iterator::operator=( const _Iterator &other) -> _Iterator & { _element = other._element; return *this; } template template inline auto RawMap::_Iterator::operator*() const -> E & { return *_element; } template template inline auto RawMap::_Iterator::operator->() const -> E * { return _element; } template template inline auto RawMap::_Iterator::operator++() -> _Iterator & { while(true) { ++_element; const _RawKey &rawKey{_raw(_key(*_element))}; if(_isPresent(rawKey)) { if(rawKey == _terminalKey) [[unlikely]] { if(_raw(_key(_element[1])) == _terminalKey) { _element = nullptr; } } return *this; } if(_raw(_key(_element[2])) == _terminalKey) [[unlikely]] { if(_raw(_key(_element[1])) == _terminalKey) [[unlikely]] { if(rawKey == _vacantVacantKey) [[likely]] { _element = nullptr; } return *this; } if(_raw(_key(_element[3])) == _terminalKey) [[likely]] { if(rawKey == _vacantGraveKey) [[likely]] { if(_raw(_key(_element[1])) == _vacantVacantKey) [[likely]] { _element = nullptr; } else { ++_element; } } return *this; } } } } template template inline auto RawMap::_Iterator::operator++(int) -> _Iterator { const _Iterator temp{*this}; operator++(); return temp; } template template template inline bool RawMap::_Iterator::operator==( const _Iterator &other) const { return _element == other._element; } } namespace std { template inline void swap(qc::hash::RawMap &a, qc::hash::RawMap &b) { a.swap(b); } } #define MOD_IS_998 1 #if __has_include("template.hpp") #include "template.hpp" #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using ll = long long; using pall = pair; template using vec = vector; template using veve = vec>; using vell = vec; using vest = vec; using vebo = basic_string; using vevell = veve; template using mset = multiset; template using priority_queue_ascend = priority_queue, greater>; constexpr ll inf = numeric_limits::max(); const string sp = " "; const string lf = "\n"; const auto &npos = string::npos; #ifdef MOD_IS_998 constexpr ll MOD = 998244353; #else constexpr ll MOD = 1e9 + 7; #endif const vec grid_move4 = {{0, 1}, {-1, 0}, {0, -1}, {1, 0}}; const vec grid_move8 = [] { auto ret = grid_move4; ret.insert(ret.end(), {{-1, 1}, {-1, -1}, {1, -1}, {1, 1}}); return ret; }(); #define cont continue #define br break #define whlie while #define foR for #define reutnr return #define retunr return #define reutrn return #define auot auto #define uato auto #define cosnt const #define conts const #define ocnst const auto &ciN = cin; auto &icn = cin; auto &icN = cin; constexpr bool ture = true; constexpr bool flase = false; namespace rnages = ranges; namespace ra = ranges; using namespace atcoder; using mint = static_modint; #define times(N) \ static_assert(is_integral_v, \ "times(): N must be integral"); \ assert((N) >= 0); \ for(typedef decltype((N) + 0) _int; \ [[maybe_unused]] const _int _i : views::iota((_int)0, (N))) #define tiems times #define itmes times template istream &operator>>(istream &in, static_modint &i) { ll tmp; in >> tmp; i = tmp; return in; } template ostream &operator<<(ostream &out, const static_modint &i) { return out << i.val(); } template istream &operator>>(istream &in, pair &p) { return in >> p.first >> p.second; } template ostream &operator<<(ostream &out, const pair &p) { return out << p.first << sp << p.second; } template istream &operator>>(istream &in, vec &v) { for(auto &&e : v) { in >> e; } return in; } struct debug_stream { template debug_stream &operator<<([[maybe_unused]] const T &x) { #ifndef ONLINE_JUDGE clog << x; #endif return *this; } debug_stream &operator<<([[maybe_unused]] ostream &(*f)(ostream &)) { #ifndef ONLINE_JUDGE clog << f; #endif return *this; } }; template concept out_stream = same_as || same_as; debug_stream clog_; #define clog clog_ template istream &in(Ts &...vecs) { static_assert(sizeof...(vecs) != 0, "myfunc::in(): At least one vector must be provided"); const set sizes = {vecs.size()...}; if(sizes.size() > 1) { throw invalid_argument("myfunc::in(): All vectors must have the same size"); } times(*sizes.begin()) { ((cin >> vecs[_i]), ...); } return cin; } void out(ranges::range auto v, const string delim, out_stream auto &out) { ranges::for_each(v, [&](const auto &e) { out << e << delim; }); } void out(ranges::range auto v, const string delim) { out(v, delim, cout); } [[nodiscard]] const string &yesno(const bool cond, const string &yes = "Yes", const string &no = "No") noexcept { if(cond) return yes; return no; } [[nodiscard]] uint64_t randint(const uint64_t mi, const uint64_t ma) noexcept { static random_device seed; static mt19937_64 mt(seed()); if(mi > ma) [[unlikely]] return randint(ma, mi); if(mi == ma) [[unlikely]] return mi; const uint64_t w = ma - mi; uint64_t r; do { r = mt(); } while(mt.max() - mt.max() % w <= r); return r % w + mi; } template requires common_with [[nodiscard]] constexpr auto min(T &&a, U &&b) noexcept { return std::min>(std::forward(a), std::forward(b)); } template requires common_with [[nodiscard]] constexpr auto max(T &&a, U &&b) noexcept { return std::max>(std::forward(a), std::forward(b)); } template [[nodiscard]] auto reduce(const ranges::range auto &r, Args &&...args) { return reduce(ranges::cbegin(r), ranges::cend(r), forward(args)...); } [[nodiscard]] constexpr ll powll(ll a, ll b, const ll m = inf) { if(b < 0) [[unlikely]] throw invalid_argument("powll(): exponent less than zero"); if(m < 1) [[unlikely]] throw invalid_argument("powll(): modulo less than one"); a %= m; ll ret = 1; while(b) { if(b % 2) ret *= a, ret %= m; a *= a, a %= m; b /= 2; } return ret; } template requires assignable_from && totally_ordered_with bool mini(T &var, U &&val) noexcept { const bool cmp = var > val; if(cmp) var = val; return cmp; } template requires assignable_from && totally_ordered_with bool maxi(T &var, U &&val) noexcept { const bool cmp = var < val; if(cmp) var = val; return cmp; } class [[nodiscard]] grid_base { public: grid_base(const ll h, const ll w) noexcept : height(h), width(w) {} [[nodiscard]] ll operator()(const ll i, const ll j) const noexcept { if(! isvalid(i, j)) return -1; return i * width + j; } [[nodiscard]] ll operator()(const pall &p) const noexcept { return (*this)(p.first, p.second); } protected: bool isvalid(const ll i, const ll j) const noexcept { return 0 <= i && 0 <= j && i < height && j < width; } const ll height, width; }; class [[nodiscard]] grid_seen : public grid_base { public: grid_seen(const ll h, const ll w) : grid_base(h, w) { visited = vebo(h * w, false); } [[nodiscard]] bool &seen(const ll i, const ll j) & { if(! isvalid(i, j)) [[unlikely]] throw out_of_range("grid_seen::seen(): out of range"); return visited[i * width + j]; } [[nodiscard]] bool &sen(const ll i, const ll j) & { return seen(i, j); } [[nodiscard]] bool &seen(const pall &p) & { return seen(p.first, p.second); } [[nodiscard]] bool &sen(const pall &p) & { return seen(p); } private: vebo visited; }; using grid_lite = grid_base; template requires is_convertible_v::mapped_type> class default_map : public map { using Map = map; public: #ifdef __cpp_lib_associative_heterogeneous_insertion template U> requires requires(Map::key_compare c, Map::key_type t, U u) { c(t, u); c(u, t); } Map::mapped_type &operator[](const U &key) { Map::try_emplace(key, defval); return Map::operator[](key); } template U> requires requires(Map::key_compare c, Map::key_type t, U u) { c(t, u); c(u, t); } Map::mapped_type &operator[](const U &&key) { Map::try_emplace(key, defval); return Map::operator[](key); } #else Map::mapped_type &operator[](const Map::key_type &key) { Map::try_emplace(key, defval); return Map::operator[](key); } Map::mapped_type &operator[](const Map::key_type &&key) { Map::try_emplace(key, defval); return Map::operator[](key); } #endif }; template requires is_convertible_v::mapped_type> class default_unordered_map : public unordered_map { using Map = unordered_map; public: #ifdef __cpp_lib_associative_heterogeneous_insertion template requires requires(Map::hasher h, U u) { h(u); } Map::mapped_type &operator[](const U &key) { Map::try_emplace(key, defval); return Map::operator[](key); } template requires requires(Map::hasher h, U u) { h(u); } Map::mapped_type &operator[](const U &&key) { Map::try_emplace(key, defval); return Map::operator[](key); } #else Map::mapped_type &operator[](const Map::key_type &key) { Map::try_emplace(key, defval); return Map::operator[](key); } Map::mapped_type &operator[](const Map::key_type &&key) { Map::try_emplace(key, defval); return Map::operator[](key); } #endif }; template requires requires(mset::key_compare c, T t, U u) { c(t, u); } auto erase_single(mset &mset, U &&v) { const auto it = mset.find(v); if(it == mset.end()) [[unlikely]] throw invalid_argument("erase_single(): why v not in mset!?!?"); return mset.erase(it); } void solve(); #endif int main(void) { cin.tie(nullptr); ios::sync_with_stdio(false); ll t = 1; times(t) solve(); return 0; } #include #include #include #include using namespace std; using ll = long long; class linear_sieve { public: linear_sieve(const ll n) { minfactor.resize(n + 1, 0); minfactor[0] = 0; minfactor[1] = 1; primes.reserve(ll(n / log(n) * 1.15)); for(ll i = 2; i <= n; i++) { if(! minfactor[i]) minfactor[i] = i, primes.push_back(i); for(size_t j = 0; j != primes.size() && primes[j] <= minfactor[i]; j++) { if(i * primes[j] > n) break; minfactor[i * primes[j]] = primes[j]; } } } vector minfactor, primes; }; class segment_sieve : public linear_sieve { public: segment_sieve(const ll l, const ll r) : linear_sieve((ll)sqrt(r) + 10) { isprime = vector(r - l + 1, true); for(const auto &p : primes) { ll cur = max(p * p, (l + p - 1) / p * p) - l; for(; cur < r - l + 1; cur += p) { isprime[cur] = false; } } } vector isprime; }; void solve() { const auto lpf = linear_sieve(1e6 + 10).minfactor; ll n; cin >> n; vell a(n); cin >> a; vevell nei(n); times(n - 1) { ll u, v; cin >> u >> v; u--, v--; nei[u].push_back(v); nei[v].push_back(u); } vec ans(n); vebo sen(n, false); const auto dfs = [&](auot &&self, const ll i) -> qc::hash::RawMap { sen[i] = ture; qc::hash::RawMap m; mint mp = a[i]; { ll tmp = a[i]; while(tmp != 1) { m[lpf[tmp]]++; tmp /= lpf[tmp]; } } clog << "in v" << i << lf; for(const auot &e : nei[i]) { if(sen[e]) cont; auto m2 = self(self, e); if(m.size() < m2.size()) { swap(m, m2); mp = ans[e]; } clog << "mp: " << mp << lf; clog << "m: "; out(m, sp, clog), clog << lf; clog << "m2: "; out(m2, sp, clog), clog << lf; for(const auto &[p, e] : m2) { if(m.contains(p) && m[p] >= e) cont; mp *= mint(p).pow(e - m[p]); m[p] = e; } clog << "mp: " << mp << lf; clog << "m: "; out(m, sp, clog), clog << lf; } ans[i] = mp; retunr m; }; dfs(dfs, 0); out(ans, lf); }