1 #ifndef _TCUINTERVAL_HPP
2 #define _TCUINTERVAL_HPP
3 /*-------------------------------------------------------------------------
4  * drawElements Quality Program Tester Core
5  * ----------------------------------------
6  *
7  * Copyright 2014 The Android Open Source Project
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  *//*!
22  * \file
23  * \brief Interval arithmetic and floating point precisions.
24  *//*--------------------------------------------------------------------*/
25 
26 #include "tcuDefs.hpp"
27 
28 #include "deMath.h"
29 
30 #include <iostream>
31 #include <limits>
32 
33 #define TCU_INFINITY	(::std::numeric_limits<float>::infinity())
34 #define TCU_NAN			(::std::numeric_limits<float>::quiet_NaN())
35 
36 namespace tcu
37 {
38 
39 // RAII context for temporarily changing the rounding mode
40 class ScopedRoundingMode
41 {
42 public:
ScopedRoundingMode(deRoundingMode mode)43 							ScopedRoundingMode	(deRoundingMode mode)
44 								: m_oldMode (deGetRoundingMode()) { deSetRoundingMode(mode); }
45 
ScopedRoundingMode(void)46 							ScopedRoundingMode	(void) : m_oldMode (deGetRoundingMode()) {}
47 
~ScopedRoundingMode(void)48 							~ScopedRoundingMode	(void)	{ deSetRoundingMode(m_oldMode); }
49 
50 private:
51 							ScopedRoundingMode	(const ScopedRoundingMode&);
52 	ScopedRoundingMode&		operator=			(const ScopedRoundingMode&);
53 
54 	const deRoundingMode	m_oldMode;
55 };
56 
57 class Interval
58 {
59 public:
60 				// Empty interval.
Interval(void)61 				Interval		(void)
62 					: m_hasNaN	(false)
63 					, m_lo		(TCU_INFINITY)
64 					, m_hi		(-TCU_INFINITY) {}
65 
66 				// Intentionally not explicit. Conversion from double to Interval is common
67 				// and reasonable.
Interval(double val)68 				Interval		(double val)
69 					: m_hasNaN	(!!deIsNaN(val))
70 					, m_lo		(m_hasNaN ? TCU_INFINITY : val)
71 					, m_hi		(m_hasNaN ? -TCU_INFINITY : val) {}
72 
Interval(const Interval & a,const Interval & b)73 				Interval		(const Interval& a, const Interval& b)
74 					: m_hasNaN	(a.m_hasNaN || b.m_hasNaN)
75 					, m_lo		(de::min(a.lo(), b.lo()))
76 					, m_hi		(de::max(a.hi(), b.hi())) {}
77 
length(void) const78 	double		length			(void) const { return m_hi - m_lo; }
lo(void) const79 	double		lo				(void) const { return m_lo; }
hi(void) const80 	double		hi				(void) const { return m_hi; }
hasNaN(void) const81 	bool		hasNaN			(void) const { return m_hasNaN; }
nan(void) const82 	Interval	nan				(void) const { return m_hasNaN ? TCU_NAN : Interval(); }
empty(void) const83 	bool		empty			(void) const { return m_lo > m_hi; }
isFinite(void) const84 	bool		isFinite		(void) const { return m_lo > -TCU_INFINITY && m_hi < TCU_INFINITY; }
isOrdinary(void) const85 	bool		isOrdinary		(void) const { return !hasNaN() && !empty() && isFinite(); }
86 
87 
operator |(const Interval & other) const88 	Interval	operator|		(const Interval& other) const
89 	{
90 		return Interval(m_hasNaN || other.m_hasNaN,
91 						de::min(m_lo, other.m_lo),
92 						de::max(m_hi, other.m_hi));
93 
94 	}
95 
operator |=(const Interval & other)96 	Interval&	operator|=		(const Interval& other)
97 	{
98 		return (*this = *this | other);
99 	}
100 
operator &(const Interval & other) const101 	Interval	operator&		(const Interval& other) const
102 	{
103 		return Interval(m_hasNaN && other.m_hasNaN,
104 						de::max(m_lo, other.m_lo),
105 						de::min(m_hi, other.m_hi));
106 	}
107 
operator &=(const Interval & other)108 	Interval&	operator&=		(const Interval& other)
109 	{
110 		return (*this = *this & other);
111 	}
112 
contains(const Interval & other) const113 	bool		contains		(const Interval& other) const
114 	{
115 		return (other.lo() >= lo() && other.hi() <= hi() &&
116 				(!other.hasNaN() || hasNaN()));
117 	}
118 
intersects(const Interval & other) const119 	bool		intersects		(const Interval& other) const
120 	{
121 		return ((other.hi() >= lo() && other.lo() <= hi()) ||
122 				(other.hasNaN() && hasNaN()));
123 	}
124 
operator -(void) const125 	Interval	operator-		(void) const
126 	{
127 		return Interval(hasNaN(), -hi(), -lo());
128 	}
129 
unbounded(bool nan=false)130 	static Interval	unbounded	(bool nan = false)
131 	{
132 		return Interval(nan, -TCU_INFINITY, TCU_INFINITY);
133 	}
134 
midpoint(void) const135 	double		midpoint		(void) const
136 	{
137 		return 0.5 * (hi() + lo()); // returns NaN when not bounded
138 	}
139 
operator ==(const Interval & other) const140 	bool		operator==		(const Interval& other) const
141 	{
142 		return ((m_hasNaN == other.m_hasNaN) &&
143 				((empty() && other.empty()) ||
144 				 (m_lo == other.m_lo && m_hi == other.m_hi)));
145 	}
146 
147 private:
Interval(bool hasNaN_,double lo_,double hi_)148 				Interval		(bool hasNaN_, double lo_, double hi_)
149 					: m_hasNaN(hasNaN_), m_lo(lo_), m_hi(hi_) {}
150 	bool		m_hasNaN;
151 	double		m_lo;
152 	double		m_hi;
153 } DE_WARN_UNUSED_TYPE;
154 
operator +(const Interval & x)155 inline Interval	operator+	(const Interval& x) { return x; }
156 Interval		exp2		(const Interval& x);
157 Interval		exp			(const Interval& x);
158 int				sign		(const Interval& x);
159 Interval		abs			(const Interval& x);
160 Interval		inverseSqrt	(const Interval& x);
161 
162 Interval		operator+	(const Interval& x,		const Interval& y);
163 Interval		operator-	(const Interval& x,		const Interval& y);
164 Interval		operator*	(const Interval& x,		const Interval& y);
165 Interval		operator/	(const Interval& nom,	const Interval& den);
166 
operator +=(Interval & x,const Interval & y)167 inline Interval& operator+=	(Interval& x,	const Interval& y) { return (x = x + y); }
operator -=(Interval & x,const Interval & y)168 inline Interval& operator-=	(Interval& x,	const Interval& y) { return (x = x - y); }
operator *=(Interval & x,const Interval & y)169 inline Interval& operator*=	(Interval& x,	const Interval& y) { return (x = x * y); }
operator /=(Interval & x,const Interval & y)170 inline Interval& operator/=	(Interval& x,	const Interval& y) { return (x = x / y); }
171 
172 std::ostream&	operator<<	(std::ostream& os, const Interval& interval);
173 
174 #define TCU_SET_INTERVAL_BOUNDS(DST, VAR, SETLOW, SETHIGH) do	\
175 {																\
176 	::tcu::ScopedRoundingMode	VAR##_ctx_;						\
177 	::tcu::Interval&			VAR##_dst_	= (DST);			\
178 	::tcu::Interval				VAR##_lo_;						\
179 	::tcu::Interval				VAR##_hi_;						\
180 																\
181 	{															\
182 		::tcu::Interval&	VAR	= VAR##_lo_;					\
183 		::deSetRoundingMode(DE_ROUNDINGMODE_TO_NEGATIVE_INF);	\
184 		SETLOW;													\
185 	}															\
186 	{															\
187 		::tcu::Interval&	VAR	= VAR##_hi_;					\
188 		::deSetRoundingMode(DE_ROUNDINGMODE_TO_POSITIVE_INF);	\
189 		SETHIGH;												\
190 	}															\
191 																\
192 	VAR##_dst_ = VAR##_lo_ | VAR##_hi_;							\
193 } while (::deGetFalse())
194 
195 #define TCU_SET_INTERVAL(DST, VAR, BODY)						\
196 	TCU_SET_INTERVAL_BOUNDS(DST, VAR, BODY, BODY)
197 
198 //! Set the interval DST to the image of BODY on ARG, assuming that BODY on
199 //! ARG is a monotone function. In practice, BODY is evaluated on both the
200 //! upper and lower bound of ARG, and DST is set to the union of these
201 //! results. While evaluating BODY, PARAM is bound to the bound of ARG, and
202 //! the output of BODY should be stored in VAR.
203 #define TCU_INTERVAL_APPLY_MONOTONE1(DST, PARAM, ARG, VAR, BODY) do		\
204 	{																	\
205 	const ::tcu::Interval&	VAR##_arg_		= (ARG);					\
206 	::tcu::Interval&		VAR##_dst_		= (DST);					\
207 	::tcu::Interval			VAR##_lo_;									\
208 	::tcu::Interval			VAR##_hi_;									\
209 	if (VAR##_arg_.empty())												\
210 		VAR##_dst_ = Interval();										\
211 	else																\
212 	{																	\
213 		{																\
214 			const double		PARAM	= VAR##_arg_.lo();				\
215 			::tcu::Interval&	VAR		= VAR##_lo_;					\
216 			BODY;														\
217 		}																\
218 		{																\
219 			const double		PARAM	= VAR##_arg_.hi();				\
220 			::tcu::Interval&	VAR		= VAR##_hi_;					\
221 			BODY;														\
222 		}																\
223 		VAR##_dst_ = VAR##_lo_ | VAR##_hi_;								\
224 	}																	\
225 	if (VAR##_arg_.hasNaN())											\
226 		VAR##_dst_ |= TCU_NAN;											\
227 } while (::deGetFalse())
228 
229 #define TCU_INTERVAL_APPLY_MONOTONE2(DST, P0, A0, P1, A1, VAR, BODY)	\
230 	TCU_INTERVAL_APPLY_MONOTONE1(										\
231 		DST, P0, A0, tmp2_,												\
232 		TCU_INTERVAL_APPLY_MONOTONE1(tmp2_, P1, A1, VAR, BODY))
233 
234 #define TCU_INTERVAL_APPLY_MONOTONE3(DST, P0, A0, P1, A1, P2, A2, VAR, BODY) \
235 	TCU_INTERVAL_APPLY_MONOTONE1(										\
236 		DST, P0, A0, tmp3_,												\
237 		TCU_INTERVAL_APPLY_MONOTONE2(tmp3_, P1, A1, P2, A2, VAR, BODY))
238 
239 typedef double		DoubleFunc1			(double);
240 typedef double		DoubleFunc2			(double, double);
241 typedef double		DoubleFunc3			(double, double, double);
242 typedef Interval	DoubleIntervalFunc1	(double);
243 typedef Interval	DoubleIntervalFunc2	(double, double);
244 typedef Interval	DoubleIntervalFunc3	(double, double, double);
245 
246 Interval	applyMonotone	(DoubleFunc1&			func,
247 							 const Interval&		arg0);
248 Interval	applyMonotone	(DoubleFunc2&			func,
249 							 const Interval&		arg0,
250 							 const Interval&		arg1);
251 Interval	applyMonotone	(DoubleIntervalFunc1&	func,
252 							 const Interval&		arg0);
253 Interval	applyMonotone	(DoubleIntervalFunc2&	func,
254 							 const Interval&		arg0,
255 							 const Interval&		arg1);
256 
257 
258 } // tcu
259 
260 #endif // _TCUINTERVAL_HPP
261