fork download
  1. import java.util.*;
  2. import java.util.stream.*;
  3. import java.lang.*;
  4. import java.io.*;
  5. import static java.lang.Math.*;
  6.  
  7. class Util {
  8. public static IllegalArgumentException BadArgf(String fmt, Object... args) {
  9. return new IllegalArgumentException(String.format(fmt, args));
  10. };
  11.  
  12. public static String trace(Exception e) {
  13. e.printStackTrace(new PrintWriter(sw));
  14. return sw.toString();
  15. }
  16.  
  17. public static int rangecheck(int x, int min, int max, String what) {
  18. if (x < min || x > max) throw BadArgf("Bad %s: %d, should be [%d; %d].", what, x, min, max);
  19. return x;
  20. }
  21. }
  22.  
  23. class Bitfield2D {
  24. int[] data;
  25. int w, h;
  26. static final int SANE_BITS_LIMIT = 1_000_000_000;
  27. static final int SANE_DIMENSION_LIMIT = 100_000_000;
  28. static final int BASE = Integer.SIZE;
  29. // BASE expected to be power of 2, then
  30. // (bitno >> BASEP2) is faster version of (bitno / BASE), and
  31. // (bitno & BASEMASK) is faster version of (bitno % BASE).
  32. //
  33. // Compiler cannot optimize these operations as effectively as programmer can,
  34. // because they work differently for negative 'bitno' values, so it should check for negatives.
  35. // And it's only we who know that 'bitno' is never negative.
  36. //
  37. // Not sure if there is no danger of BASEP2 formula NOT resulting in compile-time constant,
  38. // but it appears to be OK.
  39. static final int BASEP2 = 32 - (Integer.numberOfLeadingZeros(BASE) + 1);
  40. static final int BASEMASK = BASE - 1;
  41.  
  42. public Bitfield2D(int w, int h) {
  43. validateSize(w, h);
  44. this.data = new int[(w*h + (BASE - 1)) >> BASEP2];
  45. this.w = w;
  46. this.h = h;
  47. }
  48.  
  49. static void validateSize(int w, int h) {
  50. if (w < 0 || h < 0 || w > SANE_DIMENSION_LIMIT || h > SANE_DIMENSION_LIMIT
  51. || w > 0 && h > SANE_BITS_LIMIT / w)
  52. throw Util.BadArgf("Bad bitfield size: %d×%d.", w, h);
  53. }
  54.  
  55. public static boolean validCoord(int w, int h, int x, int y) {
  56. return x >= 0 && y >= 0 && x < w && y < h;
  57. }
  58.  
  59. public static void validateCoord(int w, int h, int x, int y) {
  60. if (!validCoord(w, h, x, y))
  61. throw Util.BadArgf("Bad coord of bitfield %d×%d: (%d, %d).", w, h, x, y);
  62. }
  63.  
  64. static void validateRect(int fullw, int fullh, int x, int y, int w, int h) {
  65. if (x < 0 || y < 0 || w < 0 || h < 0 || x + w > fullw || y + h > fullh)
  66. throw Util.BadArgf("Bad rect of bitfield %d×%d: (%d, %d) ~ (%d, %d).",
  67. fullw, fullh, x, y, x+w, y+h);
  68. }
  69.  
  70. boolean rawget(int index) { return (data[index >> BASEP2] & (1 << (index & BASEMASK))) != 0; }
  71. void rawset(int index) { data[index >> BASEP2] |= 1 << (index & BASEMASK); }
  72. void rawunset(int index) { data[index >> BASEP2] &= ~(1 << (index & BASEMASK)); }
  73. void rawset(int index, boolean value) { if (value) rawset(index); else rawunset(index); }
  74.  
  75. public boolean get(int x, int y) { validateCoord(w, h, x, y); return rawget(y*w+x); }
  76. public void set(int x, int y) { validateCoord(w, h, x, y); rawset(y*w+x); }
  77. public void unset(int x, int y) { validateCoord(w, h, x, y); rawunset(y*w+x); }
  78. public void set(int x, int y, boolean value) { validateCoord(w, h, x, y); rawset(y*w+x, value); }
  79. public int width() { return w; }
  80. public int height() { return h; }
  81.  
  82. // For functions like or() that apply one bitfield to another,
  83. // reduces potentially out-of-borders input to its valid part.
  84. static class CoordWarden {
  85. int srcX, srcY, w, h, destX, destY;
  86.  
  87. CoordWarden(int destFullW, int destFullH, int srcFullW, int srcFullH,
  88. int aSrcX, int aSrcY, int aW, int aH, int aDestX, int aDestY) {
  89.  
  90. validateRect(srcFullW, srcFullH, aSrcX, aSrcY, aW, aH);
  91. srcX = aSrcX; srcY = aSrcY; w = aW; h = aH; destX = aDestX; destY = aDestY;
  92. if (destX < 0) { srcX -= destX; w += destX; destX = 0; }
  93. if (destX + w > destFullW) { w = destFullW - destX; }
  94. if (destY < 0) { srcY -= destY; h += destY; destY = 0; }
  95. if (destY + h > destFullH) { h = destFullH - destY; }
  96. }
  97. }
  98.  
  99. public void or(Bitfield2D b, int thisX, int thisY) {
  100. or(b, 0, 0, b.w, b.h, thisX, thisY);
  101. }
  102.  
  103. public void or(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
  104. var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
  105. int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
  106. for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w - ward.w, bOfs += b.w - ward.w)
  107. for (int dx = 0; dx < ward.w; dx++, thisOfs++, bOfs++)
  108. if (b.rawget(bOfs)) this.rawset(thisOfs);
  109. }
  110.  
  111. public void orBATCH(Bitfield2D b, int thisX, int thisY) {
  112. orBATCH(b, 0, 0, b.w, b.h, thisX, thisY);
  113. }
  114.  
  115. public void orBATCH(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
  116. apply(b, bx, by, w, h, thisX, thisY, (itemA, itemB) -> itemA | itemB);
  117. }
  118.  
  119. public void andNot(Bitfield2D b, int thisX, int thisY) {
  120. andNot(b, 0, 0, b.w, b.h, thisX, thisY);
  121. }
  122.  
  123. public void andNot(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
  124. var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
  125. int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
  126. for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w - ward.w, bOfs += b.w - ward.w)
  127. for (int dx = 0; dx < ward.w; dx++, thisOfs++, bOfs++)
  128. if (b.rawget(bOfs)) this.rawunset(thisOfs);
  129. }
  130.  
  131. public void andNotBATCH(Bitfield2D b, int thisX, int thisY) {
  132. andNotBATCH(b, 0, 0, b.w, b.h, thisX, thisY);
  133. }
  134.  
  135. public void andNotBATCH(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
  136. apply(b, bx, by, w, h, thisX, thisY, (itemA, itemB) -> itemA & ~itemB);
  137. }
  138.  
  139. @FunctionalInterface
  140. interface Operation {
  141. abstract int apply(int a, int b);
  142. }
  143.  
  144. void apply(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY, Operation op) {
  145. var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
  146. int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
  147. for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w, bOfs += b.w)
  148. applyOp(this.data, thisOfs, b.data, bOfs, ward.w, op);
  149. }
  150.  
  151. public Bitfield2D copyFrom(Bitfield2D b, int thisX, int thisY) {
  152. return copyFrom(b, 0, 0, b.w, b.h, thisX, thisY);
  153. }
  154.  
  155. public Bitfield2D copyFrom(Bitfield2D b, int bx, int by, int w, int h, int thisX, int thisY) {
  156. var ward = new CoordWarden(this.w, this.h, b.w, b.h, bx, by, w, h, thisX, thisY);
  157. int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
  158. for (int dy = 0; dy < ward.h; dy++, thisOfs += this.w, bOfs += b.w)
  159. copyBits(b.data, bOfs, this.data, thisOfs, ward.w);
  160. return this;
  161. }
  162.  
  163. static int readBitsSmall(int[] src, int srcPos, int count) {
  164. assert count > 0 && count < BASE;
  165. // 0 1 2 3 4 5 6
  166. // src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  167. // ^srcPos=5
  168. // ^srcPos+count=11
  169. int bits = src[srcPos >> BASEP2] >>> (srcPos & BASEMASK); // FGH
  170. if (count > BASE - (srcPos & BASEMASK))
  171. bits |= src[(srcPos >> BASEP2) + 1] << (BASE - (srcPos & BASEMASK)); // FGH | 000IJKLMNOP = FGHIJKLMNOP
  172. return bits & ((1 << count) - 1); // FGHIJK
  173. }
  174.  
  175. static void writeBits1(int bits, int[] dst, int dstPos, int count) {
  176. assert count > 0 && count < BASE - (dstPos & BASEMASK) && bits < 1 << count;
  177. // bits = qwer
  178. // 0 1 2 3 4 5 6
  179. // src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  180. // ^dstPos=3
  181. // ^dstPos+count=7
  182. dst[dstPos >> BASEP2] = dst[dstPos >> BASEP2] // ABCDEFGH
  183. & ~(((1 << count) - 1) << (dstPos & BASEMASK)) // ABC0000H
  184. | (bits << (dstPos & BASEMASK)); // ABCqwerH
  185. }
  186.  
  187. static void copyBits(int[] src, int srcPos, int[] dst, int dstPos, int count) {
  188. // Imagine BASE=8 (as if byte[] instead of int[]), srcPos = 3, dstPos = 2, count=25.
  189. //
  190. // 0 1 2 3 4 5 6
  191. // src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  192. // ^srcPos=3 ^srcPos+count=28
  193. // dst = ...............................................
  194. // 0 ^dstPos=2 2 3 4 5
  195. //
  196. // First we align dstPos to the multiple of BASE by reading
  197. // first BASE - dstPos%BASE bits with readBitsSmall() and writing them
  198. // into the first cell of dst[] with writeBits1.
  199. //
  200. // We'll get:
  201. // 0 1 2 3 4 5 6
  202. // src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  203. // ^srcPos=9 ^srcPos+count=28
  204. // dst = ..DEFGHI.......................................
  205. // 0 1 2 3 4 5
  206. // ^dstPos=8
  207. if ((dstPos & BASEMASK) != 0) {
  208. int n = min(count, BASE - (dstPos & BASEMASK));
  209. if (n > 0) writeBits1(readBitsSmall(src, srcPos, n), dst, dstPos, n);
  210. srcPos += n;
  211. dstPos += n;
  212. count -= n;
  213. }
  214.  
  215. // Now we fill whole cells while possible.
  216. // We'll get:
  217. // src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  218. // ^srcPos=25
  219. // ^srcPos+count=28
  220. // dst = ..DEFGHIJKLMNOPQRSTUVWXY.......................
  221. // 0 1 2 3 4 5
  222. // ^dstPos=24
  223. if (count >= BASE) {
  224. if ((srcPos & BASEMASK) == 0) {
  225. // Aligned case. This is not even an optimization, because 'int' shifts are restricted to 0..31.
  226. System.arraycopy(src, srcPos >> BASEP2, dst, dstPos >> BASEP2, count >> BASEP2);
  227. } else {
  228. // Unaligned case.
  229. // src: ...ABCDE FGH.....
  230. // ^srcPos
  231. // dst: ........
  232. for (int iSrc = srcPos >> BASEP2, iDst = dstPos >> BASEP2, iDstEd = iDst + (count >> BASEP2); iDst < iDstEd; iDst++, iSrc++)
  233. dst[iDst] =
  234. (src[iSrc] >>> (srcPos & BASEMASK)) // ABCDE
  235. | (src[iSrc + 1] << (BASE - (srcPos & BASEMASK))); // ABCDE | 00000FGH = ABCDEFGH
  236. }
  237. srcPos += count - (count & BASEMASK);
  238. dstPos += count - (count & BASEMASK);
  239. count &= BASEMASK;
  240. }
  241.  
  242. // Append the tail.
  243. if (count > 0)
  244. writeBits1(readBitsSmall(src, srcPos, count), dst, dstPos, count);
  245. }
  246.  
  247. // Same as copyBits() but with custom operation instead of copying.
  248. // Argument order changed both to emphasize modifying 'dst' and
  249. // to match the order in op.apply: (dst, src) that matters for noncommutative operations.
  250. // Speaking of it, the only reason why copyBits() has (src, srcPos, dst,
  251. // dstPos, count) order is consistency with System.arraycopy.
  252. static void applyOp(int[] dst, int dstPos, int[] src, int srcPos, int count, Operation op) {
  253. if ((dstPos & BASEMASK) != 0) {
  254. int n = min(count, BASE - (dstPos & BASEMASK));
  255. if (n > 0) writeBits1(op.apply(readBitsSmall(dst, dstPos, n), readBitsSmall(src, srcPos, n)), dst, dstPos, n);
  256. srcPos += n; dstPos += n; count -= n;
  257. }
  258. if (count >= BASE) {
  259. for (int iSrc = srcPos >> BASEP2, iDst = dstPos >> BASEP2, iDstEd = iDst + (count >> BASEP2); iDst < iDstEd; iDst++, iSrc++)
  260. dst[iDst] = op.apply(dst[iDst], (srcPos & BASEMASK) == 0 ? src[iSrc] : (src[iSrc] >>> (srcPos & BASEMASK)) | (src[iSrc + 1] << (BASE - (srcPos & BASEMASK))));
  261. srcPos += count - (count & BASEMASK); dstPos += count - (count & BASEMASK); count &= BASEMASK;
  262. }
  263. if (count > 0)
  264. writeBits1(op.apply(readBitsSmall(dst, dstPos, count), readBitsSmall(src, srcPos, count)), dst, dstPos, count);
  265. }
  266.  
  267. @Override
  268. public String toString() {
  269. return toString("##", "..");
  270. }
  271.  
  272. public String toString(String one, String zero) {
  273. var sb = new StringBuilder();
  274. for (int ofs = 0, y = 0; y < h; y++) {
  275. if (y > 0) sb.append("\n");
  276. for (int x = 0; x < w; x++, ofs++)
  277. sb.append(rawget(ofs) ? one : zero);
  278. }
  279. return sb.toString();
  280. }
  281.  
  282. static Bitfield2D parse(String src) { return parse(src, "##"); }
  283.  
  284. static Bitfield2D parse(String src, String one) {
  285. if (one.isEmpty()) throw Util.BadArgf("'one' can't be empty.");
  286. if (one.indexOf('\n') >= 0) throw Util.BadArgf("Bad 'one': \"%s\". Shouldn't contain EOL.", one);
  287. // Prepass for size.
  288. int w = 0, h = 0;
  289. for (int pos = 0; pos < src.length(); ) {
  290. int eolp = src.indexOf("\n", pos);
  291. if (eolp < 0) eolp = src.length();
  292. if ((eolp - pos) % one.length() != 0)
  293. throw Util.BadArgf("Line %d:\n%s\ncontains %d character%s, which is not multiple of len(%s) = %d.",
  294. h, src.substring(pos, eolp), eolp - pos, eolp - pos != 1 ? "s" : "", one, one.length());
  295. w = max(w, (eolp - pos) / one.length());
  296. h++;
  297. pos = eolp + "\n".length();
  298. }
  299. var r = new Bitfield2D(w, h);
  300.  
  301. for (int y = 0, x = 0, ofs = 0, pos = 0; pos < src.length(); )
  302. if (src.charAt(pos) == '\n') {
  303. ofs += r.w - x;
  304. x = 0; y++;
  305. pos += "\n".length();
  306. } else {
  307. if (src.startsWith(one, pos)) r.rawset(ofs);
  308. pos += one.length();
  309. x++; ofs++;
  310. }
  311. return r;
  312. }
  313.  
  314. public boolean[][] toBool2D() { return toBool2D(0, 0, w, h); }
  315. public boolean[][] toBool2D(int atx, int aty, int w, int h) {
  316. validateRect(this.w, this.h, atx, aty, w, h);
  317. boolean[][] r = Bool2D.create(w, h);
  318. for (int y = 0, ofs = aty * this.w + atx; y < h; y++, ofs += this.w - w)
  319. for (int x = 0; x < w; x++, ofs++)
  320. r[y][x] = rawget(ofs);
  321. return r;
  322. }
  323. }
  324.  
  325. class Bool2D {
  326. public static boolean[][] create(int w, int h) {
  327. Bitfield2D.validateSize(w, h);
  328. if (h == 0) throw Util.BadArgf("boolean[][] can't have zero height.");
  329. return new boolean[h][w];
  330. }
  331.  
  332. public static void or(boolean[][] it, boolean[][] b, int itX, int itY) {
  333. or(it, b, 0, 0, b[0].length, b.length, itX, itY);
  334. }
  335.  
  336. public static void or(boolean[][] it, boolean[][] b, int bx, int by, int w, int h, int itX, int itY) {
  337. var ward = new Bitfield2D.CoordWarden(it[0].length, it.length, b[0].length, b.length, bx, by, w, h, itX, itY);
  338. for (int dy = 0, iItY = ward.destY, iBy = ward.srcY; dy < ward.h; dy++, iItY++, iBy++)
  339. for (int dx = 0, iItX = ward.destX, iBx = ward.srcX; dx < ward.w; dx++, iItX++, iBx++)
  340. it[iItY][iItX] |= b[iBy][iBx];
  341. }
  342.  
  343. public static void andNot(boolean[][] it, boolean[][] b, int itX, int itY) {
  344. andNot(it, b, 0, 0, b[0].length, b.length, itX, itY);
  345. }
  346.  
  347. public static void andNot(boolean[][] it, boolean[][] b, int bx, int by, int w, int h, int itX, int itY) {
  348. var ward = new Bitfield2D.CoordWarden(it[0].length, it.length, b[0].length, b.length, bx, by, w, h, itX, itY);
  349. for (int dy = 0, iItY = ward.destY, iBy = ward.srcY; dy < ward.h; dy++, iItY++, iBy++)
  350. for (int dx = 0, iItX = ward.destX, iBx = ward.srcX; dx < ward.w; dx++, iItX++, iBx++)
  351. it[iItY][iItX] &= !b[iBy][iBx];
  352. }
  353.  
  354. public static void copyFrom(boolean[][] it, boolean[][] b, int itX, int itY) {
  355. copyFrom(it, b, 0, 0, b[0].length, b.length, itX, itY);
  356. }
  357.  
  358. public static void copyFrom(boolean[][] it, boolean[][] b, int bx, int by, int w, int h, int itX, int itY) {
  359. var ward = new Bitfield2D.CoordWarden(it[0].length, it.length, b[0].length, b.length, bx, by, w, h, itX, itY);
  360. if (ward.w <= 0) return;
  361. for (int dy = 0, iItY = ward.destY, iBy = ward.srcY; dy < ward.h; dy++, iItY++, iBy++)
  362. System.arraycopy(b[iBy], ward.srcX, it[iItY], ward.destX, ward.w);
  363. }
  364.  
  365. public static String toString(boolean[][] it) {
  366. return toString(it, "##", "..");
  367. }
  368.  
  369. public static String toString(boolean[][] it, String one, String zero) {
  370. return toBitfield2D(it).toString(one, zero);
  371. }
  372.  
  373. public static boolean[][] parse(String src) { return parse(src, "##"); }
  374. public static boolean[][] parse(String src, String one) {
  375. return Bitfield2D.parse(src, one).toBool2D();
  376. }
  377.  
  378. public static Bitfield2D toBitfield2D(boolean[][] it) { return toBitfield2D(it, 0, 0, it[0].length, it.length); }
  379. public static Bitfield2D toBitfield2D(boolean[][] it, int atx, int aty, int w, int h) {
  380. Bitfield2D.validateRect(it[0].length, it.length, atx, aty, w, h);
  381. var r = new Bitfield2D(w, h);
  382. for (int y = 0, rOfs = 0; y < h; y++)
  383. for (int x = 0; x < w; x++, rOfs++)
  384. if (it[y][x]) r.rawset(rOfs);
  385. return r;
  386. }
  387. }
  388.  
  389. class Ideone
  390. {
  391. // Auto-growing bitfield for procedural generation when resulting size is impractical to predict.
  392. static class ProcBitfield2D {
  393. Bitfield2D b = new Bitfield2D(0, 0);
  394. int ax, ay, bx, by, boundAx, boundAy, boundBx, boundBy;
  395.  
  396. void set(int x, int y) {
  397. if (boundAx == boundBx) {
  398. ax = x; ay = y; bx = x; by = y;
  399. boundAx = x; boundAy = y; boundBx = x; boundBy = y;
  400. }
  401. if (x < ax || y < ay || x >= bx || y >= by)
  402. grow(
  403. x < ax ? growStgy(ax - x, bx - ax) : 0,
  404. y < ay ? growStgy(ay - y, by - ay) : 0,
  405. x >= bx ? growStgy(x - bx + 1, bx - ax) : 0,
  406. y >= by ? growStgy(y - by + 1, by - ay) : 0);
  407. if (x < boundAx) boundAx = x;
  408. if (x >= boundBx) boundBx = x + 1;
  409. if (y < boundAy) boundAy = y;
  410. if (y >= boundBy) boundBy = y + 1;
  411. b.set(x-ax, y-ay);
  412. }
  413.  
  414. void unset(int x, int y) {
  415. if (x >= boundAx && x < boundBx && x >= boundAy && x < boundBy)
  416. b.unset(x-ax, y-ay);
  417. }
  418.  
  419. void set(int x, int y, boolean value) { if (value) set(x, y); else unset(x, y); }
  420.  
  421. static int growStgy(int atLeast, int present) {
  422. return max(max(8, present / 2), atLeast);
  423. }
  424.  
  425. void grow(int Lx, int Ly, int Rx, int Ry) {
  426. this.b = new Bitfield2D((bx - ax) + Lx + Rx, (by - ay) + Ly + Ry).copyFrom(
  427. this.b, boundAx - ax, boundAy - ay, boundBx - boundAx, boundBy - boundAy, (boundAx - ax) + Lx, (boundAy - ay) + Ly);
  428. ax -= Lx; ay -= Ly; bx += Rx; by += Ry;
  429. }
  430.  
  431. Bitfield2D bake() {
  432. return new Bitfield2D(boundBx - boundAx, boundBy - boundAy).copyFrom(
  433. this.b, boundAx - ax, boundAy - ay, boundBx - boundAx, boundBy - boundAy, 0, 0);
  434. }
  435. }
  436.  
  437. static Bitfield2D makeSpruce(int size) {
  438. Util.rangecheck(size, 1, 20, "size");
  439. var r = new ProcBitfield2D();
  440. int nSegs = 3+size/4, trunkWidth = 1+size/3*2;
  441. int y = 0;
  442. for (int iSeg = 0; iSeg < nSegs; iSeg++) {
  443. int maxSegWidth = trunkWidth + 2 * (size + iSeg * size / 2);
  444. int segWidth = iSeg == 0 ? 1 : trunkWidth + (size + iSeg)/3*2;
  445. for (; segWidth < maxSegWidth; segWidth += 2) {
  446. for (int x = -segWidth/2; x < (segWidth+1)/2; x++)
  447. r.set(x, y);
  448. y += 1;
  449. }
  450. }
  451. for (int yTrunk = 0; yTrunk < size; yTrunk++) {
  452. for (int x = -trunkWidth/2; x < (trunkWidth + 1)/2; x++)
  453. r.set(x, y);
  454. y++;
  455. }
  456. return r.bake();
  457. }
  458.  
  459. static Bitfield2D moon = Bitfield2D.parse(
  460. "....######..........\n" +
  461. "..####..............\n" +
  462. "####................\n" +
  463. "####................\n" +
  464. "####................\n" +
  465. "####..............##\n" +
  466. "######..........####\n" +
  467. "..################..\n" +
  468. "....############", "##");
  469.  
  470. static Bitfield2D paintBitfield2D() {
  471. var r = new Bitfield2D(50, 40);
  472. r.or(moon, 11, 4);
  473. r.or(makeSpruce(4), -1, 15);
  474. r.or(makeSpruce(3), 29, 17);
  475. r.or(makeSpruce(2), 18, 18);
  476. r.or(makeSpruce(5), 32, 16);
  477. var rand = new Random(7);
  478. for (int iSnowflake = 0; iSnowflake < 25; iSnowflake++) {
  479. int x = rand.nextInt(r.width()),
  480. y = min(r.height() - 1, (int) (r.height() * pow(rand.nextFloat(), 2.0)));
  481. r.set(x, y, !r.get(x, y));
  482. }
  483. return r;
  484. }
  485.  
  486. static boolean[][] paintBool2D() {
  487. var r = Bool2D.create(50, 40);
  488. Bool2D.or(r, moon.toBool2D(), 11, 4);
  489. Bool2D.or(r, makeSpruce(4).toBool2D(), -1, 15);
  490. Bool2D.or(r, makeSpruce(3).toBool2D(), 29, 17);
  491. Bool2D.or(r, makeSpruce(2).toBool2D(), 18, 18);
  492. Bool2D.or(r, makeSpruce(5).toBool2D(), 32, 16);
  493. var rand = new Random(7);
  494. for (int iSnowflake = 0; iSnowflake < 25; iSnowflake++) {
  495. int x = rand.nextInt(r[0].length),
  496. y = min(r.length - 1, (int) (r.length * pow(rand.nextFloat(), 2.0)));
  497. r[y][x] = !r[y][x];
  498. }
  499. return r;
  500. }
  501.  
  502. static Bitfield2D makeRandomBitfield(int w, int h, Random rand) {
  503. var r = new Bitfield2D(w, h);
  504. for (int i = 0; i < w*h/5; i++) {
  505. r.set(rand.nextInt(w), rand.nextInt(h));
  506. }
  507. return r;
  508. }
  509.  
  510. static enum Op { OR, AND_NOT, COPY }
  511.  
  512. static class PlanItem {
  513. Op op;
  514. int rectIndex;
  515. int rectX, rectY, w, h, destX, destY;
  516.  
  517. static PlanItem random(Random rand, Bitfield2D canvas, Bitfield2D[] rects) {
  518. var r = new PlanItem();
  519. r.op = Op.values()[rand.nextInt(Op.values().length)];
  520.  
  521. r.rectIndex = rand.nextInt(rects.length);
  522. var rect = rects[r.rectIndex];
  523. if (rand.nextInt(2) == 0) {
  524. r.rectX = rand.nextInt(rect.width());
  525. r.rectY = rand.nextInt(rect.height());
  526. r.w = 1 + rand.nextInt(rect.width() - r.rectX);
  527. r.h = 1 + rand.nextInt(rect.height() - r.rectY);
  528. r.destX = -rect.width() + rand.nextInt(canvas.width() + rect.width() + 1);
  529. r.destY = -rect.height() + rand.nextInt(canvas.height() + rect.height() + 1);
  530. } else {
  531. r.rectX = 0;
  532. r.rectY = 0;
  533. r.w = rect.width();
  534. r.h = rect.height();
  535. r.destX = rand.nextInt(canvas.width() - rect.width());
  536. r.destY = rand.nextInt(canvas.height() - rect.height());
  537. }
  538. return r;
  539. }
  540. }
  541.  
  542. static void fusedBenchmarkTest() {
  543. Random rand = new Random(0);
  544. Bitfield2D bitCanvas = new Bitfield2D(1000, 1000);
  545. boolean[][] boolCanvas = Bool2D.create(bitCanvas.width(), bitCanvas.height());
  546. Bitfield2D bitCanvasBATCH = new Bitfield2D(bitCanvas.width(), bitCanvas.height());
  547.  
  548. Bitfield2D[] bitRects = new Bitfield2D[1000];
  549. boolean[] [][] boolRects = new boolean[bitRects.length][][];
  550. for (int i = 0; i < bitRects.length; i++) {
  551. bitRects[i] = makeRandomBitfield(1 + rand.nextInt(bitCanvas.width()/10), 1 + rand.nextInt(bitCanvas.height()/10), rand);
  552. boolRects[i] = bitRects[i].toBool2D();
  553. }
  554.  
  555. PlanItem[] plan = new PlanItem[600000];
  556. for (int i = 0; i < plan.length; i++)
  557. plan[i] = PlanItem.random(rand, bitCanvas, bitRects);
  558.  
  559. final int TRIALS = 3;
  560. for (int iTrial = 0; iTrial < TRIALS; iTrial++) {
  561. System.out.printf("%s--- TRIAL %d/%d ---\n", iTrial > 0 ? "\n" : "", 1 + iTrial, TRIALS);
  562. double refTime = benchmark("boolean[][]", () -> {
  563. for (int i = 0; i < plan.length; i++) {
  564. switch (plan[i].op) {
  565. case OR: Bool2D.or(boolCanvas, boolRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  566. case AND_NOT: Bool2D.andNot(boolCanvas, boolRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  567. case COPY: default: Bool2D.copyFrom(boolCanvas, boolRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  568. }
  569. }
  570. return boolCanvas;
  571. });
  572.  
  573. benchmark("Bitfield2D (per-bit)", () -> {
  574. for (int i = 0; i < plan.length; i++) {
  575. switch (plan[i].op) {
  576. case OR: bitCanvas.or(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  577. case AND_NOT: bitCanvas.andNot(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  578. case COPY: default: bitCanvas.copyFrom(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  579. }
  580. }
  581. return bitCanvas;
  582. }, refTime);
  583.  
  584. benchmark("Bitfield2D (bitsizeof(int) batches)", () -> {
  585. for (int i = 0; i < plan.length; i++) {
  586. switch (plan[i].op) {
  587. case OR: bitCanvasBATCH.orBATCH(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  588. case AND_NOT: bitCanvasBATCH.andNotBATCH(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  589. case COPY: default: bitCanvasBATCH.copyFrom(bitRects[plan[i].rectIndex], plan[i].rectX, plan[i].rectY, plan[i].w, plan[i].h, plan[i].destX, plan[i].destY); break;
  590. }
  591. }
  592. return bitCanvasBATCH;
  593. }, refTime);
  594.  
  595. if (iTrial == 0) {
  596. String ref = Bool2D.toString(boolCanvas);
  597. if (!bitCanvas.toString().equals(ref))
  598. throw new RuntimeException("Bitfield2D(per-bit) and boolean[][] do differ.");
  599. if (!bitCanvasBATCH.toString().equals(ref))
  600. throw new RuntimeException("Bitfield2D(bitsizeof(int) batches) and boolean[][] do differ.");
  601. }
  602. }
  603. }
  604.  
  605. @FunctionalInterface
  606. interface BenchmarkPayload {
  607. abstract Object run();
  608. }
  609.  
  610. static double benchmark(String title, BenchmarkPayload payload) {
  611. return benchmark(title, payload, -1);
  612. }
  613.  
  614. static double benchmark(String title, BenchmarkPayload payload, double refTime) {
  615. long start = System.nanoTime();
  616. Object keepAlive = payload.run();
  617. double time = (System.nanoTime() - start) * 1e-9;
  618. System.out.println(String.format(
  619. "%s: %.2f s%s",
  620. title, time, refTime >= 0 ? String.format(" (%s)", judgement(time, refTime)) : "", keepAlive));
  621. return time;
  622. }
  623.  
  624. static String judgement(double time, double refTime) {
  625. final double absThreshold = 0.3, relThreshold = .1;
  626.  
  627. return time <= absThreshold || refTime <= absThreshold ?
  628. String.format("can't judge, min. required time is %.1f s", absThreshold) :
  629. Math.max(time, refTime) > (1 + relThreshold) * Math.min(time, refTime) ?
  630. String.format("%.0f%% %s",
  631. Math.abs((time < refTime ? refTime / time : time / refTime) - 1) * 100,
  632. time < refTime ? "faster" : "slower") :
  633. String.format("no observable difference, min. %.0f%% required", relThreshold * 100);
  634. }
  635.  
  636. public static void main (String[] args) throws java.lang.Exception
  637. {
  638. try {
  639. var image = paintBitfield2D();
  640. var im2 = paintBool2D();
  641. if (!image.toString().equals(Bool2D.toString(im2))) {
  642. throw new RuntimeException(String.format("paintBool2D =\n%s\n\nyet paintBitfield2D =\n%s", im2, image));
  643. }
  644. fusedBenchmarkTest();
  645. System.out.printf("\n%s\n\n", image);
  646. } catch (Exception e) {
  647. System.out.println(Util.trace(e));
  648. }
  649. }
  650. }
Success #stdin #stdout 8.55s 146744KB
stdin
Standard input is empty
stdout
--- TRIAL 1/3 ---
boolean[][]: 0.69 s
Bitfield2D (per-bit): 1.77 s (155% slower)
Bitfield2D (bitsizeof(int) batches): 0.43 s (60% faster)

--- TRIAL 2/3 ---
boolean[][]: 0.63 s
Bitfield2D (per-bit): 1.65 s (162% slower)
Bitfield2D (bitsizeof(int) batches): 0.41 s (53% faster)

--- TRIAL 3/3 ---
boolean[][]: 0.57 s
Bitfield2D (per-bit): 1.64 s (190% slower)
Bitfield2D (bitsizeof(int) batches): 0.33 s (71% faster)

..............##....................................##................##............##..............
....................................................................................................
................##..................................................................................
..............................................##....................................................
..........................######....................................................................
........................####........................................................................
......................####..........................................................................
......................####..........................................................................
......................####..........................................................................
......................####..............##..................##......................................
......................######..........####..........................................................
........................################............................................................
##........................############..............................................................
..............................................................................##....................
....................................................##............##................................
..................##..........................##..........##........................................
................######..................................................##..............##..........
..............##########..............................................##..............######........
............##########..##................##........................######..........########........
..........##################........##..######....................##########......########..####....
..............##########................######..................######..######..##################..
............##############............##########..................##########..######################
..........##################........##..######..................##..##########....##############....
........######################........##########..............####################################..
......##########################....##############................##########..######################
............##############................##....................####################################
..........##################..............##..................######################################
........######################..............................####################################....
......##########################....##....................########################################..
....##############################..................................######....######################
..##################################................................######..########################
............##############..........................................################################
..........##################............................................############################
........######################........................................##############################
......##################..######....................................################################
....##############################................................................##############....
####################################............................................##################..
######################################............##..........................######################
########################################....................................########################
................######....................................................##########################