Home | History | Annotate | Download | only in common
      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:
     43 							ScopedRoundingMode	(deRoundingMode mode)
     44 								: m_oldMode (deGetRoundingMode()) { deSetRoundingMode(mode); }
     45 
     46 							ScopedRoundingMode	(void) : m_oldMode (deGetRoundingMode()) {}
     47 
     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.
     61 				Interval			(void)
     62 					: m_hasNaN		(false)
     63 					, m_lo			(TCU_INFINITY)
     64 					, m_hi			(-TCU_INFINITY)
     65 					, m_warningLo	(-TCU_INFINITY)
     66 					, m_warningHi	(TCU_INFINITY) {}
     67 
     68 				// Intentionally not explicit. Conversion from double to Interval is common
     69 				// and reasonable.
     70 				Interval			(double val)
     71 					: m_hasNaN		(!!deIsNaN(val))
     72 					, m_lo			(m_hasNaN ? TCU_INFINITY : val)
     73 					, m_hi			(m_hasNaN ? -TCU_INFINITY : val)
     74 					, m_warningLo	(-TCU_INFINITY)
     75 					, m_warningHi	(TCU_INFINITY) {}
     76 
     77 
     78 				Interval(bool hasNaN_, double lo_, double hi_)
     79 					: m_hasNaN(hasNaN_), m_lo(lo_), m_hi(hi_), m_warningLo(-TCU_INFINITY), m_warningHi(TCU_INFINITY) {}
     80 
     81 				Interval(bool hasNaN_, double lo_, double hi_, double wlo_, double whi_)
     82 					: m_hasNaN(hasNaN_), m_lo(lo_), m_hi(hi_), m_warningLo(wlo_), m_warningHi(whi_) {}
     83 
     84 				Interval		(const Interval& a, const Interval& b)
     85 					: m_hasNaN		(a.m_hasNaN || b.m_hasNaN)
     86 					, m_lo			(de::min(a.lo(), b.lo()))
     87 					, m_hi			(de::max(a.hi(), b.hi()))
     88 					, m_warningLo	(de::min(a.warningLo(), b.warningLo()))
     89 					, m_warningHi	(de::max(a.warningHi(), b.warningHi())) {}
     90 
     91 	double		length			(void) const { return m_hi - m_lo; }
     92 	double		lo				(void) const { return m_lo; }
     93 	double		hi				(void) const { return m_hi; }
     94 	double		warningLo		(void) const { return m_warningLo; }
     95 	double		warningHi		(void) const { return m_warningHi; }
     96 	bool		hasNaN			(void) const { return m_hasNaN; }
     97 	Interval	nan				(void) const { return m_hasNaN ? TCU_NAN : Interval(); }
     98 	bool		empty			(void) const { return m_lo > m_hi; }
     99 	bool		isFinite		(void) const { return m_lo > -TCU_INFINITY && m_hi < TCU_INFINITY; }
    100 	bool		isOrdinary		(void) const { return !hasNaN() && !empty() && isFinite(); }
    101 
    102 	void		warning			(double lo_, double hi_)
    103 	{
    104 		m_warningLo = lo_;
    105 		m_warningHi = hi_;
    106 	}
    107 
    108 	Interval	operator|		(const Interval& other) const
    109 	{
    110 		return Interval(m_hasNaN || other.m_hasNaN,
    111 						de::min(m_lo, other.m_lo),
    112 						de::max(m_hi, other.m_hi),
    113 						de::min(m_warningLo, other.m_warningLo),
    114 						de::max(m_warningHi, other.m_warningHi));
    115 	}
    116 
    117 	Interval&	operator|=		(const Interval& other)
    118 	{
    119 		return (*this = *this | other);
    120 	}
    121 
    122 	Interval	operator&		(const Interval& other) const
    123 	{
    124 		return Interval(m_hasNaN && other.m_hasNaN,
    125 						de::max(m_lo, other.m_lo),
    126 						de::min(m_hi, other.m_hi),
    127 						de::max(m_warningLo, other.m_warningLo),
    128 						de::min(m_warningHi, other.m_warningHi));
    129 	}
    130 
    131 	Interval&	operator&=		(const Interval& other)
    132 	{
    133 		return (*this = *this & other);
    134 	}
    135 
    136 	bool		contains		(const Interval& other) const
    137 	{
    138 		return (other.lo() >= lo() && other.hi() <= hi() &&
    139 				(!other.hasNaN() || hasNaN()));
    140 	}
    141 
    142 	bool		containsWarning(const Interval& other) const
    143 	{
    144 		return (other.lo() >= warningLo() && other.hi() <= warningHi() &&
    145 			(!other.hasNaN() || hasNaN()));
    146 	}
    147 
    148 	bool		intersects		(const Interval& other) const
    149 	{
    150 		return ((other.hi() >= lo() && other.lo() <= hi()) ||
    151 				(other.hasNaN() && hasNaN()));
    152 	}
    153 
    154 	Interval	operator-		(void) const
    155 	{
    156 		return Interval(hasNaN(), -hi(), -lo(), -warningHi(), -warningLo());
    157 	}
    158 
    159 	static Interval	unbounded	(bool nan = false)
    160 	{
    161 		return Interval(nan, -TCU_INFINITY, TCU_INFINITY);
    162 	}
    163 
    164 	double		midpoint		(void) const
    165 	{
    166 		return 0.5 * (hi() + lo()); // returns NaN when not bounded
    167 	}
    168 
    169 	bool		operator==		(const Interval& other) const
    170 	{
    171 		return ((m_hasNaN == other.m_hasNaN) &&
    172 				((empty() && other.empty()) ||
    173 				 (m_lo == other.m_lo && m_hi == other.m_hi)));
    174 	}
    175 
    176 private:
    177 	bool		m_hasNaN;
    178 	double		m_lo;
    179 	double		m_hi;
    180 	double		m_warningLo;
    181 	double		m_warningHi;
    182 } DE_WARN_UNUSED_TYPE;
    183 
    184 inline Interval	operator+	(const Interval& x) { return x; }
    185 Interval		exp2		(const Interval& x);
    186 Interval		exp			(const Interval& x);
    187 int				sign		(const Interval& x);
    188 Interval		abs			(const Interval& x);
    189 Interval		inverseSqrt	(const Interval& x);
    190 
    191 Interval		operator+	(const Interval& x,		const Interval& y);
    192 Interval		operator-	(const Interval& x,		const Interval& y);
    193 Interval		operator*	(const Interval& x,		const Interval& y);
    194 Interval		operator/	(const Interval& nom,	const Interval& den);
    195 
    196 inline Interval& operator+=	(Interval& x,	const Interval& y) { return (x = x + y); }
    197 inline Interval& operator-=	(Interval& x,	const Interval& y) { return (x = x - y); }
    198 inline Interval& operator*=	(Interval& x,	const Interval& y) { return (x = x * y); }
    199 inline Interval& operator/=	(Interval& x,	const Interval& y) { return (x = x / y); }
    200 
    201 std::ostream&	operator<<	(std::ostream& os, const Interval& interval);
    202 
    203 #define TCU_SET_INTERVAL_BOUNDS(DST, VAR, SETLOW, SETHIGH) do	\
    204 {																\
    205 	::tcu::ScopedRoundingMode	VAR##_ctx_;						\
    206 	::tcu::Interval&			VAR##_dst_	= (DST);			\
    207 	::tcu::Interval				VAR##_lo_;						\
    208 	::tcu::Interval				VAR##_hi_;						\
    209 																\
    210 	{															\
    211 		::tcu::Interval&	(VAR) = VAR##_lo_;					\
    212 		::deSetRoundingMode(DE_ROUNDINGMODE_TO_NEGATIVE_INF);	\
    213 		SETLOW;													\
    214 	}															\
    215 	{															\
    216 		::tcu::Interval&	(VAR) = VAR##_hi_;					\
    217 		::deSetRoundingMode(DE_ROUNDINGMODE_TO_POSITIVE_INF);	\
    218 		SETHIGH;												\
    219 	}															\
    220 																\
    221 	VAR##_dst_ = VAR##_lo_ | VAR##_hi_;							\
    222 } while (::deGetFalse())
    223 
    224 #define TCU_SET_INTERVAL(DST, VAR, BODY)						\
    225 	TCU_SET_INTERVAL_BOUNDS(DST, VAR, BODY, BODY)
    226 
    227 //! Set the interval DST to the image of BODY on ARG, assuming that BODY on
    228 //! ARG is a monotone function. In practice, BODY is evaluated on both the
    229 //! upper and lower bound of ARG, and DST is set to the union of these
    230 //! results. While evaluating BODY, PARAM is bound to the bound of ARG, and
    231 //! the output of BODY should be stored in VAR.
    232 #define TCU_INTERVAL_APPLY_MONOTONE1(DST, PARAM, ARG, VAR, BODY) do		\
    233 	{																	\
    234 	const ::tcu::Interval&	VAR##_arg_		= (ARG);					\
    235 	::tcu::Interval&		VAR##_dst_		= (DST);					\
    236 	::tcu::Interval			VAR##_lo_;									\
    237 	::tcu::Interval			VAR##_hi_;									\
    238 	if (VAR##_arg_.empty())												\
    239 		VAR##_dst_ = Interval();										\
    240 	else																\
    241 	{																	\
    242 		{																\
    243 			const double		(PARAM)	= VAR##_arg_.lo();				\
    244 			::tcu::Interval&	(VAR)	= VAR##_lo_;					\
    245 			BODY;														\
    246 		}																\
    247 		{																\
    248 			const double		(PARAM)	= VAR##_arg_.hi();				\
    249 			::tcu::Interval&	(VAR)	= VAR##_hi_;					\
    250 			BODY;														\
    251 		}																\
    252 		VAR##_dst_ = VAR##_lo_ | VAR##_hi_;								\
    253 	}																	\
    254 	if (VAR##_arg_.hasNaN())											\
    255 		VAR##_dst_ |= TCU_NAN;											\
    256 } while (::deGetFalse())
    257 
    258 #define TCU_INTERVAL_APPLY_MONOTONE2(DST, P0, A0, P1, A1, VAR, BODY)	\
    259 	TCU_INTERVAL_APPLY_MONOTONE1(										\
    260 		DST, P0, A0, tmp2_,												\
    261 		TCU_INTERVAL_APPLY_MONOTONE1(tmp2_, P1, A1, VAR, BODY))
    262 
    263 #define TCU_INTERVAL_APPLY_MONOTONE3(DST, P0, A0, P1, A1, P2, A2, VAR, BODY) \
    264 	TCU_INTERVAL_APPLY_MONOTONE1(										\
    265 		DST, P0, A0, tmp3_,												\
    266 		TCU_INTERVAL_APPLY_MONOTONE2(tmp3_, P1, A1, P2, A2, VAR, BODY))
    267 
    268 typedef double		DoubleFunc1			(double);
    269 typedef double		DoubleFunc2			(double, double);
    270 typedef double		DoubleFunc3			(double, double, double);
    271 typedef Interval	DoubleIntervalFunc1	(double);
    272 typedef Interval	DoubleIntervalFunc2	(double, double);
    273 typedef Interval	DoubleIntervalFunc3	(double, double, double);
    274 
    275 Interval	applyMonotone	(DoubleFunc1&			func,
    276 							 const Interval&		arg0);
    277 Interval	applyMonotone	(DoubleFunc2&			func,
    278 							 const Interval&		arg0,
    279 							 const Interval&		arg1);
    280 Interval	applyMonotone	(DoubleIntervalFunc1&	func,
    281 							 const Interval&		arg0);
    282 Interval	applyMonotone	(DoubleIntervalFunc2&	func,
    283 							 const Interval&		arg0,
    284 							 const Interval&		arg1);
    285 
    286 
    287 } // tcu
    288 
    289 #endif // _TCUINTERVAL_HPP
    290