// pbrt is Copyright(c) 1998-2020 Matt Pharr, Wenzel Jakob, and Greg Humphreys. // The pbrt source code is licensed under the Apache License, Version 2.0. // SPDX: Apache-2.0 #ifndef PBRT_UTIL_PSTD_H #define PBRT_UTIL_PSTD_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace pstd { template PBRT_CPU_GPU inline void swap(T &a, T &b) { T tmp = std::move(a); a = std::move(b); b = std::move(tmp); } template PBRT_CPU_GPU typename std::enable_if_t && std::is_trivially_copyable_v, To> bit_cast(const From &src) noexcept { static_assert(std::is_trivially_constructible_v, "This implementation requires the destination type to be trivially " "constructible"); To dst; std::memcpy(&dst, &src, sizeof(To)); return dst; } template class array; // Specialization for zero element arrays (to make MSVC happy) template class array { public: using value_type = T; using iterator = value_type *; using const_iterator = const value_type *; using size_t = std::size_t; array() = default; PBRT_CPU_GPU void fill(const T &v) { assert(!"should never be called"); } PBRT_CPU_GPU bool operator==(const array &a) const { return true; } PBRT_CPU_GPU bool operator!=(const array &a) const { return false; } PBRT_CPU_GPU iterator begin() { return nullptr; } PBRT_CPU_GPU iterator end() { return nullptr; } PBRT_CPU_GPU const_iterator begin() const { return nullptr; } PBRT_CPU_GPU const_iterator end() const { return nullptr; } PBRT_CPU_GPU size_t size() const { return 0; } PBRT_CPU_GPU T &operator[](size_t i) { assert(!"should never be called"); static T t; return t; } PBRT_CPU_GPU const T &operator[](size_t i) const { assert(!"should never be called"); static T t; return t; } PBRT_CPU_GPU T *data() { return nullptr; } PBRT_CPU_GPU const T *data() const { return nullptr; } }; template class array { public: using value_type = T; using iterator = value_type *; using const_iterator = const value_type *; using size_t = std::size_t; array() = default; PBRT_CPU_GPU array(std::initializer_list v) { size_t i = 0; for (const T &val : v) values[i++] = val; } PBRT_CPU_GPU void fill(const T &v) { for (int i = 0; i < N; ++i) values[i] = v; } PBRT_CPU_GPU bool operator==(const array &a) const { for (int i = 0; i < N; ++i) if (values[i] != a.values[i]) return false; return true; } PBRT_CPU_GPU bool operator!=(const array &a) const { return !(*this == a); } PBRT_CPU_GPU iterator begin() { return values; } PBRT_CPU_GPU iterator end() { return values + N; } PBRT_CPU_GPU const_iterator begin() const { return values; } PBRT_CPU_GPU const_iterator end() const { return values + N; } PBRT_CPU_GPU size_t size() const { return N; } PBRT_CPU_GPU T &operator[](size_t i) { return values[i]; } PBRT_CPU_GPU const T &operator[](size_t i) const { return values[i]; } PBRT_CPU_GPU T *data() { return values; } PBRT_CPU_GPU const T *data() const { return values; } private: T values[N] = {}; }; template class optional { public: using value_type = T; optional() = default; PBRT_CPU_GPU optional(const T &v) : set(true) { new (ptr()) T(v); } PBRT_CPU_GPU optional(T &&v) : set(true) { new (ptr()) T(std::move(v)); } PBRT_CPU_GPU optional(const optional &v) : set(v.has_value()) { if (v.has_value()) new (ptr()) T(v.value()); } PBRT_CPU_GPU optional(optional &&v) : set(v.has_value()) { if (v.has_value()) { new (ptr()) T(std::move(v.value())); v.reset(); } } PBRT_CPU_GPU optional &operator=(const T &v) { reset(); new (ptr()) T(v); set = true; return *this; } PBRT_CPU_GPU optional &operator=(T &&v) { reset(); new (ptr()) T(std::move(v)); set = true; return *this; } PBRT_CPU_GPU optional &operator=(const optional &v) { reset(); if (v.has_value()) { new (ptr()) T(v.value()); set = true; } return *this; } PBRT_CPU_GPU optional &operator=(optional &&v) { reset(); if (v.has_value()) { new (ptr()) T(std::move(v.value())); set = true; v.reset(); } return *this; } PBRT_CPU_GPU ~optional() { reset(); } PBRT_CPU_GPU explicit operator bool() const { return set; } PBRT_CPU_GPU T value_or(const T &alt) const { return set ? value() : alt; } PBRT_CPU_GPU T *operator->() { return &value(); } PBRT_CPU_GPU const T *operator->() const { return &value(); } PBRT_CPU_GPU T &operator*() { return value(); } PBRT_CPU_GPU const T &operator*() const { return value(); } PBRT_CPU_GPU T &value() { CHECK(set); return *ptr(); } PBRT_CPU_GPU const T &value() const { CHECK(set); return *ptr(); } PBRT_CPU_GPU void reset() { if (set) { value().~T(); set = false; } } PBRT_CPU_GPU bool has_value() const { return set; } private: #ifdef __NVCC__ // Work-around NVCC bug PBRT_CPU_GPU T *ptr() { return reinterpret_cast(&optionalValue); } PBRT_CPU_GPU const T *ptr() const { return reinterpret_cast(&optionalValue); } #else PBRT_CPU_GPU T *ptr() { return std::launder(reinterpret_cast(&optionalValue)); } PBRT_CPU_GPU const T *ptr() const { return std::launder(reinterpret_cast(&optionalValue)); } #endif std::aligned_storage_t optionalValue; bool set = false; }; template inline std::ostream &operator<<(std::ostream &os, const optional &opt) { if (opt.has_value()) return os << "[ pstd::optional<" << typeid(T).name() << "> set: true " << "value: " << opt.value() << " ]"; else return os << "[ pstd::optional<" << typeid(T).name() << "> set: false value: n/a ]"; } namespace span_internal { // Wrappers for access to container data pointers. template PBRT_CPU_GPU inline constexpr auto GetDataImpl(C &c, char) noexcept -> decltype(c.data()) { return c.data(); } template PBRT_CPU_GPU inline constexpr auto GetData(C &c) noexcept -> decltype(GetDataImpl(c, 0)) { return GetDataImpl(c, 0); } // Detection idioms for size() and data(). template using HasSize = std::is_integral().size())>>; // We want to enable conversion from vector to span but // disable conversion from vector to span. Here we use // the fact that U** is convertible to Q* const* if and only if Q is the same // type or a more cv-qualified version of U. We also decay the result type of // data() to avoid problems with classes which have a member function data() // which returns a reference. template using HasData = std::is_convertible()))> *, T *const *>; } // namespace span_internal inline constexpr std::size_t dynamic_extent = -1; // span implementation partially based on absl::Span from Google's Abseil library. template class span { public: // Used to determine whether a Span can be constructed from a container of // type C. template using EnableIfConvertibleFrom = typename std::enable_if_t::value && span_internal::HasSize::value>; // Used to SFINAE-enable a function when the slice elements are const. template using EnableIfConstView = typename std::enable_if_t::value, U>; // Used to SFINAE-enable a function when the slice elements are mutable. template using EnableIfMutableView = typename std::enable_if_t::value, U>; using value_type = typename std::remove_cv_t; using iterator = T *; using const_iterator = const T *; PBRT_CPU_GPU span() : ptr(nullptr), n(0) {} PBRT_CPU_GPU span(T *ptr, size_t n) : ptr(ptr), n(n) {} template PBRT_CPU_GPU span(T (&a)[N]) : span(a, N) {} PBRT_CPU_GPU span(std::initializer_list v) : span(v.begin(), v.size()) {} // Explicit reference constructor for a mutable `span` type. Can be // replaced with MakeSpan() to infer the type parameter. template , typename Y = EnableIfMutableView> PBRT_CPU_GPU explicit span(V &v) noexcept : span(v.data(), v.size()) {} // Hack: explicit constructors for std::vector to work around warnings // about calling a host function (e.g. vector::size()) form a // host+device function (the regular span constructor.) template span(std::vector &v) noexcept : span(v.data(), v.size()) {} template span(const std::vector &v) noexcept : span(v.data(), v.size()) {} // Implicit reference constructor for a read-only `span` type template , typename Y = EnableIfConstView> PBRT_CPU_GPU constexpr span(const V &v) noexcept : span(v.data(), v.size()) {} PBRT_CPU_GPU iterator begin() { return ptr; } PBRT_CPU_GPU iterator end() { return ptr + n; } PBRT_CPU_GPU const_iterator begin() const { return ptr; } PBRT_CPU_GPU const_iterator end() const { return ptr + n; } PBRT_CPU_GPU T &operator[](size_t i) { DCHECK_LT(i, size()); return ptr[i]; } PBRT_CPU_GPU const T &operator[](size_t i) const { DCHECK_LT(i, size()); return ptr[i]; } PBRT_CPU_GPU size_t size() const { return n; }; PBRT_CPU_GPU bool empty() const { return size() == 0; } PBRT_CPU_GPU T *data() { return ptr; } PBRT_CPU_GPU const T *data() const { return ptr; } PBRT_CPU_GPU T front() const { return ptr[0]; } PBRT_CPU_GPU T back() const { return ptr[n - 1]; } PBRT_CPU_GPU void remove_prefix(size_t count) { // assert(size() >= count); ptr += count; n -= count; } PBRT_CPU_GPU void remove_suffix(size_t count) { // assert(size() > = count); n -= count; } PBRT_CPU_GPU span subspan(size_t pos, size_t count = dynamic_extent) { size_t np = count < (size() - pos) ? count : (size() - pos); return span(ptr + pos, np); } private: T *ptr; size_t n; }; template PBRT_CPU_GPU inline constexpr span MakeSpan(T *ptr, size_t size) noexcept { return span(ptr, size); } template PBRT_CPU_GPU inline span MakeSpan(T *begin, T *end) noexcept { return span(begin, end - begin); } template inline span MakeSpan(std::vector &v) noexcept { return span(v.data(), v.size()); } template PBRT_CPU_GPU inline constexpr auto MakeSpan(C &c) noexcept -> decltype(MakeSpan(span_internal::GetData(c), c.size())) { return MakeSpan(span_internal::GetData(c), c.size()); } template PBRT_CPU_GPU inline constexpr span MakeSpan(T (&array)[N]) noexcept { return span(array, N); } template PBRT_CPU_GPU inline constexpr span MakeConstSpan(T *ptr, size_t size) noexcept { return span(ptr, size); } template PBRT_CPU_GPU inline span MakeConstSpan(T *begin, T *end) noexcept { return span(begin, end - begin); } template inline span MakeConstSpan(const std::vector &v) noexcept { return span(v.data(), v.size()); } template PBRT_CPU_GPU inline constexpr auto MakeConstSpan(const C &c) noexcept -> decltype(MakeSpan(c)) { return MakeSpan(c); } template PBRT_CPU_GPU inline constexpr span MakeConstSpan(const T (&array)[N]) noexcept { return span(array, N); } // memory_resource... namespace pmr { class memory_resource { static constexpr size_t max_align = alignof(std::max_align_t); public: virtual ~memory_resource(); void *allocate(size_t bytes, size_t alignment = max_align) { if (bytes == 0) return nullptr; return do_allocate(bytes, alignment); } void deallocate(void *p, size_t bytes, size_t alignment = max_align) { if (!p) return; return do_deallocate(p, bytes, alignment); } bool is_equal(const memory_resource &other) const noexcept { return do_is_equal(other); } private: virtual void *do_allocate(size_t bytes, size_t alignment) = 0; virtual void do_deallocate(void *p, size_t bytes, size_t alignment) = 0; virtual bool do_is_equal(const memory_resource &other) const noexcept = 0; }; inline bool operator==(const memory_resource &a, const memory_resource &b) noexcept { return a.is_equal(b); } inline bool operator!=(const memory_resource &a, const memory_resource &b) noexcept { return !(a == b); } // TODO struct pool_options { size_t max_blocks_per_chunk = 0; size_t largest_required_pool_block = 0; }; class synchronized_pool_resource; class unsynchronized_pool_resource; // global memory resources memory_resource *new_delete_resource() noexcept; // TODO: memory_resource* null_memory_resource() noexcept; memory_resource *set_default_resource(memory_resource *r) noexcept; memory_resource *get_default_resource() noexcept; class alignas(64) monotonic_buffer_resource : public memory_resource { public: explicit monotonic_buffer_resource(memory_resource *upstream) : upstream(upstream) { #ifndef NDEBUG constructTID = std::this_thread::get_id(); #endif } monotonic_buffer_resource(size_t block_size, memory_resource *upstream) : block_size(block_size), upstream(upstream) { #ifndef NDEBUG constructTID = std::this_thread::get_id(); #endif } #if 0 // TODO monotonic_buffer_resource(void *buffer, size_t buffer_size, memory_resource *upstream); #endif monotonic_buffer_resource() : monotonic_buffer_resource(get_default_resource()) {} explicit monotonic_buffer_resource(size_t initial_size) : monotonic_buffer_resource(initial_size, get_default_resource()) {} #if 0 // TODO monotonic_buffer_resource(void *buffer, size_t buffer_size) : monotonic_buffer_resource(buffer, buffer_size, get_default_resource()) {} #endif monotonic_buffer_resource(const monotonic_buffer_resource &) = delete; ~monotonic_buffer_resource() { release(); } monotonic_buffer_resource operator=(const monotonic_buffer_resource &) = delete; void release() { block *b = block_list; while (b) { block *next = b->next; free_block(b); b = next; } block_list = nullptr; current = nullptr; } memory_resource *upstream_resource() const { return upstream; } protected: void *do_allocate(size_t bytes, size_t align) override; void do_deallocate(void *p, size_t bytes, size_t alignment) override { if (bytes > block_size) // do_allocate() passes large allocations on to the upstream memory resource, // so we might as well deallocate when it's possible. upstream->deallocate(p, bytes); } bool do_is_equal(const memory_resource &other) const noexcept override { return this == &other; } private: struct block { void *ptr; size_t size; block *next; }; block *allocate_block(size_t size) { // Single allocation for both the block and its memory. This means // that strictly speaking MemoryBlock::ptr is redundant, but let's not get too // fancy here... block *b = static_cast( upstream->allocate(sizeof(block) + size, alignof(block))); b->ptr = reinterpret_cast(b) + sizeof(block); b->size = size; b->next = block_list; block_list = b; return b; } void free_block(block *b) { upstream->deallocate(b, sizeof(block) + b->size); } #ifndef NDEBUG std::thread::id constructTID; #endif memory_resource *upstream; size_t block_size = 256 * 1024; block *current = nullptr; size_t current_pos = 0; block *block_list = nullptr; }; template class polymorphic_allocator { public: using value_type = Tp; polymorphic_allocator() noexcept { memoryResource = new_delete_resource(); } polymorphic_allocator(memory_resource *r) : memoryResource(r) {} polymorphic_allocator(const polymorphic_allocator &other) = default; template polymorphic_allocator(const polymorphic_allocator &other) noexcept : memoryResource(other.resource()) {} polymorphic_allocator &operator=(const polymorphic_allocator &rhs) = delete; // member functions [[nodiscard]] Tp *allocate(size_t n) { return static_cast(resource()->allocate(n * sizeof(Tp), alignof(Tp))); } void deallocate(Tp *p, size_t n) { resource()->deallocate(p, n); } void *allocate_bytes(size_t nbytes, size_t alignment = alignof(max_align_t)) { return resource()->allocate(nbytes, alignment); } void deallocate_bytes(void *p, size_t nbytes, size_t alignment = alignof(std::max_align_t)) { return resource()->deallocate(p, nbytes, alignment); } template T *allocate_object(size_t n = 1) { return static_cast(allocate_bytes(n * sizeof(T), alignof(T))); } template void deallocate_object(T *p, size_t n = 1) { deallocate_bytes(p, n * sizeof(T), alignof(T)); } template T *new_object(Args &&...args) { // NOTE: this doesn't handle constructors that throw exceptions... T *p = allocate_object(); construct(p, std::forward(args)...); return p; } template void delete_object(T *p) { destroy(p); deallocate_object(p); } template void construct(T *p, Args &&...args) { ::new ((void *)p) T(std::forward(args)...); } template void destroy(T *p) { p->~T(); } // polymorphic_allocator select_on_container_copy_construction() const; memory_resource *resource() const { return memoryResource; } private: memory_resource *memoryResource; }; template bool operator==(const polymorphic_allocator &a, const polymorphic_allocator &b) noexcept { return a.resource() == b.resource(); } template bool operator!=(const polymorphic_allocator &a, const polymorphic_allocator &b) noexcept { return !(a == b); } } // namespace pmr template > class vector { public: using value_type = T; using allocator_type = Allocator; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = value_type &; using const_reference = const value_type &; using pointer = T *; using const_pointer = const T *; using iterator = T *; using const_iterator = const T *; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; vector(const Allocator &alloc = {}) : alloc(alloc) {} vector(size_t count, const T &value, const Allocator &alloc = {}) : alloc(alloc) { reserve(count); for (size_t i = 0; i < count; ++i) this->alloc.template construct(ptr + i, value); nStored = count; } vector(size_t count, const Allocator &alloc = {}) : vector(count, T{}, alloc) {} vector(const vector &other, const Allocator &alloc = {}) : alloc(alloc) { reserve(other.size()); for (size_t i = 0; i < other.size(); ++i) this->alloc.template construct(ptr + i, other[i]); nStored = other.size(); } template vector(InputIt first, InputIt last, const Allocator &alloc = {}) : alloc(alloc) { reserve(last - first); size_t i = 0; for (InputIt iter = first; iter != last; ++iter, ++i) this->alloc.template construct(ptr + i, *iter); nStored = nAlloc; } vector(vector &&other) : alloc(other.alloc) { nStored = other.nStored; nAlloc = other.nAlloc; ptr = other.ptr; other.nStored = other.nAlloc = 0; other.ptr = nullptr; } vector(vector &&other, const Allocator &alloc) { if (alloc == other.alloc) { ptr = other.ptr; nAlloc = other.nAlloc; nStored = other.nStored; other.ptr = nullptr; other.nAlloc = other.nStored = 0; } else { reserve(other.size()); for (size_t i = 0; i < other.size(); ++i) alloc.template construct(ptr + i, std::move(other[i])); nStored = other.size(); } } vector(std::initializer_list init, const Allocator &alloc = {}) : vector(init.begin(), init.end(), alloc) {} vector &operator=(const vector &other) { if (this == &other) return *this; clear(); reserve(other.size()); for (size_t i = 0; i < other.size(); ++i) alloc.template construct(ptr + i, other[i]); nStored = other.size(); return *this; } vector &operator=(vector &&other) { if (this == &other) return *this; if (alloc == other.alloc) { pstd::swap(ptr, other.ptr); pstd::swap(nAlloc, other.nAlloc); pstd::swap(nStored, other.nStored); } else { clear(); reserve(other.size()); for (size_t i = 0; i < other.size(); ++i) alloc.template construct(ptr + i, std::move(other[i])); nStored = other.size(); } return *this; } vector &operator=(std::initializer_list &init) { reserve(init.size()); clear(); iterator iter = begin(); for (const auto &value : init) { *iter = value; ++iter; } return *this; } void assign(size_type count, const T &value) { clear(); reserve(count); for (size_t i = 0; i < count; ++i) push_back(value); } template void assign(InputIt first, InputIt last) { LOG_FATAL("TODO"); // TODO } void assign(std::initializer_list &init) { assign(init.begin(), init.end()); } ~vector() { clear(); alloc.deallocate_object(ptr, nAlloc); } PBRT_CPU_GPU iterator begin() { return ptr; } PBRT_CPU_GPU iterator end() { return ptr + nStored; } PBRT_CPU_GPU const_iterator begin() const { return ptr; } PBRT_CPU_GPU const_iterator end() const { return ptr + nStored; } PBRT_CPU_GPU const_iterator cbegin() const { return ptr; } PBRT_CPU_GPU const_iterator cend() const { return ptr + nStored; } PBRT_CPU_GPU reverse_iterator rbegin() { return reverse_iterator(end()); } PBRT_CPU_GPU reverse_iterator rend() { return reverse_iterator(begin()); } PBRT_CPU_GPU const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } PBRT_CPU_GPU const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } allocator_type get_allocator() const { return alloc; } PBRT_CPU_GPU size_t size() const { return nStored; } PBRT_CPU_GPU bool empty() const { return size() == 0; } PBRT_CPU_GPU size_t max_size() const { return (size_t)-1; } PBRT_CPU_GPU size_t capacity() const { return nAlloc; } void reserve(size_t n) { if (nAlloc >= n) return; T *ra = alloc.template allocate_object(n); for (int i = 0; i < nStored; ++i) { alloc.template construct(ra + i, std::move(begin()[i])); alloc.destroy(begin() + i); } alloc.deallocate_object(ptr, nAlloc); nAlloc = n; ptr = ra; } // TODO: shrink_to_fit PBRT_CPU_GPU reference operator[](size_type index) { DCHECK_LT(index, size()); return ptr[index]; } PBRT_CPU_GPU const_reference operator[](size_type index) const { DCHECK_LT(index, size()); return ptr[index]; } PBRT_CPU_GPU reference front() { return ptr[0]; } PBRT_CPU_GPU const_reference front() const { return ptr[0]; } PBRT_CPU_GPU reference back() { return ptr[nStored - 1]; } PBRT_CPU_GPU const_reference back() const { return ptr[nStored - 1]; } PBRT_CPU_GPU pointer data() { return ptr; } PBRT_CPU_GPU const_pointer data() const { return ptr; } void clear() { for (int i = 0; i < nStored; ++i) alloc.destroy(&ptr[i]); nStored = 0; } iterator insert(const_iterator, const T &value) { // TODO LOG_FATAL("TODO"); } iterator insert(const_iterator, T &&value) { // TODO LOG_FATAL("TODO"); } iterator insert(const_iterator pos, size_type count, const T &value) { // TODO LOG_FATAL("TODO"); } template iterator insert(const_iterator pos, InputIt first, InputIt last) { if (pos == end()) { size_t firstOffset = size(); for (auto iter = first; iter != last; ++iter) push_back(*iter); return begin() + firstOffset; } else LOG_FATAL("TODO"); } iterator insert(const_iterator pos, std::initializer_list init) { // TODO LOG_FATAL("TODO"); } template iterator emplace(const_iterator pos, Args &&...args) { // TODO LOG_FATAL("TODO"); } template void emplace_back(Args &&...args) { if (nAlloc == nStored) reserve(nAlloc == 0 ? 4 : 2 * nAlloc); alloc.construct(ptr + nStored, std::forward(args)...); ++nStored; } iterator erase(const_iterator pos) { // TODO LOG_FATAL("TODO"); } iterator erase(const_iterator first, const_iterator last) { // TODO LOG_FATAL("TODO"); } void push_back(const T &value) { if (nAlloc == nStored) reserve(nAlloc == 0 ? 4 : 2 * nAlloc); alloc.construct(ptr + nStored, value); ++nStored; } void push_back(T &&value) { if (nAlloc == nStored) reserve(nAlloc == 0 ? 4 : 2 * nAlloc); alloc.construct(ptr + nStored, std::move(value)); ++nStored; } void pop_back() { DCHECK(!empty()); alloc.destroy(ptr + nStored - 1); --nStored; } void resize(size_type n) { if (n < size()) { for (size_t i = n; i < size(); ++i) alloc.destroy(ptr + i); if (n == 0) { alloc.deallocate_object(ptr, nAlloc); ptr = nullptr; nAlloc = 0; } } else if (n > size()) { reserve(n); for (size_t i = nStored; i < n; ++i) alloc.construct(ptr + i); } nStored = n; } void resize(size_type count, const value_type &value) { // TODO LOG_FATAL("TODO"); } void swap(vector &other) { CHECK(alloc == other.alloc); // TODO: handle this std::swap(ptr, other.ptr); std::swap(nAlloc, other.nAlloc); std::swap(nStored, other.nStored); } private: Allocator alloc; T *ptr = nullptr; size_t nAlloc = 0, nStored = 0; }; template struct tuple; template <> struct tuple<> { template using type = void; }; template struct tuple : tuple { using Base = tuple; tuple() = default; tuple(const tuple &) = default; tuple(tuple &&) = default; tuple &operator=(tuple &&) = default; tuple &operator=(const tuple &) = default; tuple(const T &value, const Ts &...values) : Base(values...), value(value) {} tuple(T &&value, Ts &&...values) : Base(std::move(values)...), value(std::move(value)) {} T value; }; template tuple(Ts &&...) -> tuple...>; template PBRT_CPU_GPU auto &get(tuple &t) { if constexpr (I == 0) return t.value; else return get((tuple &)t); } template PBRT_CPU_GPU const auto &get(const tuple &t) { if constexpr (I == 0) return t.value; else return get((const tuple &)t); } template PBRT_CPU_GPU auto &get(tuple &t) { if constexpr (std::is_same_v) return t.value; else return get((tuple &)t); } template PBRT_CPU_GPU const auto &get(const tuple &t) { if constexpr (std::is_same_v) return t.value; else return get((const tuple &)t); } template struct complex { PBRT_CPU_GPU complex(T re) : re(re), im(0) {} PBRT_CPU_GPU complex(T re, T im) : re(re), im(im) {} PBRT_CPU_GPU complex operator-() const { return {-re, -im}; } PBRT_CPU_GPU complex operator+(complex z) const { return {re + z.re, im + z.im}; } PBRT_CPU_GPU complex operator-(complex z) const { return {re - z.re, im - z.im}; } PBRT_CPU_GPU complex operator*(complex z) const { return {re * z.re - im * z.im, re * z.im + im * z.re}; } PBRT_CPU_GPU complex operator/(complex z) const { T scale = 1 / (z.re * z.re + z.im * z.im); return {scale * (re * z.re + im * z.im), scale * (im * z.re - re * z.im)}; } friend PBRT_CPU_GPU complex operator+(T value, complex z) { return complex(value) + z; } friend PBRT_CPU_GPU complex operator-(T value, complex z) { return complex(value) - z; } friend PBRT_CPU_GPU complex operator*(T value, complex z) { return complex(value) * z; } friend PBRT_CPU_GPU complex operator/(T value, complex z) { return complex(value) / z; } T re, im; }; PBRT_CPU_GPU inline float sqrt(float f) { return ::sqrtf(f); } PBRT_CPU_GPU inline double sqrt(double f) { return ::sqrt(f); } PBRT_CPU_GPU inline float abs(float f) { return ::fabsf(f); } PBRT_CPU_GPU inline double abs(double f) { return ::fabs(f); } PBRT_CPU_GPU inline float copysign(float mag, float sign) { #ifdef PBRT_IS_GPU_CODE return ::copysignf(mag, sign); #else return std::copysign(mag, sign); #endif } PBRT_CPU_GPU inline double copysign(double mag, double sign) { #ifdef PBRT_IS_GPU_CODE return ::copysign(mag, sign); #else return std::copysign(mag, sign); #endif } PBRT_CPU_GPU inline float floor(float arg) { #ifdef PBRT_IS_GPU_CODE return ::floorf(arg); #else return std::floor(arg); #endif } PBRT_CPU_GPU inline double floor(double arg) { #ifdef PBRT_IS_GPU_CODE return ::floor(arg); #else return std::floor(arg); #endif } PBRT_CPU_GPU inline float ceil(float arg) { #ifdef PBRT_IS_GPU_CODE return ::ceilf(arg); #else return std::ceil(arg); #endif } PBRT_CPU_GPU inline double ceil(double arg) { #ifdef PBRT_IS_GPU_CODE return ::ceil(arg); #else return std::ceil(arg); #endif } PBRT_CPU_GPU inline float round(float arg) { #ifdef PBRT_IS_GPU_CODE return ::roundf(arg); #else return std::round(arg); #endif } PBRT_CPU_GPU inline double round(double arg) { #ifdef PBRT_IS_GPU_CODE return ::round(arg); #else return std::round(arg); #endif } template PBRT_CPU_GPU T real(const complex &z) { return z.re; } template PBRT_CPU_GPU T imag(const complex &z) { return z.im; } template PBRT_CPU_GPU T norm(const complex &z) { return z.re * z.re + z.im * z.im; } template PBRT_CPU_GPU T abs(const complex &z) { return pstd::sqrt(pstd::norm(z)); } template PBRT_CPU_GPU complex sqrt(const complex &z) { T n = pstd::abs(z), t1 = pstd::sqrt(T(.5) * (n + pstd::abs(z.re))), t2 = T(.5) * z.im / t1; if (n == 0) return 0; if (z.re >= 0) return {t1, t2}; else return {pstd::abs(t2), pstd::copysign(t1, z.im)}; } } // namespace pstd #endif // PBRT_UTIL_PSTD_H