Loading...
Searching...
No Matches
control.h
Go to the documentation of this file.
1#ifndef _BBM_ENOKI_CONTROL_H_
2#define _BBM_ENOKI_CONTROL_H_
3
4#include "core/error.h"
6#include "backbone/type_traits.h"
7#include "backbone/array.h"
8
9/************************************************************************/
10/*! \file control.h
11
12 \brief Data and flow control
13 + select
14 + lookup
15 + cast
16 + set
17 + binary_search
18
19*************************************************************************/
20
21namespace backbone {
22
23 /**********************************************************************/
24 /*! \brief cast
25 **********************************************************************/
26 template<typename NEWTYPE, typename OLDTYPE>
27 NEWTYPE cast(OLDTYPE&& val)
28 {
29 if constexpr (is_diff_v<OLDTYPE> && !is_diff_v<NEWTYPE>) return NEWTYPE(enoki::detach(val));
30 else return NEWTYPE(val);
31 }
32
33 /**********************************************************************/
34 /*! \brief Extension of enoki::select robust to diff/non-diff masks
35 *********************************************************************/
36 template<typename MASK, typename A, typename B>
37 inline auto select(MASK&& mask, A&& a, B&& b)
38 {
39 if constexpr (std::same_as<std::decay_t<MASK>, detail::bbm_mask>)
40 return backbone::select(enoki::DiffArray<bool>(mask), std::forward<A>(a), std::forward<B>(b));
41 else if constexpr (is_diff_v<MASK> && !is_diff_v<A> && !is_diff_v<B>)
42 return enoki::select(enoki::detach(mask), std::forward<A>(a), std::forward<B>(b));
43 else if constexpr (!is_diff_v<MASK> && is_diff_v<A>)
44 return enoki::select(mask_t<A>(mask), std::forward<A>(a), std::forward<B>(b));
45 else if constexpr (!is_diff_v<MASK> && is_diff_v<B>)
46 return enoki::select(mask_t<B>(mask), std::forward<A>(a), std::forward<B>(b));
47 else if constexpr (is_packet_v<MASK> && !is_packet_v<A> && !is_packet_v<B>)
48 return enoki::select(std::forward<MASK>(mask), enoki::Packet<A>(a), enoki::Packet<B>(b));
49 else
50 return enoki::select(std::forward<MASK>(mask), std::forward<A>(a), std::forward<B>(b));
51 }
52
53 /**********************************************************************/
54 /*! \brief Non-packet look up
55 **********************************************************************/
56 template<typename RET, typename C, typename Index> requires (!is_packet_v<Index>) && std::ranges::range<C> && std::convertible_to<bbm::iterable_value_t<C>, RET> && is_index_v<Index>
57 inline constexpr RET lookup(C&& container, const Index& idx, const index_mask_t<Index>& mask=true)
58 {
59 // quick bailout
60 if(none(mask)) return RET();
61
62 // lookup
63 if(idx >= Index(bbm::size(container))) throw bbm_out_of_range;
64 return RET( *(std::next(bbm::begin(container), idx)));
65 }
66
67
68 /**********************************************************************/
69 /*! \brief Non-packet data, Packet look up
70 **********************************************************************/
71 template<typename RET, typename C, typename Index> requires is_packet_v<RET> && is_packet_v<Index> && std::ranges::range<C> && is_index_v<Index> && std::convertible_to<bbm::iterable_value_t<C>, remove_packet_t<RET>> && (!is_packet_v<bbm::iterable_value_t<C>>)
72 inline RET lookup(C&& container, const Index& idx, const index_mask_t<Index>& mask=true)
73 {
74 // quick bailout
75 if(none(mask)) return RET();
76
77 // lookup
78 RET result;
79 for(size_t i=0; i != idx.Size; ++i)
80 if(enoki::slice(mask,i))
81 enoki::packet(result, i) = backbone::lookup<bbm::iterable_value_t<C>>(std::forward<C>(container), enoki::slice(idx, i));
82
83 // Done.
84 return result;
85 }
86
87 /**********************************************************************/
88 /*! \brief Packet data, Packet look up
89 *********************************************************************/
90 template<typename RET, typename C, typename Index> requires is_packet_v<RET> && is_packet_v<Index> && std::ranges::range<C> && is_index_v<Index> && std::convertible_to<bbm::iterable_value_t<C>, RET> && is_packet_v<bbm::iterable_value_t<C>>
91 inline RET lookup(C&& container, const Index& idx, const index_mask_t<Index>& mask=true)
92 {
93 // quick bailout
94 if(none(mask)) return RET();
95
96 // lookup
97 RET result;
98 for(size_t i=0; i != idx.Size; ++i)
99 if( enoki::slice(mask, i) )
100 {
101 if(enoki::slice(idx, i) >= bbm::size(container)) throw bbm_out_of_range;
102 else enoki::slice(result, i) = enoki::slice(*(std::next(bbm::begin(container), enoki::slice(idx, i))), i);
103 }
104
105 // Done.
106 return result;
107 }
108
109
110 /**********************************************************************/
111 /*! \brief Non-packet set
112 **********************************************************************/
113 template<typename VAL, typename C, typename Index> requires (!is_packet_v<Index>) && std::ranges::range<C> && is_index_v<Index> && std::convertible_to<VAL, bbm::iterable_value_t<C>>
114 inline constexpr void set(C&& container, const Index& idx, VAL&& value, const index_mask_t<Index>& mask=true)
115 {
116 // quick bailout
117 if(none(mask)) return;
118
119 // set
120 if(idx >= Index(bbm::size(container))) throw bbm_out_of_range;
121 *(std::next(bbm::begin(std::forward<C>(container)), idx)) = std::forward<VAL>(value);
122 }
123
124
125 /**********************************************************************/
126 /*! \brief Non-packet data, Packet set
127 **********************************************************************/
128 template<typename VAL, typename C, typename Index> requires is_packet_v<VAL> && is_packet_v<Index> && std::ranges::range<C> && is_index_v<Index> && std::convertible_to<remove_packet_t<VAL>, bbm::iterable_value_t<C>> && (!is_packet_v<bbm::iterable_value_t<C>>)
129 inline void set(C&& container, const Index& idx, VAL&& value, const index_mask_t<Index>& mask=true)
130 {
131 // quick bailout
132 if(none(mask)) return;
133
134 // set
135 for(size_t i=0; i != idx.Size; ++i)
136 backbone::set(std::forward<C>(container), enoki::slice(idx, i), enoki::slice(value, i), enoki::slice(mask, i));
137 }
138
139
140 /**********************************************************************/
141 /*! \brief Packet data, Packet set
142 *********************************************************************/
143 template<typename VAL, typename C, typename Index> requires is_packet_v<VAL> && is_packet_v<Index> && std::ranges::range<C> && is_index_v<Index> && std::convertible_to<VAL, bbm::iterable_value_t<C>> && is_packet_v<bbm::iterable_value_t<C>>
144 inline void set(C&& container, const Index& idx, VAL&& value, const index_mask_t<Index>& mask=true)
145 {
146 // quick bailout
147 if(none(mask)) return;
148
149 // set
150 for(size_t i=0; i != idx.Size; ++i)
151 if( enoki::slice(mask, i) )
152 {
153 if(enoki::slice(idx, i) >= bbm::size(container)) throw bbm_out_of_range;
154 enoki::slice(*(std::next(bbm::begin(container), enoki::slice(idx, i))), i) = enoki::slice(value, i);
155 }
156 }
157
158
159 /**********************************************************************/
160 /*! \brief binary search
161 **********************************************************************/
162 template<typename C, typename PRED> requires std::ranges::range<C> && std::is_invocable_r_v<mask_t<bbm::iterable_value_t<C>>, PRED, bbm::iterable_value_t<C>>
163 inline constexpr index_t<bbm::iterable_value_t<C>> binary_search(C&& container, PRED&& predicate, const index_mask_t<bbm::iterable_value_t<C>>& mask=true)
164 {
165 using value_type = bbm::iterable_value_t<C>;
166 using index_type = index_t<value_type>;
167
168 // quick exit
169 if(none(mask)) return index_type(bbm::size(container));
170
171 // create a wrapper for the predicate to meet enoki's expectations
172 auto pred_wrapper = [&](const index_type& index)
173 {
174 auto result = predicate( backbone::lookup<value_type>(container, index, mask) );
175 return cast<index_mask_t<index_type>>(result);
176 };
177
178 // pass control to enoki
179 index_type idx = enoki::binary_search(0, bbm::size(container)-1, pred_wrapper);
180 return select(mask && !pred_wrapper(idx), idx, bbm::size(container));
181 }
182
183} // end backbone namespace
184
185
186#endif /* _BBM_ENOKI_CONTROL_H_ */
Predefined exceptions for common errors.
#define bbm_out_of_range
Definition: error.h:44
Extensions for STL iterators/ranges.
Random number generator; built on top of Drjit.
Definition: backbone.h:53
constexpr void set(C &&container, const Index &idx, VAL &&value, const index_mask_t< Index > &mask=true)
Non-packet set.
Definition: control.h:77
typename detail::index_impl< std::decay_t< T > >::mask index_mask_t
Return the mask of an index of a type.
Definition: type_traits.h:215
bool none(const T &t)
Definition: horizontal.h:56
constexpr RET lookup(C &&container, const Index &idx, const index_mask_t< Index > &mask=true)
Non-packet look up.
Definition: control.h:61
NEWTYPE cast(OLDTYPE &&val)
cast
Definition: control.h:27
auto select(MASK &&mask, A &&a, B &&b)
Extension of drjit::select to diff/non-diff masks.
Definition: control.h:47
size_t size(T &&t)
Definition: iterator_util.h:22
std::decay_t< decltype(*bbm::begin(std::declval< T >()))> iterable_value_t
Definition: iterator_util.h:61
auto begin(T &&t)
Definition: iterator_util.h:29