1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Random Shader Generator
3 * ----------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Binary ops.
22 *//*--------------------------------------------------------------------*/
23
24 #include "rsgBinaryOps.hpp"
25 #include "rsgVariableManager.hpp"
26 #include "rsgUtils.hpp"
27 #include "deMath.h"
28
29 using std::vector;
30
31 namespace rsg
32 {
33
34 // CustomAbsOp and CustomBinaryOp are used to resolve float comparision corner case.
35 // This error happened when two floats with the same value were compared
36 // without using epsilon. If result of this comparisment influenced the
37 // output color then result and reference images could differ.
38 class CustomAbsOp : public Expression
39 {
40 public:
41 CustomAbsOp (void);
42 virtual ~CustomAbsOp (void);
43
44 void setChild (Expression* expression);
45 Expression* createNextChild (GeneratorState& state);
46 void tokenize (GeneratorState& state, TokenStream& str) const;
47
48 void evaluate (ExecutionContext& execCtx);
getValue(void) const49 ExecConstValueAccess getValue (void) const { return m_value.getValue(m_type); }
50
51 private:
52 std::string m_function;
53 VariableType m_type;
54 ExecValueStorage m_value;
55 Expression* m_child;
56 };
57
CustomAbsOp(void)58 CustomAbsOp::CustomAbsOp (void)
59 : m_function ("abs")
60 , m_type (VariableType::TYPE_FLOAT, 1)
61 , m_child (DE_NULL)
62 {
63 m_value.setStorage(m_type);
64 }
65
~CustomAbsOp(void)66 CustomAbsOp::~CustomAbsOp (void)
67 {
68 delete m_child;
69 }
70
setChild(Expression * expression)71 void CustomAbsOp::setChild(Expression* expression)
72 {
73 m_child = expression;
74 }
75
createNextChild(GeneratorState &)76 Expression* CustomAbsOp::createNextChild (GeneratorState&)
77 {
78 DE_ASSERT(0);
79 return DE_NULL;
80 }
81
tokenize(GeneratorState & state,TokenStream & str) const82 void CustomAbsOp::tokenize (GeneratorState& state, TokenStream& str) const
83 {
84 str << Token(m_function.c_str()) << Token::LEFT_PAREN;
85 m_child->tokenize(state, str);
86 str << Token::RIGHT_PAREN;
87 }
88
evaluate(ExecutionContext & execCtx)89 void CustomAbsOp::evaluate (ExecutionContext& execCtx)
90 {
91 m_child->evaluate(execCtx);
92
93 ExecConstValueAccess srcValue = m_child->getValue();
94 ExecValueAccess dstValue = m_value.getValue(m_type);
95
96 for (int elemNdx = 0; elemNdx < m_type.getNumElements(); elemNdx++)
97 {
98 ExecConstValueAccess srcComp = srcValue.component(elemNdx);
99 ExecValueAccess dstComp = dstValue.component(elemNdx);
100
101 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
102 dstComp.asFloat(compNdx) = deFloatAbs(srcComp.asFloat(compNdx));
103 }
104 }
105
106 typedef BinaryOp<5, ASSOCIATIVITY_LEFT> CustomBinaryBase;
107
108 // CustomBinaryOp and CustomAbsOp are used to resolve float comparision corner case.
109 // CustomBinaryOp supports addition and substraction as only those functionalities
110 // were needed.
111 template <typename ComputeValue>
112 class CustomBinaryOp: public CustomBinaryBase
113 {
114 public:
115 CustomBinaryOp ();
~CustomBinaryOp(void)116 virtual ~CustomBinaryOp (void) {}
117
118 void setLeftValue (Expression* expression);
119 void setRightValue (Expression* expression);
120
121 void evaluate (ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b);
122 };
123
124 template <typename ComputeValue>
CustomBinaryOp()125 CustomBinaryOp<ComputeValue>::CustomBinaryOp ()
126 : CustomBinaryBase(Token::PLUS)
127 {
128 // By default add operation is assumed, for every other operation
129 // separate constructor specialization should be implemented
130 m_type = VariableType(VariableType::TYPE_FLOAT, 1);
131 m_value.setStorage(m_type);
132 }
133
134 template <>
CustomBinaryOp()135 CustomBinaryOp<EvaluateSub>::CustomBinaryOp ()
136 : CustomBinaryBase(Token::MINUS)
137 {
138 // Specialization for substraction
139 m_type = VariableType(VariableType::TYPE_FLOAT, 1);
140 m_leftValueRange = ValueRange(m_type);
141 m_rightValueRange = ValueRange(m_type);
142 m_value.setStorage(m_type);
143 }
144
145 template <>
CustomBinaryOp()146 CustomBinaryOp<EvaluateLessThan>::CustomBinaryOp ()
147 : CustomBinaryBase(Token::CMP_LT)
148 {
149 // Specialization for less_then comparision
150 m_type = VariableType(VariableType::TYPE_BOOL, 1);
151 VariableType floatType = VariableType(VariableType::TYPE_FLOAT, 1);
152 m_leftValueRange = ValueRange(floatType);
153 m_rightValueRange = ValueRange(floatType);
154 m_value.setStorage(m_type);
155 }
156
157 template <typename ComputeValue>
setLeftValue(Expression * expression)158 void CustomBinaryOp<ComputeValue>::setLeftValue(Expression* expression)
159 {
160 m_leftValueExpr = expression;
161 }
162
163 template <typename ComputeValue>
setRightValue(Expression * expression)164 void CustomBinaryOp<ComputeValue>::setRightValue(Expression* expression)
165 {
166 m_rightValueExpr = expression;
167 }
168
169 template <typename ComputeValue>
evaluate(ExecValueAccess dst,ExecConstValueAccess a,ExecConstValueAccess b)170 void CustomBinaryOp<ComputeValue>::evaluate(ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
171 {
172 DE_ASSERT(dst.getType() == a.getType());
173 DE_ASSERT(dst.getType() == b.getType());
174 DE_ASSERT(dst.getType().getBaseType() == VariableType::TYPE_FLOAT);
175
176 for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
177 {
178 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
179 dst.component(elemNdx).asFloat(compNdx) = ComputeValue()(a.component(elemNdx).asFloat(compNdx),b.component(elemNdx).asFloat(compNdx));
180 }
181 }
182
183 template <>
evaluate(ExecValueAccess dst,ExecConstValueAccess a,ExecConstValueAccess b)184 void CustomBinaryOp<EvaluateLessThan>::evaluate(ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
185 {
186 DE_ASSERT(a.getType() == b.getType());
187 DE_ASSERT(dst.getType().getBaseType() == VariableType::TYPE_BOOL);
188
189 for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
190 {
191 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
192 dst.component(elemNdx).asBool(compNdx) = EvaluateLessThan()(a.component(elemNdx).asFloat(compNdx),b.component(elemNdx).asFloat(compNdx));
193 }
194 }
195
196 template <int Precedence, Associativity Assoc>
BinaryOp(Token::Type operatorToken)197 BinaryOp<Precedence, Assoc>::BinaryOp (Token::Type operatorToken)
198 : m_operator (operatorToken)
199 , m_leftValueRange (m_type)
200 , m_rightValueRange (m_type)
201 , m_leftValueExpr (DE_NULL)
202 , m_rightValueExpr (DE_NULL)
203 {
204 }
205
206 template <int Precedence, Associativity Assoc>
~BinaryOp(void)207 BinaryOp<Precedence, Assoc>::~BinaryOp (void)
208 {
209 delete m_leftValueExpr;
210 delete m_rightValueExpr;
211 }
212
213 template <int Precedence, Associativity Assoc>
createNextChild(GeneratorState & state)214 Expression* BinaryOp<Precedence, Assoc>::createNextChild (GeneratorState& state)
215 {
216 int leftPrec = Assoc == ASSOCIATIVITY_LEFT ? Precedence : Precedence-1;
217 int rightPrec = Assoc == ASSOCIATIVITY_LEFT ? Precedence-1 : Precedence;
218
219 if (m_rightValueExpr == DE_NULL)
220 {
221 state.pushPrecedence(rightPrec);
222 m_rightValueExpr = Expression::createRandom(state, m_rightValueRange.asAccess());
223 state.popPrecedence();
224 return m_rightValueExpr;
225 }
226 else if (m_leftValueExpr == DE_NULL)
227 {
228 state.pushPrecedence(leftPrec);
229 m_leftValueExpr = Expression::createRandom(state, m_leftValueRange.asAccess());
230 state.popPrecedence();
231 return m_leftValueExpr;
232 }
233 else
234 {
235 // Check for corrner cases
236 switch (m_operator)
237 {
238 case Token::CMP_LE:
239 {
240 // When comparing two floats epsilon should be included
241 // to eliminate the risk that we get different results
242 // because of precission error
243 VariableType floatType(VariableType::TYPE_FLOAT, 1);
244 if (m_rightValueRange.getType() == floatType)
245 {
246 FloatLiteral* epsilonLiteral = new FloatLiteral(0.001f);
247
248 typedef CustomBinaryOp<EvaluateAdd> CustomAddOp;
249 CustomAddOp* addOperation = new CustomAddOp();
250 addOperation->setLeftValue(m_rightValueExpr);
251 addOperation->setRightValue(epsilonLiteral);
252
253 // add epsilon to right-hand side
254 m_rightValueExpr = addOperation;
255 }
256 break;
257 }
258 case Token::CMP_GE:
259 {
260 // When comparing two floats epsilon should be included
261 // to eliminate the risk that we get different results
262 // because of precission error
263 VariableType floatType(VariableType::TYPE_FLOAT, 1);
264 if (m_leftValueRange.getType() == floatType)
265 {
266 FloatLiteral* epsilonLiteral = new FloatLiteral(0.001f);
267
268 typedef CustomBinaryOp<EvaluateAdd> CustomAddOp;
269 CustomAddOp* addOperation = new CustomAddOp();
270 addOperation->setLeftValue(m_leftValueExpr);
271 addOperation->setRightValue(epsilonLiteral);
272
273 // add epsilon to left-hand side
274 m_leftValueExpr = addOperation;
275 }
276 break;
277 }
278 case Token::CMP_EQ:
279 {
280 // When comparing two floats epsilon should be included
281 // to eliminate the risk that we get different results
282 // because of precission error
283 VariableType floatType(VariableType::TYPE_FLOAT, 1);
284 if (m_leftValueRange.getType() == floatType)
285 {
286 VariableType boolType(VariableType::TYPE_BOOL, 1);
287 const ValueRange boolRange(boolType);
288
289 ParenOp* parenRight = new ParenOp(state, boolRange);
290 parenRight->setChild(m_rightValueExpr);
291
292 typedef CustomBinaryOp<EvaluateSub> CustomSubOp;
293 CustomSubOp* subOperation = new CustomSubOp();
294 subOperation->setLeftValue(m_leftValueExpr);
295 subOperation->setRightValue(parenRight);
296
297 CustomAbsOp* absOperation = new CustomAbsOp();
298 absOperation->setChild(subOperation);
299 FloatLiteral* epsilonLiteral = new FloatLiteral(0.001f);
300
301 typedef CustomBinaryOp<EvaluateLessThan> CustomLessThanOp;
302 CustomLessThanOp* lessOperation = new CustomLessThanOp();
303 lessOperation->setLeftValue(absOperation);
304 lessOperation->setRightValue(epsilonLiteral);
305
306 ParenOp* parenOperation = new ParenOp(state, boolRange);
307 parenOperation->setChild(lessOperation);
308 BoolLiteral* trueLiteral = new BoolLiteral(true);
309
310 // EQ operation cant be removed so it is replaced with:
311 // ((abs(lhs-rhs) < epsilon) == true).
312 m_leftValueExpr = parenOperation;
313 m_rightValueExpr = trueLiteral;
314 }
315 break;
316 }
317 default:
318 break;
319 }
320
321 return DE_NULL;
322 }
323 }
324
325 template <int Precedence, Associativity Assoc>
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)326 float BinaryOp<Precedence, Assoc>::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
327 {
328 if (state.getPrecedence() < Precedence)
329 return 0.0f;
330
331 int availableLevels = state.getShaderParameters().maxExpressionDepth - state.getExpressionDepth();
332
333 if (valueRange.getType().isVoid())
334 return availableLevels >= 2 ? unusedValueWeight : 0.0f;
335
336 if (availableLevels < getConservativeValueExprDepth(state, valueRange) + 1)
337 return 0.0f;
338
339 return 1.0f;
340 }
341
342 template <int Precedence, Associativity Assoc>
tokenize(GeneratorState & state,TokenStream & str) const343 void BinaryOp<Precedence, Assoc>::tokenize (GeneratorState& state, TokenStream& str) const
344 {
345 m_leftValueExpr->tokenize(state, str);
346 str << m_operator;
347 m_rightValueExpr->tokenize(state, str);
348 }
349
350 template <int Precedence, Associativity Assoc>
evaluate(ExecutionContext & execCtx)351 void BinaryOp<Precedence, Assoc>::evaluate (ExecutionContext& execCtx)
352 {
353 m_leftValueExpr->evaluate(execCtx);
354 m_rightValueExpr->evaluate(execCtx);
355
356 ExecConstValueAccess leftVal = m_leftValueExpr->getValue();
357 ExecConstValueAccess rightVal = m_rightValueExpr->getValue();
358 ExecValueAccess dst = m_value.getValue(m_type);
359
360 evaluate(dst, leftVal, rightVal);
361 }
362
363 template <int Precedence, bool Float, bool Int, bool Bool, class ComputeValueRange, class EvaluateComp>
BinaryVecOp(GeneratorState & state,Token::Type operatorToken,ConstValueRangeAccess inValueRange)364 BinaryVecOp<Precedence, Float, Int, Bool, ComputeValueRange, EvaluateComp>::BinaryVecOp (GeneratorState& state, Token::Type operatorToken, ConstValueRangeAccess inValueRange)
365 : BinaryOp<Precedence, ASSOCIATIVITY_LEFT>(operatorToken)
366 {
367 ValueRange valueRange = inValueRange;
368
369 if (valueRange.getType().isVoid())
370 {
371 int availableLevels = state.getShaderParameters().maxExpressionDepth - state.getExpressionDepth();
372 vector<VariableType::Type> baseTypes;
373
374 if (Float) baseTypes.push_back(VariableType::TYPE_FLOAT);
375 if (Int) baseTypes.push_back(VariableType::TYPE_INT);
376 if (Bool) baseTypes.push_back(VariableType::TYPE_BOOL);
377
378 VariableType::Type baseType = state.getRandom().choose<VariableType::Type>(baseTypes.begin(), baseTypes.end());
379 int numElements = state.getRandom().getInt(1, availableLevels >= 3 ? 4 : 1);
380
381 valueRange = ValueRange(VariableType(baseType, numElements));
382 computeRandomValueRange(state, valueRange.asAccess());
383 }
384
385 // Choose type, allocate storage for execution
386 this->m_type = valueRange.getType();
387 this->m_value.setStorage(this->m_type);
388
389 // Initialize storage for value ranges
390 this->m_rightValueRange = ValueRange(this->m_type);
391 this->m_leftValueRange = ValueRange(this->m_type);
392
393 VariableType::Type baseType = this->m_type.getBaseType();
394
395 // Compute range for b that satisfies requested value range
396 for (int elemNdx = 0; elemNdx < this->m_type.getNumElements(); elemNdx++)
397 {
398 ConstValueRangeAccess dst = valueRange.asAccess().component(elemNdx);
399 ValueRangeAccess a = this->m_leftValueRange.asAccess().component(elemNdx); // \todo [2011-03-25 pyry] Commutative: randomize inputs
400 ValueRangeAccess b = this->m_rightValueRange.asAccess().component(elemNdx);
401
402 // Just pass undefined ranges
403 if ((baseType == VariableType::TYPE_FLOAT || baseType == VariableType::TYPE_INT) && isUndefinedValueRange(dst))
404 {
405 a.getMin() = dst.getMin().value();
406 b.getMin() = dst.getMin().value();
407 a.getMax() = dst.getMax().value();
408 b.getMax() = dst.getMax().value();
409 continue;
410 }
411
412 if (baseType == VariableType::TYPE_FLOAT)
413 ComputeValueRange()(state.getRandom(), dst.getMin().asFloat(), dst.getMax().asFloat(),
414 a.getMin().asFloat(), a.getMax().asFloat(),
415 b.getMin().asFloat(), b.getMax().asFloat());
416 else if (baseType == VariableType::TYPE_INT)
417 ComputeValueRange()(state.getRandom(), dst.getMin().asInt(), dst.getMax().asInt(),
418 a.getMin().asInt(), a.getMax().asInt(),
419 b.getMin().asInt(), b.getMax().asInt());
420 else
421 {
422 DE_ASSERT(baseType == VariableType::TYPE_BOOL);
423 ComputeValueRange()(state.getRandom(), dst.getMin().asBool(), dst.getMax().asBool(),
424 a.getMin().asBool(), a.getMax().asBool(),
425 b.getMin().asBool(), b.getMax().asBool());
426 }
427 }
428 }
429
430 template <int Precedence, bool Float, bool Int, bool Bool, class ComputeValueRange, class EvaluateComp>
~BinaryVecOp(void)431 BinaryVecOp<Precedence, Float, Int, Bool, ComputeValueRange, EvaluateComp>::~BinaryVecOp (void)
432 {
433 }
434
435 template <int Precedence, bool Float, bool Int, bool Bool, class ComputeValueRange, class EvaluateComp>
evaluate(ExecValueAccess dst,ExecConstValueAccess a,ExecConstValueAccess b)436 void BinaryVecOp<Precedence, Float, Int, Bool, ComputeValueRange, EvaluateComp>::evaluate (ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
437 {
438 DE_ASSERT(dst.getType() == a.getType());
439 DE_ASSERT(dst.getType() == b.getType());
440 switch (dst.getType().getBaseType())
441 {
442 case VariableType::TYPE_FLOAT:
443 for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
444 {
445 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
446 dst.component(elemNdx).asFloat(compNdx) = EvaluateComp()(a.component(elemNdx).asFloat(compNdx), b.component(elemNdx).asFloat(compNdx));
447 }
448 break;
449
450 case VariableType::TYPE_INT:
451 for (int elemNdx = 0; elemNdx < dst.getType().getNumElements(); elemNdx++)
452 {
453 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
454 dst.component(elemNdx).asInt(compNdx) = EvaluateComp()(a.component(elemNdx).asInt(compNdx), b.component(elemNdx).asInt(compNdx));
455 }
456 break;
457
458 default:
459 DE_ASSERT(DE_FALSE); // Invalid type for multiplication
460 }
461 }
462
operator ()(de::Random & rnd,float dstMin,float dstMax,float & aMin,float & aMax,float & bMin,float & bMax) const463 void ComputeMulRange::operator() (de::Random& rnd, float dstMin, float dstMax, float& aMin, float& aMax, float& bMin, float& bMax) const
464 {
465 const float minScale = 0.25f;
466 const float maxScale = 2.0f;
467 const float subRangeStep = 0.25f;
468 const float scaleStep = 0.25f;
469
470 float scale = getQuantizedFloat(rnd, minScale, maxScale, scaleStep);
471 float scaledMin = dstMin/scale;
472 float scaledMax = dstMax/scale;
473
474 // Quantize scaled value range if possible
475 if (!quantizeFloatRange(scaledMin, scaledMax))
476 {
477 // Fall back to 1.0 as a scale
478 scale = 1.0f;
479 scaledMin = dstMin;
480 scaledMax = dstMax;
481 }
482
483 float subRangeLen = getQuantizedFloat(rnd, 0.0f, scaledMax-scaledMin, subRangeStep);
484 aMin = scaledMin + getQuantizedFloat(rnd, 0.0f, (scaledMax-scaledMin)-subRangeLen, subRangeStep);
485 aMax = aMin + subRangeLen;
486
487 // Find scale range
488 bMin = scale;
489 bMax = scale;
490 for (int i = 0; i < 5; i++)
491 {
492 if (de::inBounds(aMin*(scale-(float)i*scaleStep), dstMin, dstMax) &&
493 de::inBounds(aMax*(scale-(float)i*scaleStep), dstMin, dstMax))
494 bMin = scale-(float)i*scaleStep;
495
496 if (de::inBounds(aMin*(scale+(float)i*scaleStep), dstMin, dstMax) &&
497 de::inBounds(aMax*(scale+(float)i*scaleStep), dstMin, dstMax))
498 bMax = scale+(float)i*scaleStep;
499 }
500
501 // Negative scale?
502 if (rnd.getBool())
503 {
504 std::swap(aMin, aMax);
505 std::swap(bMin, bMax);
506 aMin *= -1.0f;
507 aMax *= -1.0f;
508 bMin *= -1.0f;
509 bMax *= -1.0f;
510 }
511
512 #if defined(DE_DEBUG)
513 const float eps = 0.001f;
514 DE_ASSERT(aMin <= aMax && bMin <= bMax);
515 DE_ASSERT(de::inRange(aMin*bMin, dstMin-eps, dstMax+eps));
516 DE_ASSERT(de::inRange(aMin*bMax, dstMin-eps, dstMax+eps));
517 DE_ASSERT(de::inRange(aMax*bMin, dstMin-eps, dstMax+eps));
518 DE_ASSERT(de::inRange(aMax*bMax, dstMin-eps, dstMax+eps));
519 #endif
520 }
521
operator ()(de::Random & rnd,int dstMin,int dstMax,int & aMin,int & aMax,int & bMin,int & bMax) const522 void ComputeMulRange::operator() (de::Random& rnd, int dstMin, int dstMax, int& aMin, int& aMax, int& bMin, int& bMax) const
523 {
524 DE_UNREF(rnd);
525 aMin = dstMin;
526 aMax = dstMax;
527 bMin = 1;
528 bMax = 1;
529 }
530
MulOp(GeneratorState & state,ConstValueRangeAccess valueRange)531 MulOp::MulOp (GeneratorState& state, ConstValueRangeAccess valueRange)
532 : MulBase(state, Token::MUL, valueRange)
533 {
534 }
535
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)536 float MulOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
537 {
538 if (valueRange.getType().isVoid() ||
539 valueRange.getType().isFloatOrVec() ||
540 valueRange.getType().isIntOrVec())
541 return MulBase::getWeight(state, valueRange);
542 else
543 return 0.0f;
544 }
545
546 template <typename T>
operator ()(de::Random & random,T dstMin,T dstMax,T & aMin,T & aMax,T & bMin,T & bMax) const547 void ComputeAddRange::operator() (de::Random& random, T dstMin, T dstMax, T& aMin, T& aMax, T& bMin, T& bMax) const
548 {
549 struct GetRandom
550 {
551 int operator() (de::Random& rnd, int min, int max) const { return rnd.getInt(min, max); }
552 float operator() (de::Random& rnd, float min, float max) const { return getQuantizedFloat(rnd, min, max, 0.5f); }
553 };
554
555 T rangeLen = dstMax-dstMin;
556 T subRangeLen = GetRandom()(random, T(0), rangeLen);
557 T aOffset = GetRandom()(random, T(-8), T(8));
558
559 aMin = dstMin+aOffset;
560 aMax = aMin+subRangeLen;
561
562 bMin = -aOffset;
563 bMax = -aOffset+(rangeLen-subRangeLen);
564
565 #if defined(DE_DEBUG)
566 T eps = T(0.001);
567 DE_ASSERT(aMin <= aMax && bMin <= bMax);
568 DE_ASSERT(de::inRange(aMin+bMin, dstMin-eps, dstMax+eps));
569 DE_ASSERT(de::inRange(aMin+bMax, dstMin-eps, dstMax+eps));
570 DE_ASSERT(de::inRange(aMax+bMin, dstMin-eps, dstMax+eps));
571 DE_ASSERT(de::inRange(aMax+bMax, dstMin-eps, dstMax+eps));
572 #endif
573 }
574
575 template <>
operator()576 void ComputeAddRange::operator()<bool> (de::Random&, bool, bool, bool&, bool&, bool&, bool&) const
577 {
578 DE_ASSERT(DE_FALSE);
579 }
580
AddOp(GeneratorState & state,ConstValueRangeAccess valueRange)581 AddOp::AddOp (GeneratorState& state, ConstValueRangeAccess valueRange)
582 : AddBase(state, Token::PLUS, valueRange)
583 {
584 }
585
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)586 float AddOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
587 {
588 if (valueRange.getType().isVoid() ||
589 valueRange.getType().isFloatOrVec() ||
590 valueRange.getType().isIntOrVec())
591 return AddBase::getWeight(state, valueRange);
592 else
593 return 0.0f;
594 }
595
596 template <typename T>
operator ()(de::Random & random,T dstMin,T dstMax,T & aMin,T & aMax,T & bMin,T & bMax) const597 void ComputeSubRange::operator() (de::Random& random, T dstMin, T dstMax, T& aMin, T& aMax, T& bMin, T& bMax) const
598 {
599 struct GetRandom
600 {
601 int operator() (de::Random& rnd, int min, int max) const { return rnd.getInt(min, max); }
602 float operator() (de::Random& rnd, float min, float max) const { return getQuantizedFloat(rnd, min, max, 0.5f); }
603 };
604
605 T rangeLen = dstMax-dstMin;
606 T subRangeLen = GetRandom()(random, T(0), rangeLen);
607 T aOffset = GetRandom()(random, T(-8), T(8));
608
609 aMin = dstMin+aOffset;
610 aMax = aMin+subRangeLen;
611
612 bMin = aOffset-(rangeLen-subRangeLen);
613 bMax = aOffset;
614
615 #if defined(DE_DEBUG)
616 T eps = T(0.001);
617 DE_ASSERT(aMin <= aMax && bMin <= bMax);
618 DE_ASSERT(de::inRange(aMin-bMin, dstMin-eps, dstMax+eps));
619 DE_ASSERT(de::inRange(aMin-bMax, dstMin-eps, dstMax+eps));
620 DE_ASSERT(de::inRange(aMax-bMin, dstMin-eps, dstMax+eps));
621 DE_ASSERT(de::inRange(aMax-bMax, dstMin-eps, dstMax+eps));
622 #endif
623 }
624
625 template <>
operator()626 void ComputeSubRange::operator()<bool> (de::Random&, bool, bool, bool&, bool&, bool&, bool&) const
627 {
628 DE_ASSERT(DE_FALSE);
629 }
630
SubOp(GeneratorState & state,ConstValueRangeAccess valueRange)631 SubOp::SubOp (GeneratorState& state, ConstValueRangeAccess valueRange)
632 : SubBase(state, Token::MINUS, valueRange)
633 {
634 }
635
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)636 float SubOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
637 {
638 if (valueRange.getType().isVoid() ||
639 valueRange.getType().isFloatOrVec() ||
640 valueRange.getType().isIntOrVec())
641 return SubBase::getWeight(state, valueRange);
642 else
643 return 0.0f;
644 }
645
646 template <class ComputeValueRange, class EvaluateComp>
RelationalOp(GeneratorState & state,Token::Type operatorToken,ConstValueRangeAccess inValueRange)647 RelationalOp<ComputeValueRange, EvaluateComp>::RelationalOp (GeneratorState& state, Token::Type operatorToken, ConstValueRangeAccess inValueRange)
648 : BinaryOp<7, ASSOCIATIVITY_LEFT>(operatorToken)
649 {
650 ValueRange valueRange = inValueRange;
651
652 if (valueRange.getType().isVoid())
653 {
654 valueRange = ValueRange(VariableType(VariableType::TYPE_BOOL, 1));
655 computeRandomValueRange(state, valueRange.asAccess());
656 }
657
658 // Choose type, allocate storage for execution
659 this->m_type = valueRange.getType();
660 this->m_value.setStorage(this->m_type);
661
662 // Choose random input type
663 VariableType::Type inBaseTypes[] = { VariableType::TYPE_FLOAT, VariableType::TYPE_INT };
664 VariableType::Type inBaseType = state.getRandom().choose<VariableType::Type>(&inBaseTypes[0], &inBaseTypes[DE_LENGTH_OF_ARRAY(inBaseTypes)]);
665
666 // Initialize storage for input value ranges
667 this->m_rightValueRange = ValueRange(VariableType(inBaseType, 1));
668 this->m_leftValueRange = ValueRange(VariableType(inBaseType, 1));
669
670 // Compute range for b that satisfies requested value range
671 {
672 bool dstMin = valueRange.getMin().asBool();
673 bool dstMax = valueRange.getMax().asBool();
674 ValueRangeAccess a = this->m_leftValueRange.asAccess();
675 ValueRangeAccess b = this->m_rightValueRange.asAccess();
676
677 if (inBaseType == VariableType::TYPE_FLOAT)
678 ComputeValueRange()(state.getRandom(), dstMin, dstMax,
679 a.getMin().asFloat(), a.getMax().asFloat(),
680 b.getMin().asFloat(), b.getMax().asFloat());
681 else if (inBaseType == VariableType::TYPE_INT)
682 ComputeValueRange()(state.getRandom(), dstMin, dstMax,
683 a.getMin().asInt(), a.getMax().asInt(),
684 b.getMin().asInt(), b.getMax().asInt());
685 }
686 }
687
688 template <class ComputeValueRange, class EvaluateComp>
~RelationalOp(void)689 RelationalOp<ComputeValueRange, EvaluateComp>::~RelationalOp (void)
690 {
691 }
692
693 template <class ComputeValueRange, class EvaluateComp>
evaluate(ExecValueAccess dst,ExecConstValueAccess a,ExecConstValueAccess b)694 void RelationalOp<ComputeValueRange, EvaluateComp>::evaluate (ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
695 {
696 DE_ASSERT(a.getType() == b.getType());
697 switch (a.getType().getBaseType())
698 {
699 case VariableType::TYPE_FLOAT:
700 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
701 dst.asBool(compNdx) = EvaluateComp()(a.asFloat(compNdx), b.asFloat(compNdx));
702 break;
703
704 case VariableType::TYPE_INT:
705 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
706 dst.asBool(compNdx) = EvaluateComp()(a.asInt(compNdx), b.asInt(compNdx));
707 break;
708
709 default:
710 DE_ASSERT(DE_FALSE);
711 }
712 }
713
714 template <class ComputeValueRange, class EvaluateComp>
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)715 float RelationalOp<ComputeValueRange, EvaluateComp>::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
716 {
717 if (!state.getProgramParameters().useComparisonOps)
718 return 0.0f;
719
720 if (valueRange.getType().isVoid() ||
721 (valueRange.getType().getBaseType() == VariableType::TYPE_BOOL && valueRange.getType().getNumElements() == 1))
722 return BinaryOp<7, ASSOCIATIVITY_LEFT>::getWeight(state, valueRange);
723 else
724 return 0.0f;
725 }
726
727 namespace
728 {
729
730 template <typename T> T getStep (void);
getStep(void)731 template <> inline float getStep (void) { return 0.25f; }
getStep(void)732 template <> inline int getStep (void) { return 1; }
733
734 } // anonymous
735
736 template <typename T>
operator ()(de::Random & rnd,bool dstMin,bool dstMax,T & aMin,T & aMax,T & bMin,T & bMax) const737 void ComputeLessThanRange::operator () (de::Random& rnd, bool dstMin, bool dstMax, T& aMin, T& aMax, T& bMin, T& bMax) const
738 {
739 struct GetRandom
740 {
741 int operator() (de::Random& random, int min, int max) const { return random.getInt(min, max); }
742 float operator() (de::Random& random, float min, float max) const { return getQuantizedFloat(random, min, max, getStep<float>()); }
743 };
744
745 // One random range
746 T rLen = GetRandom()(rnd, T(0), T(8));
747 T rMin = GetRandom()(rnd, T(-4), T(4));
748 T rMax = rMin+rLen;
749
750 if (dstMin == false && dstMax == true)
751 {
752 // Both values are possible, use same range for both inputs
753 aMin = rMin;
754 aMax = rMax;
755 bMin = rMin;
756 bMax = rMax;
757 }
758 else if (dstMin == true && dstMax == true)
759 {
760 // Compute range that is less than rMin..rMax
761 T aLen = GetRandom()(rnd, T(0), T(8)-rLen);
762
763 aMax = rMin - getStep<T>();
764 aMin = aMax - aLen;
765
766 bMin = rMin;
767 bMax = rMax;
768 }
769 else
770 {
771 // Compute range that is greater than or equal to rMin..rMax
772 T aLen = GetRandom()(rnd, T(0), T(8)-rLen);
773
774 aMin = rMax;
775 aMax = aMin + aLen;
776
777 bMin = rMin;
778 bMax = rMax;
779 }
780 }
781
LessThanOp(GeneratorState & state,ConstValueRangeAccess valueRange)782 LessThanOp::LessThanOp (GeneratorState& state, ConstValueRangeAccess valueRange)
783 : LessThanBase(state, Token::CMP_LT, valueRange)
784 {
785 }
786
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)787 float LessThanOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
788 {
789 return LessThanBase::getWeight(state, valueRange);
790 }
791
792 template <typename T>
operator ()(de::Random & rnd,bool dstMin,bool dstMax,T & aMin,T & aMax,T & bMin,T & bMax) const793 void ComputeLessOrEqualRange::operator () (de::Random& rnd, bool dstMin, bool dstMax, T& aMin, T& aMax, T& bMin, T& bMax) const
794 {
795 struct GetRandom
796 {
797 int operator() (de::Random& random, int min, int max) const { return random.getInt(min, max); }
798 float operator() (de::Random& random, float min, float max) const { return getQuantizedFloat(random, min, max, getStep<float>()); }
799 };
800
801 // One random range
802 T rLen = GetRandom()(rnd, T(0), T(8));
803 T rMin = GetRandom()(rnd, T(-4), T(4));
804 T rMax = rMin+rLen;
805
806 if (dstMin == false && dstMax == true)
807 {
808 // Both values are possible, use same range for both inputs
809 aMin = rMin;
810 aMax = rMax;
811 bMin = rMin;
812 bMax = rMax;
813 }
814 else if (dstMin == true && dstMax == true)
815 {
816 // Compute range that is less than or equal to rMin..rMax
817 T aLen = GetRandom()(rnd, T(0), T(8)-rLen);
818
819 aMax = rMin;
820 aMin = aMax - aLen;
821
822 bMin = rMin;
823 bMax = rMax;
824 }
825 else
826 {
827 // Compute range that is greater than rMin..rMax
828 T aLen = GetRandom()(rnd, T(0), T(8)-rLen);
829
830 aMin = rMax + getStep<T>();
831 aMax = aMin + aLen;
832
833 bMin = rMin;
834 bMax = rMax;
835 }
836 }
837
LessOrEqualOp(GeneratorState & state,ConstValueRangeAccess valueRange)838 LessOrEqualOp::LessOrEqualOp (GeneratorState& state, ConstValueRangeAccess valueRange)
839 : LessOrEqualBase(state, Token::CMP_LE, valueRange)
840 {
841 }
842
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)843 float LessOrEqualOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
844 {
845 return LessOrEqualBase::getWeight(state, valueRange);
846 }
847
GreaterThanOp(GeneratorState & state,ConstValueRangeAccess valueRange)848 GreaterThanOp::GreaterThanOp (GeneratorState& state, ConstValueRangeAccess valueRange)
849 : GreaterThanBase(state, Token::CMP_GT, valueRange)
850 {
851 }
852
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)853 float GreaterThanOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
854 {
855 return GreaterThanBase::getWeight(state, valueRange);
856 }
857
GreaterOrEqualOp(GeneratorState & state,ConstValueRangeAccess valueRange)858 GreaterOrEqualOp::GreaterOrEqualOp (GeneratorState& state, ConstValueRangeAccess valueRange)
859 : GreaterOrEqualBase(state, Token::CMP_GE, valueRange)
860 {
861 }
862
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)863 float GreaterOrEqualOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
864 {
865 return GreaterOrEqualBase::getWeight(state, valueRange);
866 }
867
868 namespace
869 {
870
871 template <bool IsEqual, typename T>
computeEqualityValueRange(de::Random & rnd,bool dstMin,bool dstMax,T & aMin,T & aMax,T & bMin,T & bMax)872 void computeEqualityValueRange (de::Random& rnd, bool dstMin, bool dstMax, T& aMin, T& aMax, T& bMin, T& bMax)
873 {
874 if (dstMin == false && dstMax == true)
875 ComputeLessThanRange()(rnd, false, true, aMin, aMax, bMin, bMax);
876 else if (IsEqual && dstMin == false)
877 ComputeLessThanRange()(rnd, true, true, aMin, aMax, bMin, bMax);
878 else if (!IsEqual && dstMin == true)
879 ComputeLessThanRange()(rnd, true, true, aMin, aMax, bMin, bMax);
880 else
881 {
882 // Must have exactly same values.
883 struct GetRandom
884 {
885 int operator() (de::Random& random, int min, int max) const { return random.getInt(min, max); }
886 float operator() (de::Random& random, float min, float max) const { return getQuantizedFloat(random, min, max, 0.5f); }
887 };
888
889 T val = GetRandom()(rnd, T(-1), T(1));
890
891 aMin = val;
892 aMax = val;
893 bMin = val;
894 bMax = val;
895 }
896 }
897
898 template <>
computeEqualityValueRange(de::Random & rnd,bool dstMin,bool dstMax,bool & aMin,bool & aMax,bool & bMin,bool & bMax)899 void computeEqualityValueRange<true, bool> (de::Random& rnd, bool dstMin, bool dstMax, bool& aMin, bool& aMax, bool& bMin, bool& bMax)
900 {
901 if (dstMin == false && dstMax == true)
902 {
903 aMin = false;
904 aMax = true;
905 bMin = false;
906 bMax = true;
907 }
908 else if (dstMin == false)
909 {
910 DE_ASSERT(dstMax == false);
911 bool val = rnd.getBool();
912
913 aMin = val;
914 aMax = val;
915 bMin = !val;
916 bMax = !val;
917 }
918 else
919 {
920 DE_ASSERT(dstMin == true && dstMax == true);
921 bool val = rnd.getBool();
922
923 aMin = val;
924 aMax = val;
925 bMin = val;
926 bMax = val;
927 }
928 }
929
930 template <>
computeEqualityValueRange(de::Random & rnd,bool dstMin,bool dstMax,bool & aMin,bool & aMax,bool & bMin,bool & bMax)931 void computeEqualityValueRange<false, bool> (de::Random& rnd, bool dstMin, bool dstMax, bool& aMin, bool& aMax, bool& bMin, bool& bMax)
932 {
933 if (dstMin == false && dstMax == true)
934 computeEqualityValueRange<true>(rnd, dstMin, dstMax, aMin, aMax, bMin, bMax);
935 else
936 computeEqualityValueRange<true>(rnd, !dstMin, !dstMax, aMin, aMax, bMin, bMax);
937 }
938
939 } // anonymous
940
941 template <bool IsEqual>
EqualityComparisonOp(GeneratorState & state,ConstValueRangeAccess inValueRange)942 EqualityComparisonOp<IsEqual>::EqualityComparisonOp (GeneratorState& state, ConstValueRangeAccess inValueRange)
943 : BinaryOp<8, ASSOCIATIVITY_LEFT>(IsEqual ? Token::CMP_EQ : Token::CMP_NE)
944 {
945 ValueRange valueRange = inValueRange;
946
947 if (valueRange.getType().isVoid())
948 {
949 valueRange = ValueRange(VariableType(VariableType::TYPE_BOOL, 1));
950 computeRandomValueRange(state, valueRange.asAccess());
951 }
952
953 // Choose type, allocate storage for execution
954 this->m_type = valueRange.getType();
955 this->m_value.setStorage(this->m_type);
956
957 // Choose random input type
958 VariableType::Type inBaseTypes[] = { VariableType::TYPE_FLOAT, VariableType::TYPE_INT };
959 VariableType::Type inBaseType = state.getRandom().choose<VariableType::Type>(&inBaseTypes[0], &inBaseTypes[DE_LENGTH_OF_ARRAY(inBaseTypes)]);
960 int availableLevels = state.getShaderParameters().maxExpressionDepth - state.getExpressionDepth();
961 int numElements = state.getRandom().getInt(1, availableLevels >= 3 ? 4 : 1);
962
963 // Initialize storage for input value ranges
964 this->m_rightValueRange = ValueRange(VariableType(inBaseType, numElements));
965 this->m_leftValueRange = ValueRange(VariableType(inBaseType, numElements));
966
967 // Compute range for b that satisfies requested value range
968 for (int elementNdx = 0; elementNdx < numElements; elementNdx++)
969 {
970 bool dstMin = valueRange.getMin().asBool();
971 bool dstMax = valueRange.getMax().asBool();
972
973 ValueRangeAccess a = this->m_leftValueRange.asAccess().component(elementNdx);
974 ValueRangeAccess b = this->m_rightValueRange.asAccess().component(elementNdx);
975
976 if (inBaseType == VariableType::TYPE_FLOAT)
977 computeEqualityValueRange<IsEqual>(state.getRandom(), dstMin, dstMax,
978 a.getMin().asFloat(), a.getMax().asFloat(),
979 b.getMin().asFloat(), b.getMax().asFloat());
980 else if (inBaseType == VariableType::TYPE_INT)
981 computeEqualityValueRange<IsEqual>(state.getRandom(), dstMin, dstMax,
982 a.getMin().asInt(), a.getMax().asInt(),
983 b.getMin().asInt(), b.getMax().asInt());
984 else
985 {
986 DE_ASSERT(inBaseType == VariableType::TYPE_BOOL);
987 computeEqualityValueRange<IsEqual>(state.getRandom(), dstMin, dstMax,
988 a.getMin().asBool(), a.getMax().asBool(),
989 b.getMin().asBool(), b.getMax().asBool());
990 }
991 }
992 }
993
994 template <bool IsEqual>
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)995 float EqualityComparisonOp<IsEqual>::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
996 {
997 if (!state.getProgramParameters().useComparisonOps)
998 return 0.0f;
999
1000 // \todo [2011-06-13 pyry] Weight down cases that would force constant inputs.
1001
1002 if (valueRange.getType().isVoid() ||
1003 (valueRange.getType().getBaseType() == VariableType::TYPE_BOOL && valueRange.getType().getNumElements() == 1))
1004 return BinaryOp<8, ASSOCIATIVITY_LEFT>::getWeight(state, valueRange);
1005 else
1006 return 0.0f;
1007 }
1008
1009 namespace
1010 {
1011
1012 template <bool IsEqual>
1013 struct EqualityCompare
1014 {
1015 template <typename T>
1016 static bool compare (T a, T b);
1017 static bool combine (bool a, bool b);
1018 };
1019
1020 template <>
1021 template <typename T>
compare(T a,T b)1022 inline bool EqualityCompare<true>::compare (T a, T b) { return a == b; }
1023
1024 template <>
combine(bool a,bool b)1025 inline bool EqualityCompare<true>::combine (bool a, bool b) { return a && b; }
1026
1027 template <>
1028 template <typename T>
compare(T a,T b)1029 inline bool EqualityCompare<false>::compare (T a, T b) { return a != b; }
1030
1031 template <>
combine(bool a,bool b)1032 inline bool EqualityCompare<false>::combine (bool a, bool b) { return a || b; }
1033
1034 } // anonymous
1035
1036 template <bool IsEqual>
evaluate(ExecValueAccess dst,ExecConstValueAccess a,ExecConstValueAccess b)1037 void EqualityComparisonOp<IsEqual>::evaluate (ExecValueAccess dst, ExecConstValueAccess a, ExecConstValueAccess b)
1038 {
1039 DE_ASSERT(a.getType() == b.getType());
1040
1041
1042 switch (a.getType().getBaseType())
1043 {
1044 case VariableType::TYPE_FLOAT:
1045 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
1046 {
1047 bool result = IsEqual ? true : false;
1048
1049 for (int elemNdx = 0; elemNdx < a.getType().getNumElements(); elemNdx++)
1050 result = EqualityCompare<IsEqual>::combine(result, EqualityCompare<IsEqual>::compare(a.component(elemNdx).asFloat(compNdx), b.component(elemNdx).asFloat(compNdx)));
1051
1052 dst.asBool(compNdx) = result;
1053 }
1054 break;
1055
1056 case VariableType::TYPE_INT:
1057 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
1058 {
1059 bool result = IsEqual ? true : false;
1060
1061 for (int elemNdx = 0; elemNdx < a.getType().getNumElements(); elemNdx++)
1062 result = EqualityCompare<IsEqual>::combine(result, EqualityCompare<IsEqual>::compare(a.component(elemNdx).asInt(compNdx), b.component(elemNdx).asInt(compNdx)));
1063
1064 dst.asBool(compNdx) = result;
1065 }
1066 break;
1067
1068 case VariableType::TYPE_BOOL:
1069 for (int compNdx = 0; compNdx < EXEC_VEC_WIDTH; compNdx++)
1070 {
1071 bool result = IsEqual ? true : false;
1072
1073 for (int elemNdx = 0; elemNdx < a.getType().getNumElements(); elemNdx++)
1074 result = EqualityCompare<IsEqual>::combine(result, EqualityCompare<IsEqual>::compare(a.component(elemNdx).asBool(compNdx), b.component(elemNdx).asBool(compNdx)));
1075
1076 dst.asBool(compNdx) = result;
1077 }
1078 break;
1079
1080 default:
1081 DE_ASSERT(DE_FALSE);
1082 }
1083 }
1084
EqualOp(GeneratorState & state,ConstValueRangeAccess valueRange)1085 EqualOp::EqualOp (GeneratorState& state, ConstValueRangeAccess valueRange)
1086 : EqualityComparisonOp<true>(state, valueRange)
1087 {
1088 }
1089
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)1090 float EqualOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
1091 {
1092 return EqualityComparisonOp<true>::getWeight(state, valueRange);
1093 }
1094
NotEqualOp(GeneratorState & state,ConstValueRangeAccess valueRange)1095 NotEqualOp::NotEqualOp (GeneratorState& state, ConstValueRangeAccess valueRange)
1096 : EqualityComparisonOp<false>(state, valueRange)
1097 {
1098 }
1099
getWeight(const GeneratorState & state,ConstValueRangeAccess valueRange)1100 float NotEqualOp::getWeight (const GeneratorState& state, ConstValueRangeAccess valueRange)
1101 {
1102 return EqualityComparisonOp<false>::getWeight(state, valueRange);
1103 }
1104
1105 } // rsg
1106