tcuInterval.js (18617B)
1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 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 Interval arithmetic and floating point precisions. 22 *//*--------------------------------------------------------------------*/ 23 'use strict'; 24 goog.provide('framework.common.tcuInterval'); 25 goog.require('framework.delibs.debase.deMath'); 26 27 goog.scope(function() { 28 29 var tcuInterval = framework.common.tcuInterval; 30 var deMath = framework.delibs.debase.deMath; 31 32 /** 33 * @typedef {function(number):number} 34 */ 35 tcuInterval.DoubleFunc1; 36 37 /** 38 * @typedef {function(number, number):number} 39 */ 40 tcuInterval.DoubleFunc2; 41 42 /** 43 * @typedef {function(number,number,number):number} 44 */ 45 tcuInterval.DoubleFunc3; 46 47 /** 48 * @typedef {function(number):tcuInterval.Interval} 49 */ 50 tcuInterval.DoubleIntervalFunc1; 51 52 /** 53 * @typedef {function(number,number):tcuInterval.Interval} 54 */ 55 tcuInterval.DoubleIntervalFunc2; 56 57 /** 58 * @typedef {function(number,number,number):tcuInterval.Interval} 59 */ 60 tcuInterval.DoubleIntervalFunc3; 61 62 /** 63 * @param {function(number): number} func 64 * @param {tcuInterval.Interval} arg0 65 * @return {tcuInterval.Interval} 66 */ 67 tcuInterval.applyMonotone1p = function(func, arg0) { 68 /** 69 * @param {number=} x 70 * @param {number=} y 71 * @return {number} 72 */ 73 var body = function(x, y) { 74 x = x || 0; 75 return func(x); 76 }; 77 return tcuInterval.applyMonotone1(arg0, 78 function(x) { return tcuInterval.setInterval(body, x); }); 79 }; 80 81 /** 82 * @param {function(number): tcuInterval.Interval} func 83 * @param {tcuInterval.Interval} arg0 84 * @return {tcuInterval.Interval} 85 */ 86 tcuInterval.applyMonotone1i = function(func, arg0) { 87 return tcuInterval.withIntervals(func(arg0.lo()), func(arg0.hi())); 88 }; 89 90 /** 91 * @param {function(number, number): number} func 92 * @param {tcuInterval.Interval} arg0 93 * @param {tcuInterval.Interval} arg1 94 * @return {tcuInterval.Interval} 95 */ 96 tcuInterval.applyMonotone2p = function(func, arg0, arg1) { 97 /** 98 * @param {number=} x 99 * @param {number=} y 100 * @return {number} 101 */ 102 var body = function(x, y) { 103 x = x || 0; 104 y = y || 0; 105 return func(x, y); 106 }; 107 return tcuInterval.applyMonotone2(arg0, arg1, 108 function(x, y) { return tcuInterval.setInterval(body, x, y); }); 109 }; 110 111 /** 112 * @param {function(number, number): tcuInterval.Interval} func 113 * @param {tcuInterval.Interval} arg0 114 * @param {tcuInterval.Interval} arg1 115 * @return {tcuInterval.Interval} 116 */ 117 tcuInterval.applyMonotone2i = function(func, arg0, arg1) { 118 /** @type {number} */ var lo0 = arg0.lo(); 119 /** @type {number} */ var hi0 = arg0.hi(); 120 /** @type {number} */ var lo1 = arg1.lo(); 121 /** @type {number} */ var hi1 = arg1.hi(); 122 var a = tcuInterval.withIntervals(func(lo0, lo1), func(lo0, hi1)); 123 var b = tcuInterval.withIntervals(func(hi0, lo1), func(hi0, hi1)); 124 return tcuInterval.withIntervals(a, b); 125 }; 126 127 /** 128 * @constructor 129 * @param {number=} val 130 */ 131 tcuInterval.Interval = function(val) { 132 if (val === undefined) { 133 this.m_hasNaN = false; 134 this.m_lo = Number.POSITIVE_INFINITY; 135 this.m_hi = Number.NEGATIVE_INFINITY; 136 } else { 137 this.m_hasNaN = isNaN(val); 138 this.m_lo = this.m_hasNaN ? Number.POSITIVE_INFINITY : val; 139 this.m_hi = this.m_hasNaN ? Number.NEGATIVE_INFINITY : val; 140 } 141 }; 142 143 tcuInterval.Interval.prototype.toString = function() { 144 var str = 'Interval(' + this.m_lo + ', ' + this.m_hi; 145 if (this.m_hasNaN) 146 str += ', hasNaN'; 147 str += ')'; 148 return str; 149 }; 150 151 /** 152 * @param {tcuInterval.Interval} a 153 * @param {tcuInterval.Interval} b 154 * @return {tcuInterval.Interval} 155 */ 156 tcuInterval.withIntervals = function(a, b) { 157 /** @type {tcuInterval.Interval} */ var interval = new tcuInterval.Interval(); 158 interval.m_hasNaN = (a.m_hasNaN || b.m_hasNaN); 159 interval.m_lo = Math.min(a.m_lo, b.m_lo); 160 interval.m_hi = Math.max(a.m_hi, b.m_hi); 161 return interval; 162 }; 163 164 /** 165 * @param {number} a 166 * @param {number} b 167 * @return {tcuInterval.Interval} 168 */ 169 tcuInterval.withNumbers = function(a, b) { 170 var x = new tcuInterval.Interval(a); 171 var y = new tcuInterval.Interval(b); 172 return tcuInterval.withIntervals(x, y); 173 }; 174 175 /** 176 * @param {boolean} hasNaN_ 177 * @param {number} lo_ 178 * @param {number} hi_ 179 * @return {tcuInterval.Interval} 180 */ 181 tcuInterval.withParams = function(hasNaN_, lo_, hi_) { 182 /** @type {tcuInterval.Interval} */ var interval = new tcuInterval.Interval(); 183 interval.m_hasNaN = hasNaN_; 184 interval.m_lo = lo_; 185 interval.m_hi = hi_; 186 return interval; 187 }; 188 189 /** 190 * @return {number} 191 */ 192 tcuInterval.Interval.prototype.length = function() { 193 return this.m_hi - this.m_lo; 194 }; 195 196 /** 197 * @return {number} 198 */ 199 tcuInterval.Interval.prototype.lo = function() { 200 return this.m_lo; 201 }; 202 203 /** 204 * @return {number} 205 */ 206 tcuInterval.Interval.prototype.hi = function() { 207 return this.m_hi; 208 }; 209 210 /** 211 * @return {boolean} 212 */ 213 tcuInterval.Interval.prototype.hasNaN = function() { 214 return this.m_hasNaN; 215 }; 216 217 /** 218 * @return {tcuInterval.Interval} 219 */ 220 tcuInterval.Interval.prototype.nan = function() { 221 return this.m_hasNaN ? new tcuInterval.Interval(NaN) : new tcuInterval.Interval(); 222 }; 223 224 /** 225 * @return {boolean} 226 */ 227 tcuInterval.Interval.prototype.empty = function() { 228 return this.m_lo > this.m_hi; 229 }; 230 231 /** 232 * @return {boolean} 233 */ 234 tcuInterval.Interval.prototype.isFinite = function() { 235 return isFinite(this.m_lo) && isFinite(this.m_hi); 236 }; 237 238 /** 239 * @return {boolean} 240 */ 241 tcuInterval.Interval.prototype.isOrdinary = function() { 242 return !this.hasNaN() && !this.empty() && this.isFinite(); 243 }; 244 245 /** 246 * @param {tcuInterval.Interval} other 247 * @return {tcuInterval.Interval} 248 */ 249 tcuInterval.Interval.prototype.operatorOrBinary = function(other) { 250 /** @type {tcuInterval.Interval} */ var temp = new tcuInterval.Interval(); 251 temp.m_hasNaN = this.m_hasNaN || other.m_hasNaN; 252 temp.m_lo = Math.min(this.m_lo, other.m_lo); 253 temp.m_hi = Math.max(this.m_hi, other.m_hi); 254 return temp; 255 }; 256 257 /** 258 * @param {tcuInterval.Interval} other 259 */ 260 tcuInterval.Interval.prototype.operatorOrAssignBinary = function(other) { 261 /** @type {tcuInterval.Interval} */ var temp = this.operatorOrBinary(other); 262 this.m_hasNaN = temp.m_hasNaN; 263 this.m_lo = temp.m_lo; 264 this.m_hi = temp.m_hi; 265 }; 266 267 /** 268 * @param {tcuInterval.Interval} other 269 * @return {tcuInterval.Interval} 270 */ 271 tcuInterval.Interval.prototype.operatorAndBinary = function(other) { 272 /** @type {tcuInterval.Interval} */ var temp = new tcuInterval.Interval(); 273 temp.m_hasNaN = this.m_hasNaN && other.m_hasNaN; 274 temp.m_lo = Math.max(this.m_lo, other.m_lo); 275 temp.m_hi = Math.min(this.m_hi, other.m_hi); 276 return temp; 277 }; 278 279 /** 280 * @param {tcuInterval.Interval} other 281 */ 282 tcuInterval.Interval.prototype.operatorAndAssignBinary = function(other) { 283 /** @type {tcuInterval.Interval} */ var temp = this.operatorAndBinary(other); 284 this.m_hasNaN = temp.m_hasNaN; 285 this.m_lo = temp.m_lo; 286 this.m_hi = temp.m_hi; 287 }; 288 289 /** 290 * @param {tcuInterval.Interval} other 291 * @return {boolean} 292 */ 293 tcuInterval.Interval.prototype.contains = function(other) { 294 return (other.lo() >= this.lo() && other.hi() <= this.hi() && 295 (!other.hasNaN() || this.hasNaN())); 296 }; 297 298 /** 299 * @param {tcuInterval.Interval} other 300 * @return {boolean} 301 */ 302 tcuInterval.Interval.prototype.intersects = function(other) { 303 return ((other.hi() >= this.lo() && other.lo() >= this.hi()) || 304 (other.hasNaN() && this.hasNaN())); 305 }; 306 307 /** 308 * @return {tcuInterval.Interval} 309 */ 310 tcuInterval.Interval.prototype.operatorNegative = function() { 311 /** @type {tcuInterval.Interval} */ var temp = new tcuInterval.Interval(); 312 temp.m_hasNaN = this.m_hasNaN; 313 temp.m_lo = -this.m_hi; 314 temp.m_hi = -this.m_lo; 315 return temp; 316 }; 317 318 /** 319 * @param {boolean=} nan 320 * @return {tcuInterval.Interval} 321 */ 322 tcuInterval.unbounded = function(nan) { 323 if (nan === undefined) 324 nan = false; 325 return tcuInterval.withParams(nan, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY); 326 }; 327 328 /** 329 * @return {number} 330 */ 331 tcuInterval.Interval.prototype.midpoint = function() { 332 return 0.5 * (this.hi() + this.lo()); // returns NaN when not bounded 333 }; 334 335 /** 336 * @param {tcuInterval.Interval} other 337 * @return {boolean} 338 */ 339 tcuInterval.Interval.prototype.operatorCompare = function(other) { 340 return ((this.m_hasNaN == other.m_hasNaN) && 341 ((this.empty() && other.empty()) || 342 (this.m_lo == other.m_lo && this.m_hi == other.m_hi))); 343 }; 344 345 /** 346 * @param {tcuInterval.Interval} x 347 * @return {tcuInterval.Interval} 348 */ 349 tcuInterval.Interval.operatorPositive = function(x) { 350 return x; 351 }; 352 353 /** 354 * @param {tcuInterval.Interval} x 355 * @return {tcuInterval.Interval} 356 */ 357 tcuInterval.Interval.exp2 = function(x) { 358 // std::pow 359 return tcuInterval.applyMonotone2p(Math.pow, new tcuInterval.Interval(2), x); 360 }; 361 362 /** 363 * @param {tcuInterval.Interval} x 364 * @return {tcuInterval.Interval} 365 */ 366 tcuInterval.Interval.exp = function(x) { 367 // std::exp 368 return tcuInterval.applyMonotone1p(Math.exp, x); 369 }; 370 371 /** 372 * @param {tcuInterval.Interval} x 373 * @return {tcuInterval.Interval} 374 */ 375 tcuInterval.Interval.sign = function(x) { 376 // TODO 377 throw new Error('Unimplemented'); 378 }; 379 380 /** 381 * @param {tcuInterval.Interval} x 382 * @param {tcuInterval.Interval} y 383 * @return {tcuInterval.Interval} 384 */ 385 tcuInterval.Interval.operatorSum = function(x, y) { 386 /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); 387 388 if (!x.empty() && !y.empty()) 389 ret = tcuInterval.setIntervalBounds(function(dummy) {return x.lo() + y.lo();}, function(dummy) {return x.hi() + y.hi();}); 390 if (x.hasNaN() || y.hasNaN()) 391 ret.operatorOrAssignBinary(new tcuInterval.Interval(NaN)); 392 393 return ret; 394 }; 395 396 /** 397 * @param {tcuInterval.Interval} x 398 * @param {tcuInterval.Interval} y 399 * @return {tcuInterval.Interval} 400 */ 401 tcuInterval.Interval.operatorSub = function(x, y) { 402 /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); 403 404 /** 405 * @param {number=} x 406 * @param {number=} y 407 * @return {tcuInterval.Interval} 408 */ 409 var body = function(x, y) { 410 return new tcuInterval.Interval(x - y); 411 }; 412 413 ret = tcuInterval.applyMonotone2(x, y, body); 414 return ret; 415 }; 416 417 /** 418 * @param {tcuInterval.Interval} x 419 * @param {tcuInterval.Interval} y 420 * @return {tcuInterval.Interval} 421 */ 422 tcuInterval.Interval.operatorMul = function(x, y) { 423 /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); 424 425 /** 426 * @param {number=} x 427 * @param {number=} y 428 * @return {tcuInterval.Interval} 429 */ 430 var body = function(x, y) { 431 return new tcuInterval.Interval(x * y); 432 }; 433 434 ret = tcuInterval.applyMonotone2(x, y, body); 435 436 return ret; 437 }; 438 439 /** 440 * @param {tcuInterval.Interval} nom 441 * @param {tcuInterval.Interval} den 442 * @return {tcuInterval.Interval} 443 */ 444 tcuInterval.Interval.operatorDiv = function(nom, den) { 445 if (den.contains(new tcuInterval.Interval(0))) { 446 // \todo [2014-03-21 lauri] Non-inf endpoint when one den endpoint is 447 // zero and nom doesn't cross zero? 448 return tcuInterval.unbounded(); 449 } else { 450 /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); 451 /** 452 * @param {number=} x 453 * @param {number=} y 454 * @return {tcuInterval.Interval} 455 */ 456 var body = function(x, y) { 457 return new tcuInterval.Interval(x / y); 458 }; 459 460 ret = tcuInterval.applyMonotone2(nom, den, body); 461 462 return ret; 463 } 464 }; 465 466 /** 467 * @param {tcuInterval.Interval} x 468 * @return {tcuInterval.Interval} 469 */ 470 tcuInterval.Interval.prototype.abs = function(x) { 471 //std::abs 472 /** @type {tcuInterval.Interval} */ var mono = tcuInterval.applyMonotone1p(Math.abs, x); 473 var zero = new tcuInterval.Interval(0); 474 if (x.contains(zero)) 475 return tcuInterval.withIntervals(zero, mono); 476 477 return mono; 478 }; 479 480 /** 481 * @param {tcuInterval.Interval} x 482 * @return {tcuInterval.Interval} 483 */ 484 tcuInterval.Interval.sqrt = function(x) { 485 return tcuInterval.applyMonotone1p(Math.sqrt, x); 486 }; 487 488 /** 489 * @param {tcuInterval.Interval} x 490 * @return {tcuInterval.Interval} 491 */ 492 tcuInterval.Interval.inverseSqrt = function(x) { 493 var ret = new tcuInterval.Interval(1); 494 ret = tcuInterval.Interval.operatorDiv(ret, tcuInterval.Interval.sqrt(x)); 495 return ret; 496 }; 497 498 /** 499 * @param {function(number=, number=): number} setLow 500 * @param {function(number=, number=): number} setHigh 501 * @param {number=} arg0 502 * @param {number=} arg1 503 * @return {tcuInterval.Interval} 504 */ 505 tcuInterval.setIntervalBounds = function(setLow, setHigh, arg0, arg1) { 506 // TODO: No support for rounding modes. Originally, setLow() was rounded down and setHigh() rounded up 507 var lo = new tcuInterval.Interval(setLow(arg0, arg1)); 508 var hi = new tcuInterval.Interval(setHigh(arg0, arg1)); 509 return lo.operatorOrBinary(hi); 510 }; 511 512 /** 513 * @param {function(number=, number=): number} set 514 * @param {number=} arg0 515 * @param {number=} arg1 516 * @return {tcuInterval.Interval} 517 */ 518 tcuInterval.setInterval = function(set, arg0, arg1) { 519 return tcuInterval.setIntervalBounds(set, set, arg0, arg1); 520 }; 521 522 /** 523 * @param {tcuInterval.Interval} arg 524 * @param {function(number): tcuInterval.Interval} body 525 * @return {tcuInterval.Interval} 526 */ 527 tcuInterval.applyMonotone1 = function(arg, body) { 528 var ret = new tcuInterval.Interval(); 529 530 if (!arg.empty()) { 531 var lo = body(arg.lo()); 532 var hi = body(arg.hi()); 533 ret = lo.operatorOrBinary(hi); 534 } 535 536 if (arg.hasNaN()) { 537 ret = ret.operatorOrBinary(new tcuInterval.Interval(NaN)); 538 } 539 540 return ret; 541 }; 542 543 /** 544 * TODO: Check if this function works properly 545 * @param {tcuInterval.Interval} arg0 546 * @param {tcuInterval.Interval} arg1 547 * @param {function(number, number): tcuInterval.Interval} body 548 * @return {tcuInterval.Interval} 549 */ 550 tcuInterval.applyMonotone2 = function(arg0, arg1, body) { 551 var ret = new tcuInterval.Interval(); 552 553 if (!arg0.empty() && !arg1.empty()) { 554 var lo0 = body(arg0.lo(), arg1.lo()); 555 var lo1 = body(arg0.lo(), arg1.hi()); 556 var hi0 = body(arg0.hi(), arg1.lo()); 557 var hi1 = body(arg0.hi(), arg1.hi()); 558 var a = lo0.operatorOrBinary(hi0); 559 var b = lo1.operatorOrBinary(hi1); 560 ret = a.operatorOrBinary(b); 561 } 562 563 if (arg0.hasNaN() || arg1.hasNaN()) { 564 ret = ret.operatorOrBinary(new tcuInterval.Interval(NaN)); 565 } 566 567 return ret; 568 }; 569 570 /** 571 * TODO: Check if this function works properly 572 * @param {tcuInterval.Interval} arg0 573 * @param {tcuInterval.Interval} arg1 574 * @param {tcuInterval.Interval} arg2 575 * @param {function(number, number, number): tcuInterval.Interval} body 576 * @return {tcuInterval.Interval} 577 */ 578 tcuInterval.applyMonotone3 = function(arg0, arg1, arg2, body) { 579 var ret = new tcuInterval.Interval(); 580 581 if (!arg0.empty() && !arg1.empty() && !arg2.empty()) { 582 var i0 = body(arg0.lo(), arg1.lo(), arg2.lo()); 583 var i1 = body(arg0.lo(), arg1.lo(), arg2.hi()); 584 var i2 = body(arg0.lo(), arg1.hi(), arg2.lo()); 585 var i3 = body(arg0.lo(), arg1.hi(), arg2.hi()); 586 var i4 = body(arg0.hi(), arg1.lo(), arg2.lo()); 587 var i5 = body(arg0.hi(), arg1.lo(), arg2.hi()); 588 var i6 = body(arg0.hi(), arg1.hi(), arg2.lo()); 589 var i7 = body(arg0.hi(), arg1.hi(), arg2.hi()); 590 591 var low = Math.min(i0.lo(), i1.lo(), i2.lo(), i3.lo(), i4.lo(), i5.lo(), i6.lo(), i7.lo()); 592 var high = Math.max(i0.hi(), i1.hi(), i2.hi(), i3.hi(), i4.hi(), i5.hi(), i6.hi(), i7.hi()); 593 var hasNaN = i0.hasNaN() || i1.hasNaN() || i2.hasNaN() || i3.hasNaN() || i4.hasNaN() || i5.hasNaN() || i6.hasNaN() || i7.hasNaN(); 594 595 ret = tcuInterval.withParams(hasNaN, low, high); 596 } 597 598 if (arg0.hasNaN() || arg1.hasNaN() || arg2.hasNaN()) { 599 ret = ret.operatorOrBinary(new tcuInterval.Interval(NaN)); 600 } 601 602 return ret; 603 }; 604 605 /** @const */ tcuInterval.POSITIVE_INFINITY = new tcuInterval.Interval(Infinity); 606 /** @const */ tcuInterval.NEGATIVE_INFINITY = new tcuInterval.Interval(-Infinity); 607 /** @const */ tcuInterval.ZERO = new tcuInterval.Interval(0); 608 /** @const */ tcuInterval.NAN = new tcuInterval.Interval(NaN); 609 });