1 /* 2 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 @test 26 @summary 27 @summary com.apple.junit.java.util; 28 @run junit CollectionStress01 29 */ 30 31 /* 32 33 This test generates a field of boolean values, then uses recursive checks to 34 attempt to find a minimal set of rectangles that contain only the false values. 35 36 Puts a fair bit of stress on the collection classes. 37 38 */ 39 40 import junit.framework.*; 41 42 import java.awt.*; 43 import java.util.*; 44 45 46 public class CollectionStress01 extends TestCase { 47 48 static final boolean kDebug = false; 49 static final int kSeed = 12225; 50 static final int kPaintThreshold = 50000; 51 static final int kPauseTime = 5; 52 static final int kBooleanDensity = 25; 53 static final int kWidth = 32; 54 static final int kHeight = 36; 55 static final int kScale = 18; 56 static final Rectangle kBoolRect = new Rectangle( 0, 0, kWidth, kHeight); 57 static final Rectangle kDisplayRect = new Rectangle( 0, 0, kWidth*kScale, kHeight*kScale); 58 59 boolean[][] boolSpace = new boolean[kWidth][kHeight]; 60 61 Frame displayFrame = null; 62 DisplayArea displayArea = new DisplayArea(); 63 Random rand = new Random(kSeed); 64 65 HashMap<Rectangle,Vector<Rectangle>> cache = new HashMap<Rectangle,Vector<Rectangle>> (); 66 67 class DisplayArea extends Panel { 68 private static final long serialVersionUID = 1L; 69 Vector<Rectangle> currEmpties; 70 Vector<Rectangle> debugCases; 71 String debugInfo; 72 Rectangle currRect; 73 Point currPoint; 74 75 public Dimension getPreferredSize() { 76 return ( new Dimension(kDisplayRect.width, kDisplayRect.height) ); 77 } 78 79 public void paint( Graphics g){ 80 super.paint(g); 81 g.clearRect(0,0, kDisplayRect.width, kDisplayRect.height); 82 83 // representation of boolean field 84 g.setColor( Color.RED ); 85 for(int i = 0; i < boolSpace.length; i+=1) { 86 for(int ii = 0; ii < boolSpace[i].length; ii+=1) { 87 if (boolSpace[i][ii]) { 88 g.fillRect(i*kScale, ii*kScale, kScale, kScale); 89 } 90 } 91 } 92 93 // draw the currPoint 94 g.setColor( Color.BLUE ); 95 Point p = null; 96 synchronized (this) { 97 p = currPoint; 98 } 99 if (p != null) { 100 g.fillRect(p.x*kScale, p.y*kScale, kScale, kScale); 101 } 102 103 // draw the debugCases 104 g.setColor( new Color( 0x40, 0xFF, 0x40, 0x80 )); 105 Vector<Rectangle> dVect = null; 106 synchronized (this) { 107 dVect = debugCases; 108 } 109 if (dVect != null) { 110 for (Rectangle e : dVect) { 111 g.fillRoundRect((e.x*kScale)+1, (e.y*kScale)+1, (e.width*kScale)-2, (e.height*kScale) -2 , 15, 15); 112 } 113 } 114 115 // draw the currEmpties 116 g.setColor( Color.CYAN); 117 Vector<Rectangle> eVect = null; 118 synchronized (this) { 119 eVect = currEmpties; 120 } 121 if (eVect != null) { 122 for (Rectangle e : eVect) { 123 g.fillRoundRect((e.x*kScale)+1, (e.y*kScale)+1, (e.width*kScale)-2, (e.height*kScale) -2 , 15, 15); 124 } 125 } 126 127 // draw the currRect 128 g.setColor( Color.BLACK ); 129 Rectangle r = null; 130 synchronized (this) { 131 r = currRect; 132 } 133 if (r != null) { 134 g.drawRect(r.x*kScale+2, r.y*kScale+2, r.width*kScale-4, r.height*kScale-4); 135 } 136 137 // draw debugInfo at currPoint 138 g.setColor(Color.BLACK); 139 String d = null; 140 synchronized (this) { 141 d = debugInfo; 142 } 143 if (d != null) { 144 if ( p != null ) { 145 g.drawString(d, p.x*kScale, p.y*kScale+10); 146 } 147 else { 148 g.drawString(d, 5, 15); 149 } 150 } 151 } 152 153 int paintCount = 0; 154 void tickle() { 155 if (paintCount > kPaintThreshold) { 156 paintCount = 0; 157 repaint(); 158 try { 159 Thread.sleep(kPauseTime); 160 } catch (InterruptedException e) { 161 e.printStackTrace(); 162 } 163 } 164 else { 165 paintCount +=1; 166 } 167 168 } 169 170 // setters which also (occasionally) redisplay 171 public void setCurrEmpties(Vector<Rectangle> currEmpties) { 172 synchronized (this) { 173 this.currEmpties = currEmpties; 174 } 175 tickle(); 176 } 177 178 public void setCurrPoint(Point currPoint) { 179 synchronized (this) { 180 this.currPoint = currPoint; 181 } 182 tickle(); 183 } 184 185 public void setCurrRect(Rectangle currRect) { 186 synchronized (this) { 187 this.currRect = currRect; 188 } 189 tickle(); 190 } 191 192 public void setDebugCases(Vector<Rectangle> debugCases) { 193 synchronized (this) { 194 this.debugCases = debugCases; 195 } 196 tickle(); 197 } 198 199 public void setDebugInfo(String debugInfo) { 200 synchronized (this) { 201 this.debugInfo = debugInfo; 202 } 203 tickle(); 204 } 205 206 } 207 208 protected void setUp() throws Exception { 209 super.setUp(); 210 displayFrame = new Frame(); 211 displayFrame.setLocation(10,10); 212 displayFrame.add( displayArea ); 213 displayFrame.pack(); 214 } 215 216 protected void tearDown() throws Exception { 217 super.tearDown(); 218 displayFrame.dispose(); 219 } 220 221 public void xxtestSplit() throws Exception { 222 init(); 223 224 assertNotNull(displayFrame); 225 displayFrame.setVisible(true); 226 227 228 for (int i = 0; i < 200; i+=1) { 229 int ww = rand.nextInt(1+kBoolRect.width); 230 int hh = rand.nextInt(1+kBoolRect.height); 231 int xx = rand.nextInt(1+kBoolRect.width-ww); 232 int yy = rand.nextInt(1+kBoolRect.height-hh); 233 234 Rectangle probe = new Rectangle(xx, yy, ww, hh); 235 displayArea.setCurrRect(probe); 236 237 Point p = split(probe); 238 displayArea.setCurrPoint(p); 239 displayArea.repaint(); 240 // Thread.sleep(1250); 241 } 242 243 Thread.sleep(125); 244 } 245 246 public void testSubRects() throws Exception { 247 init(); 248 249 assertNotNull(displayFrame); 250 // displayFrame.setVisible(true); 251 252 for (int i = 0; i < 10; i+=1) { 253 int x = kBoolRect.x + rand.nextInt( kBoolRect.width); 254 int y = kBoolRect.y + rand.nextInt( kBoolRect.height); 255 256 Point p = new Point(x,y); 257 displayArea.setCurrPoint(p); 258 for (int kind =0; kind< 8; kind++) { 259 Vector<Rectangle> caseRects = divideRectForSubcase(kBoolRect, p, kind); 260 displayArea.setDebugInfo( kind + " " + caseRects.size() ); 261 displayArea.setDebugCases(caseRects); 262 } 263 } 264 265 displayArea.setCurrRect(kBoolRect); 266 displayArea.setDebugCases(null); 267 268 Thread.sleep(125); 269 } 270 271 public void testRecursiveStress() throws Exception { 272 init(); 273 274 assertNotNull(displayFrame); 275 displayFrame.setVisible(true); 276 277 Vector<Rectangle> answer = findEmptyRects(kBoolRect, 0); 278 assertTrue( answer.size() < (kBoolRect.width * kBoolRect.height) ); 279 280 //System.out.println(answer.size()); 281 282 displayArea.setCurrPoint(null); 283 displayArea.setCurrRect(kBoolRect); 284 displayArea.setDebugCases(null); 285 displayArea.setCurrEmpties(answer); 286 displayArea.setDebugInfo( ""+ answer.size() ); 287 displayArea.repaint(); 288 289 Thread.sleep(125); 290 } 291 292 // initialize the boolean field to random values, and load 1,1 empties into cache 293 void init() { 294 for(int i = 0; i < boolSpace.length; i+=1) { 295 for(int ii = 0; ii < boolSpace[i].length; ii+=1) { 296 boolean val = (rand.nextInt(100)<= kBooleanDensity); 297 boolSpace[i][ii] = val; 298 299 if (!val) { 300 Rectangle r = new Rectangle(i,ii,1,1); 301 Vector<Rectangle> v = new Vector<Rectangle>(); 302 v.add(r); 303 cache.put(r, v); 304 } 305 } 306 } 307 } 308 309 // text display of boolean field 310 void displayRaw() { 311 System.out.println(); 312 for(int i = 0; i < boolSpace.length; i+=1) { 313 for(int ii = 0; ii < boolSpace[i].length; ii+=1) { 314 System.out.print( boolSpace[i][ii] ? " " : "X" ); 315 } 316 System.out.println(); 317 } 318 } 319 320 321 // return an OK fracture point, or null if rectangle is empty 322 // algorithm is correct for any fracture point, but will converge faster for central points 323 Point split(Rectangle r) { 324 Point result = null; 325 326 int midx = r.x + r.width/2; 327 int midy = r.y + r.height/2; 328 int score = Integer.MAX_VALUE; 329 330 for(int xoff = 0; (xoff < r.width); xoff+=1) { 331 for(int yoff = 0; (yoff < r.height); yoff+=1) { 332 int x = r.x + xoff; 333 int y = r.y + yoff; 334 displayArea.setCurrPoint( new Point(x,y)); 335 336 if (boolSpace[x][y] ) { 337 int newscore = (x-midx)*(x-midx)+(y-midy)*(y-midy); 338 if (newscore < score) { 339 score = newscore; 340 result = new Point(x,y); 341 } 342 } 343 } 344 } 345 346 return result; 347 } 348 349 Vector<Rectangle> findEmptyRects( Rectangle r, int depth) throws Exception { 350 debug( depth, "findEmptyRects " + show(r)); 351 displayArea.setCurrEmpties(null); 352 displayArea.setCurrRect(r); 353 354 if (r.width <= 0 || r.height <= 0) { 355 debug( depth, "Unexpected rectagle :" + r); 356 return null ; 357 } 358 359 if ( cache.containsKey(r) ) { 360 debug( depth, "C :" + show(r) + "--" + show( cache.get(r))); 361 return cache.get(r); 362 } 363 364 if (r.width == 1 && r.height == 1) { 365 debug( depth, "F :" + show(r) + "--"); 366 assertTrue( boolSpace[r.x][r.y]); 367 return null; 368 } 369 370 // figure it out recursively 371 Vector<Rectangle> empties = new Vector<Rectangle>(); 372 373 Point p = split( r ); 374 displayArea.setCurrPoint(p); 375 376 if (p == null) { 377 debug( depth, "S :" + show(r) + "--" + show(r)); 378 empties.add(r); 379 } 380 else { 381 debug( depth, "point :" + p.x + " "+ p.y); 382 383 Vector<Rectangle> bestEmpties = null; 384 // iterate through the 8 possible cases 385 for (int kind =0; kind< 8; kind++) { 386 debug( depth, "case :" + kind); 387 Vector<Rectangle> caseRects = divideRectForSubcase(r, p, kind); 388 displayArea.setCurrRect(r); 389 displayArea.setCurrEmpties(null); 390 displayArea.setDebugInfo(""+caseRects.size()); 391 displayArea.setDebugCases(caseRects); 392 393 Vector<Rectangle> caseEmpties = new Vector<Rectangle>(); 394 for ( Rectangle caseRect : caseRects) { 395 Vector<Rectangle> subEmpties = findEmptyRects( caseRect, depth+1 ); 396 if (subEmpties != null) { 397 caseEmpties.addAll(subEmpties); 398 } 399 } 400 displayArea.setCurrEmpties(caseEmpties); 401 debug( depth, "totals :" + caseEmpties.size()); 402 403 404 if ( (bestEmpties == null) || (bestEmpties.size() > caseEmpties.size())) { 405 debug( depth, "new best! :" + caseEmpties.size()); 406 displayArea.setCurrEmpties(caseEmpties); 407 bestEmpties = caseEmpties; 408 } 409 410 if (caseRects.size() <= 2) { 411 break; // simple case, so exit early 412 } 413 } 414 415 debug(depth, "R : " + r + "--"+show(bestEmpties)); 416 empties = bestEmpties; 417 418 } 419 420 cache.put(r, empties); 421 return empties; 422 } 423 424 425 Vector<Rectangle> divideRectForSubcase(Rectangle r, Point p, int i ) { 426 Vector<Rectangle> fullCase = new Vector<Rectangle>(); 427 428 int topHeight = p.y - r.y; 429 int botHeight = (r.height - topHeight) -1; 430 int leftWidth = p.x - r.x; 431 int rightWidth = (r.width - leftWidth) -1; 432 433 switch(i) { 434 case 0: 435 fullCase.add(new Rectangle( r.x, r.y, r.width, topHeight)); 436 fullCase.add(new Rectangle( r.x, p.y+1, r.width, botHeight)); 437 fullCase.add(new Rectangle( r.x, p.y, leftWidth, 1)); 438 fullCase.add(new Rectangle( p.x+1, p.y, rightWidth, 1)); 439 break; 440 case 1: 441 fullCase.add(new Rectangle( r.x, r.y, leftWidth, r.height)); 442 fullCase.add(new Rectangle( p.x, r.y, 1, topHeight)); 443 fullCase.add(new Rectangle( p.x+1, r.y, rightWidth, r.height)); 444 fullCase.add(new Rectangle( p.x, p.y+1, 1, botHeight)); 445 break; 446 case 2: 447 fullCase.add(new Rectangle( r.x, r.y, leftWidth, topHeight+1)); 448 fullCase.add(new Rectangle( p.x, r.y, 1, topHeight)); 449 fullCase.add(new Rectangle( r.x, p.y+1, r.width, botHeight)); 450 fullCase.add(new Rectangle( p.x+1, r.y, rightWidth, topHeight+1)); 451 break; 452 case 3: 453 fullCase.add(new Rectangle( r.x, r.y, leftWidth, r.height)); 454 fullCase.add(new Rectangle( p.x, r.y, rightWidth+1, topHeight)); 455 fullCase.add(new Rectangle( p.x, p.y+1, rightWidth+1, botHeight)); 456 fullCase.add(new Rectangle( p.x+1, p.y, rightWidth, 1)); 457 break; 458 case 4: 459 fullCase.add(new Rectangle( r.x, r.y, r.width, topHeight)); 460 fullCase.add(new Rectangle( r.x, p.y, leftWidth, botHeight+1)); 461 fullCase.add(new Rectangle( p.x, p.y+1, 1, botHeight)); 462 fullCase.add(new Rectangle( p.x+1, p.y, rightWidth, botHeight+1)); 463 break; 464 case 5: 465 fullCase.add(new Rectangle( r.x, r.y, leftWidth+1, topHeight)); 466 fullCase.add(new Rectangle( r.x, p.y, leftWidth, 1)); 467 fullCase.add(new Rectangle( r.x, p.y+1, leftWidth+1, botHeight)); 468 fullCase.add(new Rectangle( p.x+1, r.y, rightWidth, r.height)); 469 break; 470 case 6: 471 fullCase.add(new Rectangle( r.x, r.y, leftWidth+1, topHeight)); 472 fullCase.add(new Rectangle( r.x, p.y, leftWidth, botHeight+1)); 473 fullCase.add(new Rectangle( p.x, p.y+1, rightWidth+1, botHeight)); 474 fullCase.add(new Rectangle( p.x+1, r.y, rightWidth, topHeight+1)); 475 break; 476 case 7: 477 fullCase.add(new Rectangle( r.x, r.y, leftWidth, topHeight+1)); 478 fullCase.add(new Rectangle( r.x, p.y+1, leftWidth+1, botHeight)); 479 fullCase.add(new Rectangle( p.x, r.y, rightWidth+1, topHeight)); 480 fullCase.add(new Rectangle( p.x+1, p.y, rightWidth, botHeight+1)); 481 break; 482 /* ### note that there are 8! more cases.... ### */ 483 484 default: 485 System.out.println("Unexpected case!"); break; 486 } 487 488 489 Vector<Rectangle> result = new Vector<Rectangle>(); 490 for (Rectangle rr : fullCase ) { 491 if (rr.width > 0 && rr.height > 0) { 492 result.add(rr); 493 } 494 } 495 return result; 496 497 } 498 499 // debugging utilities 500 static void debug( int depth, String s) { 501 if (kDebug) { 502 for(int i= 0; i<depth; i+=1) { 503 System.out.print("\t"); 504 System.out.println(s); 505 } 506 } 507 } 508 509 static String show( Rectangle r) { 510 return "["+r.x+","+r.y+" "+r.width+"x"+r.height+"]"; 511 } 512 513 static String show( Vector<Rectangle> rects) { 514 String result = "" + rects.size() + " "; 515 for( Rectangle r : rects) { 516 result = result + " " +show(r); 517 } 518 return result; 519 } 520 521 522 // boilerplate for running as a JUnit test 523 public static Test suite() { 524 return new TestSuite(CollectionStress01.class); 525 } 526 527 public static void main (String[] args) throws RuntimeException { 528 TestResult tr = junit.textui.TestRunner.run(suite()); 529 if((tr.errorCount() != 0) || (tr.failureCount() != 0)) { 530 throw new RuntimeException("### Unexpected JUnit errors or failures."); 531 } 532 } 533 } 534