001package squidpony.squidmath;
002
003import squidpony.ArrayTools;
004import squidpony.LZSEncoding;
005import squidpony.StringKit;
006import squidpony.squidgrid.zone.MutableZone;
007import squidpony.squidgrid.zone.Zone;
008
009import java.io.Serializable;
010import java.util.*;
011
012import static squidpony.squidmath.CrossHash.Water.*;
013
014/**
015 * Region encoding of on/off information about areas using bitsets; uncompressed (fatty), but fast (greased lightning).
016 * This can handle any size of 2D data, and is not strictly limited to 256x256 as CoordPacker is. It stores several long
017 * arrays and uses each bit in one of those numbers to represent a single point, though sometimes this does waste bits
018 * if the height of the area this encodes is not a multiple of 64 (if you store a 80x64 map, this uses 80 longs; if you
019 * store an 80x65 map, this uses 160 longs, 80 for the first 64 rows and 80 more to store the next row). It's much
020 * faster than CoordPacker at certain operations (anything that expands or retracts an area, including
021 * {@link #expand()}), {@link #retract()}), {@link #fringe()}), {@link #surface()}, and {@link #flood(GreasedRegion)},
022 * and slightly faster on others, like {@link #and(GreasedRegion)} (called intersectPacked() in CoordPacker) and
023 * {@link #or(GreasedRegion)} (called unionPacked() in CoordPacker).
024 * <br>
025 * Each GreasedRegion is mutable, and instance methods typically modify that instance and return it for chaining. There
026 * are exceptions, usually where multiple GreasedRegion values are returned and the instance is not modified.
027 * <br>
028 * Typical usage involves constructing a GreasedRegion from some input data, like a char[][] for a map or a double[][]
029 * from DijkstraMap, and modifying it spatially with expand(), retract(), flood(), etc. It's common to mix in data from
030 * other GreasedRegions with and() (which gets the intersection of two GreasedRegions and stores it in one), or() (which
031 * is like and() but for the union), xor() (like and() but for exclusive or, finding only cells that are on in exactly
032 * one of the two GreasedRegions), and andNot() (which can be considered the "subtract another region from me" method).
033 * There are 8-way (Chebyshev distance) variants on all of the spatial methods, and methods without "8way" in the name
034 * are either 4-way (Manhattan distance) or not affected by distance measurement. Once you have a GreasedRegion, you may
035 * want to:
036 * <ul>
037 *     <li>get a single random point from it (use {@link #singleRandom(IRNG)}),</li>
038 *     <li>get several random points from it with random sampling (use {@link #randomPortion(IRNG, int)}),</li>
039 *     <li>mutate the current GreasedRegion to keep random points from it with random sampling (use {@link #randomRegion(IRNG, int)}),</li>
040 *     <li>get random points that are likely to be separated (use {@link #mixedRandomSeparated(double, int, long)} with
041 *     a random long for the last parameter, or {@link #quasiRandomSeparated(double)} if you don't want a random seed),</li>
042 *     <li>do what any of the above "separated" methods can do, but mutate the current GreasedRegion (use
043 *     {@link #mixedRandomRegion(double, int, long)} or {@link #quasiRandomRegion(double, int)}),</li>
044 *     <li>get all points from it (use {@link #asCoords()} to get a Coord array, or produce a 2D array of the contents
045 *     with {@link #decode()} or {@link #toChars(char, char)}),</li>
046 *     <li>use it to modify other 2D data, such as with {@link #mask(char[][], char)},
047 *     {@link #inverseMask(char[][], char)}, {@link #writeDoubles(double[][], double)}, or
048 *     {@link #writeIntsInto(int[][], int)}, along with variations on those.</li>
049 * </ul>
050 * <br>
051 * You may also want to produce some 2D data from one or more GreasedRegions, as with {@link #sum(GreasedRegion...)} or
052 * {@link #toChars()}. The most effective techniques regarding GreasedRegion involve multiple methods, like getting a
053 * few random points from an existing GreasedRegion representing floor tiles in a dungeon with
054 * {@link #randomRegion(IRNG, int)}, then finding a random expansion of those initial points with
055 * {@link #spill(GreasedRegion, int, IRNG)}, giving the original GreasedRegion of floor tiles as the first argument.
056 * This could be used to position puddles of water or toxic waste in a dungeon level, while still keeping the starting
057 * points and finished points within the boundaries of valid (floor) cells. If you wanted to place something like mold
058 * that can be on floors or on cells immediately adjacent to floors (like walls), you could call {@link #expand()} on
059 * the floor tiles before calling spill, allowing the spill to spread onto non-floor cells that are next to floors.
060 * <br>
061 * For efficiency, you can place one GreasedRegion into another (typically a temporary value that is no longer needed
062 * and can be recycled) using {@link #remake(GreasedRegion)}, or give the information that would normally be used to
063 * construct a fresh GreasedRegion to an existing one of the same dimensions with {@link #refill(boolean[][])} or any
064 * of the overloads of refill(). These re-methods don't do as much work as a constructor does if the width and height
065 * of their argument are identical to their current width and height, and don't create more garbage for the GC.
066 * <br>
067 * Created by Tommy Ettinger on 6/24/2016.
068 */
069public class GreasedRegion extends Zone.Skeleton implements Collection<Coord>, Serializable, MutableZone {
070    private static final long serialVersionUID = 0;
071    private static final SobolQRNG sobol = new SobolQRNG(2);
072
073    public long[] data;
074    public int height;
075    public int width;
076    private int ySections;
077    private long yEndMask;
078    private boolean tallied;
079    private int ct;
080    private int[] counts;
081
082    private void tally()
083    {
084        ct = 0;
085        for (int i = 0, tmp; i < counts.length; i++) {
086            tmp = Long.bitCount(data[i]);
087            counts[i] = tmp == 0 ? 0 : (ct += tmp);
088        }
089        tallied = true;
090    }
091
092    /**
093     * Constructs an empty 64x64 GreasedRegion.
094     * GreasedRegions are mutable, so you can add to this with insert() or insertSeveral(), among others.
095     */
096    public GreasedRegion()
097    {
098        width = 64;
099        height = 64;
100        ySections = 1;
101        yEndMask = -1L;
102        data = new long[64];
103        counts = new int[64];
104        ct = 0;
105        tallied = true;
106    }
107
108    /**
109     * Constructs a GreasedRegion with the given rectangular boolean array, with width of bits.length and height of
110     * bits[0].length, any value of true considered "on", and any value of false considered "off."
111     * @param bits a rectangular 2D boolean array where true is on and false is off
112     */
113    public GreasedRegion(final boolean[][] bits)
114    {
115        width = bits.length;
116        height = bits[0].length;
117        ySections = (height + 63) >> 6;
118        yEndMask = -1L >>> (64 - (height & 63));
119        data = new long[width * ySections];
120        for (int x = 0, xs = 0; x < width; x++, xs += ySections) {
121            for (int y = 0; y < height; y++) {
122                if(bits[x][y]) data[xs + (y >> 6)] |= 1L << (y & 63);
123            }
124        }
125        counts = new int[width * ySections];
126        tallied = false;
127    }
128    /**
129     * Reassigns this GreasedRegion with the given rectangular boolean array, reusing the current data storage (without
130     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
131     * this are always cleared, then any value of true in map is considered "on", and any value of false in map is
132     * considered "off."
133     * @param map a rectangular 2D boolean array where true is on and false is off
134     * @return this for chaining
135     */
136    public GreasedRegion refill(final boolean[][] map) {
137        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
138            Arrays.fill(data, 0L);
139            for (int x = 0; x < width; x++) {
140                for (int y = 0; y < height; y++) {
141                    data[x * ySections + (y >> 6)] |= (map[x][y] ? 1L : 0L) << (y & 63);
142                }
143            }
144            tallied = false;
145            return this;
146        } else {
147            width = (map == null) ? 0 : map.length;
148            height = (map == null || map.length <= 0) ? 0 : map[0].length;
149            ySections = (height + 63) >> 6;
150            yEndMask = -1L >>> (64 - (height & 63));
151            data = new long[width * ySections];
152            for (int x = 0; x < width; x++) {
153                for (int y = 0; y < height; y++) {
154                    if(map[x][y]) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
155                }
156            }
157            counts = new int[width * ySections];
158            tallied = false;
159            return this;
160        }
161    }
162
163    /**
164     * Constructs a GreasedRegion with the given rectangular char array, with width of map.length and height of
165     * map[0].length, any value that equals yes is considered "on", and any other value considered "off."
166     * @param map a rectangular 2D char array where yes is on and everything else is off
167     * @param yes which char to encode as "on"
168     */
169    public GreasedRegion(final char[][] map, final char yes)
170    {
171        width = map.length;
172        height = map[0].length;
173        ySections = (height + 63) >> 6;
174        yEndMask = -1L >>> (64 - (height & 63));
175        data = new long[width * ySections];
176        for (int x = 0; x < width; x++) {
177            for (int y = 0; y < height; y++) {
178                if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
179            }
180        }
181        counts = new int[width * ySections];
182        tallied = false;
183    }
184    /**
185     * Reassigns this GreasedRegion with the given rectangular char array, reusing the current data storage (without
186     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
187     * this are always cleared, then any value that equals yes is considered "on", and any other value considered "off."
188     * @param map a rectangular 2D char array where yes is on and everything else is off
189     * @param yes which char to encode as "on"
190     * @return this for chaining
191     */
192    public GreasedRegion refill(final char[][] map, final char yes) {
193        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
194            Arrays.fill(data, 0L);
195            for (int x = 0; x < width; x++) {
196                for (int y = 0; y < height; y++) {
197                    data[x * ySections + (y >> 6)] |= ((map[x][y] == yes) ? 1L : 0L) << (y & 63);
198                }
199            }
200            tallied = false;
201            return this;
202        } else {
203            width = (map == null) ? 0 : map.length;
204            height = (map == null || map.length <= 0) ? 0 : map[0].length;
205            ySections = (height + 63) >> 6;
206            yEndMask = -1L >>> (64 - (height & 63));
207            data = new long[width * ySections];
208            for (int x = 0; x < width; x++) {
209                for (int y = 0; y < height; y++) {
210                    if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
211                }
212            }
213            counts = new int[width * ySections];
214            tallied = false;
215            return this;
216        }
217    }
218
219    /**
220     * Constructs a GreasedRegion with the given rectangular char array, with width of map.length and height of
221     * map[0].length, any value that equals yes is considered "on", and any other value considered "off."
222     * @param map a rectangular 2D char array where yes is on and everything else is off
223     * @param yes which char to encode as "on"
224     */
225    public GreasedRegion(final char[][] map, final char[] yes)
226    {
227        width = map.length;
228        height = map[0].length;
229        ySections = (height + 63) >> 6;
230        yEndMask = -1L >>> (64 - (height & 63));
231        data = new long[width * ySections];
232        for (int x = 0; x < width; x++) {
233            for (int y = 0; y < height; y++) {
234                for(char e : yes)
235                {
236                    if(map[x][y] == e) {
237                        data[x * ySections + (y >> 6)] |= 1L << (y & 63);
238                        break;
239                    }
240                }
241            }
242        }
243        counts = new int[width * ySections];
244        tallied = false;
245    }
246    /**
247     * Reassigns this GreasedRegion with the given rectangular char array, reusing the current data storage (without
248     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
249     * this are always cleared, then any value that equals yes is considered "on", and any other value considered "off."
250     * @param map a rectangular 2D char array where yes is on and everything else is off
251     * @param yes which char to encode as "on"
252     * @return this for chaining
253     */
254    public GreasedRegion refill(final char[][] map, final char[] yes) {
255        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
256            Arrays.fill(data, 0L);
257            for (int x = 0; x < width; x++) {
258                for (int y = 0; y < height; y++) {
259                    for(char e : yes)
260                    {
261                        if(map[x][y] == e) {
262                            data[x * ySections + (y >> 6)] |= 1L << (y & 63);
263                            break;
264                        }
265                    }
266                }
267            }
268            tallied = false;
269            return this;
270        } else {
271            width = (map == null) ? 0 : map.length;
272            height = (map == null || map.length <= 0) ? 0 : map[0].length;
273            ySections = (height + 63) >> 6;
274            yEndMask = -1L >>> (64 - (height & 63));
275            data = new long[width * ySections];
276            for (int x = 0; x < width; x++) {
277                for (int y = 0; y < height; y++) {
278                    for(char e : yes)
279                    {
280                        if(map[x][y] == e) {
281                            data[x * ySections + (y >> 6)] |= 1L << (y & 63);
282                            break;
283                        }
284                    }
285                }
286            }
287            counts = new int[width * ySections];
288            tallied = false;
289            return this;
290        }
291    }
292
293    /**
294     * Weird constructor that takes a String array, _as it would be printed_, so each String is a row and indexing would
295     * be done with y, x instead of the normal x, y.
296     * @param map String array (as printed, not the normal storage) where each String is a row
297     * @param yes the char to consider "on" in the GreasedRegion
298     */
299    public GreasedRegion(final String[] map, final char yes)
300    {
301        height = map.length;
302        width = map[0].length();
303        ySections = (height + 63) >> 6;
304        yEndMask = -1L >>> (64 - (height & 63));
305        data = new long[width * ySections];
306        for (int x = 0; x < width; x++) {
307            for (int y = 0; y < height; y++) {
308                if(map[y].charAt(x) == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
309            }
310        }
311        counts = new int[width * ySections];
312        tallied = false;
313    }
314
315    /**
316     * Weird refill method that takes a String array, _as it would be printed_, so each String is a row and indexing
317     * would be done with y, x instead of the normal x, y.
318     * @param map String array (as printed, not the normal storage) where each String is a row
319     * @param yes the char to consider "on" in the GreasedRegion
320     * @return this for chaining
321     */
322    public GreasedRegion refill(final String[] map, final char yes) {
323        if (map != null && map.length > 0 && height == map.length && width == map[0].length()) {
324            Arrays.fill(data, 0L);
325            for (int x = 0; x < width; x++) {
326                for (int y = 0; y < height; y++) {
327                    data[x * ySections + (y >> 6)] |= ((map[y].charAt(x) == yes) ? 1L : 0L) << (y & 63);
328                }
329            }
330            tallied = false;
331            return this;
332        } else {
333            height = (map == null) ? 0 : map.length;
334            width = (map == null || map.length <= 0) ? 0 : map[0].length();
335            ySections = (height + 63) >> 6;
336            yEndMask = -1L >>> (64 - (height & 63));
337            data = new long[width * ySections];
338            for (int x = 0; x < width; x++) {
339                for (int y = 0; y < height; y++) {
340                    if(map[y].charAt(y) == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
341                }
342            }
343            counts = new int[width * ySections];
344            tallied = false;
345            return this;
346        }
347    }
348    /**
349     * Constructs a GreasedRegion with the given rectangular int array, with width of map.length and height of
350     * map[0].length, any value that equals yes is considered "on", and any other value considered "off."
351     * @param map a rectangular 2D int array where an int == yes is on and everything else is off
352     * @param yes which int to encode as "on"
353     */
354    public GreasedRegion(final int[][] map, final int yes)
355    {
356        width = map.length;
357        height = map[0].length;
358        ySections = (height + 63) >> 6;
359        yEndMask = -1L >>> (64 - (height & 63));
360        data = new long[width * ySections];
361        for (int x = 0; x < width; x++) {
362            for (int y = 0; y < height; y++) {
363                if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
364            }
365        }
366        counts = new int[width * ySections];
367        tallied = false;
368    }
369    /**
370     * Reassigns this GreasedRegion with the given rectangular int array, reusing the current data storage (without
371     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
372     * this are always cleared, then any value that equals yes is considered "on", and any other value considered "off."
373     * @param map a rectangular 2D int array where an int == yes is on and everything else is off
374     * @param yes which int to encode as "on"
375     * @return this for chaining
376     */
377    public GreasedRegion refill(final int[][] map, final int yes) {
378        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
379            Arrays.fill(data, 0L);
380            for (int x = 0; x < width; x++) {
381                for (int y = 0; y < height; y++) {
382                    data[x * ySections + (y >> 6)] |= ((map[x][y] == yes) ? 1L : 0L) << (y & 63);
383                }
384            }
385            tallied = false;
386            return this;
387        } else {
388            width = (map == null) ? 0 : map.length;
389            height = (map == null || map.length <= 0) ? 0 : map[0].length;
390            ySections = (height + 63) >> 6;
391            yEndMask = -1L >>> (64 - (height & 63));
392            data = new long[width * ySections];
393            for (int x = 0; x < width; x++) {
394                for (int y = 0; y < height; y++) {
395                    if(map[x][y] == yes) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
396                }
397            }
398            counts = new int[width * ySections];
399            tallied = false;
400            return this;
401        }
402    }
403
404    /**
405     * Constructs this GreasedRegion using an int[][], treating cells as on if they are greater than or equal to lower
406     * and less than upper, or off otherwise.
407     * @param map an int[][] that should have some ints between lower and upper
408     * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower
409     * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper
410     */
411    public GreasedRegion(final int[][] map, final int lower, final int upper)
412    {
413        width = map.length;
414        height = map[0].length;
415        ySections = (height + 63) >> 6;
416        yEndMask = -1L >>> (64 - (height & 63));
417        data = new long[width * ySections];
418        int[] column;
419        for (int x = 0; x < width; x++) {
420            column = map[x];
421            for (int y = 0; y < height; y++) {
422                if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
423            }
424        }
425        counts = new int[width * ySections];
426        tallied = false;
427    }
428
429    /**
430     * Reassigns this GreasedRegion with the given rectangular int array, reusing the current data storage (without
431     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
432     * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than
433     * upper, or off otherwise.
434     * @param map a rectangular 2D int array that should have some values between lower and upper
435     * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower
436     * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper
437     * @return this for chaining
438     */
439    public GreasedRegion refill(final int[][] map, final int lower, final int upper) {
440        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
441            Arrays.fill(data, 0L);
442            int[] column;
443            for (int x = 0; x < width; x++) {
444                column = map[x];
445                for (int y = 0; y < height; y++) {
446                    data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63);
447                }
448            }
449            tallied = false;
450            return this;
451        } else {
452            width = (map == null) ? 0 : map.length;
453            height = (map == null || map.length <= 0) ? 0 : map[0].length;
454            ySections = (height + 63) >> 6;
455            yEndMask = -1L >>> (64 - (height & 63));
456            data = new long[width * ySections];
457            int[] column;
458            for (int x = 0; x < width; x++) {
459                column = map[x];
460                for (int y = 0; y < height; y++) {
461                    if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
462                }
463            }
464            counts = new int[width * ySections];
465            tallied = false;
466            return this;
467        }
468    }
469
470    /**
471     * Constructs this GreasedRegion using a byte[][], treating cells as on if they are greater than or equal to lower
472     * and less than upper, or off otherwise.
473     * @param map a byte[][] that should have some bytes between lower and upper
474     * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower
475     * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper
476     */
477    public GreasedRegion(final byte[][] map, final int lower, final int upper)
478    {
479        width = map.length;
480        height = map[0].length;
481        ySections = (height + 63) >> 6;
482        yEndMask = -1L >>> (64 - (height & 63));
483        data = new long[width * ySections];
484        byte[] column;
485        for (int x = 0; x < width; x++) {
486            column = map[x];
487            for (int y = 0; y < height; y++) {
488                if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
489            }
490        }
491        counts = new int[width * ySections];
492        tallied = false;
493    }
494
495    /**
496     * Reassigns this GreasedRegion with the given rectangular byte array, reusing the current data storage (without
497     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
498     * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than
499     * upper, or off otherwise.
500     * @param map a rectangular 2D byte array that should have some values between lower and upper
501     * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower
502     * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper
503     * @return this for chaining
504     */
505    public GreasedRegion refill(final byte[][] map, final int lower, final int upper) {
506        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
507            Arrays.fill(data, 0L);
508            byte[] column;
509            for (int x = 0; x < width; x++) {
510                column = map[x];
511                for (int y = 0; y < height; y++) {
512                    data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63);
513                }
514            }
515            tallied = false;
516            return this;
517        } else {
518            width = (map == null) ? 0 : map.length;
519            height = (map == null || map.length <= 0) ? 0 : map[0].length;
520            ySections = (height + 63) >> 6;
521            yEndMask = -1L >>> (64 - (height & 63));
522            data = new long[width * ySections];
523            byte[] column;
524            for (int x = 0; x < width; x++) {
525                column = map[x];
526                for (int y = 0; y < height; y++) {
527                    if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
528                }
529            }
530            counts = new int[width * ySections];
531            tallied = false;
532            return this;
533        }
534    }
535
536
537    /**
538     * Constructs this GreasedRegion using a short[][], treating cells as on if they are greater than or equal to lower
539     * and less than upper, or off otherwise.
540     * @param map a short[][] that should have some shorts between lower and upper
541     * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower
542     * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper
543     */
544    public GreasedRegion(final short[][] map, final int lower, final int upper)
545    {
546        width = map.length;
547        height = map[0].length;
548        ySections = (height + 63) >> 6;
549        yEndMask = -1L >>> (64 - (height & 63));
550        data = new long[width * ySections];
551        short[] column;
552        for (int x = 0; x < width; x++) {
553            column = map[x];
554            for (int y = 0; y < height; y++) {
555                if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
556            }
557        }
558        counts = new int[width * ySections];
559        tallied = false;
560    }
561
562    /**
563     * Reassigns this GreasedRegion with the given rectangular short array, reusing the current data storage (without
564     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
565     * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than
566     * upper, or off otherwise.
567     * @param map a rectangular 2D short array that should have some values between lower and upper
568     * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower
569     * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper
570     * @return this for chaining
571     */
572    public GreasedRegion refill(final short[][] map, final int lower, final int upper) {
573        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
574            Arrays.fill(data, 0L);
575            short[] column;
576            for (int x = 0; x < width; x++) {
577                column = map[x];
578                for (int y = 0; y < height; y++) {
579                    data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63);
580                }
581            }
582            tallied = false;
583            return this;
584        } else {
585            width = (map == null) ? 0 : map.length;
586            height = (map == null || map.length <= 0) ? 0 : map[0].length;
587            ySections = (height + 63) >> 6;
588            yEndMask = -1L >>> (64 - (height & 63));
589            data = new long[width * ySections];
590            short[] column;
591            for (int x = 0; x < width; x++) {
592                column = map[x];
593                for (int y = 0; y < height; y++) {
594                    if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
595                }
596            }
597            counts = new int[width * ySections];
598            tallied = false;
599            return this;
600        }
601    }
602    /**
603     * Constructs this GreasedRegion using a double[][] (typically one generated by
604     * {@link squidpony.squidai.DijkstraMap}) that only stores two relevant states:  an "on" state for values less than
605     * or equal to upperBound (inclusive), and an "off" state for anything else.
606     * @param map a double[][] that probably relates in some way to DijkstraMap.
607     * @param upperBound upper inclusive; any double greater than this will be off, any others will be on
608     */
609    public GreasedRegion(final double[][] map, final double upperBound)
610    {
611        width = map.length;
612        height = map[0].length;
613        ySections = (height + 63) >> 6;
614        yEndMask = -1L >>> (64 - (height & 63));
615        data = new long[width * ySections];
616        for (int x = 0; x < width; x++) {
617            for (int y = 0; y < height; y++) {
618                if(map[x][y] <= upperBound)
619                    data[x * ySections + (y >> 6)] |= 1L << (y & 63);
620            }
621        }
622        counts = new int[width * ySections];
623        tallied = false;
624    }
625    /**
626     * Reassigns this GreasedRegion with the given rectangular double array, reusing the current data storage (without
627     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
628     * this are always cleared, then cells are treated as on if they are less than or equal to upperBound, or off
629     * otherwise.
630     * @param map a rectangular 2D double array that should usually have some values less than or equal to upperBound
631     * @param upperBound upper bound, inclusive; all on cells will have values in map that are less than or equal to this
632     * @return this for chaining
633     */
634    public GreasedRegion refill(final double[][] map, final double upperBound) {
635        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
636            Arrays.fill(data, 0L);
637            for (int x = 0; x < width; x++) {
638                for (int y = 0; y < height; y++) {
639                    if(map[x][y] <= upperBound) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
640                }
641            }
642            tallied = false;
643            return this;
644        } else {
645            width = (map == null) ? 0 : map.length;
646            height = (map == null || map.length <= 0) ? 0 : map[0].length;
647            ySections = (height + 63) >> 6;
648            yEndMask = -1L >>> (64 - (height & 63));
649            data = new long[width * ySections];
650            for (int x = 0; x < width; x++) {
651                for (int y = 0; y < height; y++) {
652                    if(map[x][y] <= upperBound) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
653                }
654            }
655            counts = new int[width * ySections];
656            tallied = false;
657            return this;
658        }
659    }
660
661    /**
662     * Constructs this GreasedRegion using a double[][] (typically one generated by
663     * {@link squidpony.squidai.DijkstraMap}) that only stores two relevant states:  an "on" state for values between
664     * lowerBound (inclusive) and upperBound (exclusive), and an "off" state for anything else.
665     * @param map a double[][] that probably relates in some way to DijkstraMap.
666     * @param lowerBound lower inclusive; any double lower than this will be off, any equal to or greater than this,
667     *                   but less than upper, will be on
668     * @param upperBound upper exclusive; any double greater than or equal to this this will be off, any doubles both
669     *                   less than this and equal to or greater than lower will be on
670     */
671    public GreasedRegion(final double[][] map, final double lowerBound, final double upperBound)
672    {
673        width = map.length;
674        height = map[0].length;
675        ySections = (height + 63) >> 6;
676        yEndMask = -1L >>> (64 - (height & 63));
677        data = new long[width * ySections];
678        for (int x = 0; x < width; x++) {
679            for (int y = 0; y < height; y++) {
680                if(map[x][y] >= lowerBound && map[x][y] < upperBound)
681                    data[x * ySections + (y >> 6)] |= 1L << (y & 63);
682            }
683        }
684        counts = new int[width * ySections];
685        tallied = false;
686    }
687    /**
688     * Reassigns this GreasedRegion with the given rectangular double array, reusing the current data storage (without
689     * extra allocations) if this.width == map.length and this.height == map[0].length. The current values stored in
690     * this are always cleared, then cells are treated as on if they are greater than or equal to lower and less than
691     * upper, or off otherwise.
692     * @param map a rectangular 2D double array that should have some values between lower and upper
693     * @param lower lower bound, inclusive; all on cells will have values in map that are at least equal to lower
694     * @param upper upper bound, exclusive; all on cells will have values in map that are less than upper
695     * @return this for chaining
696     */
697    public GreasedRegion refill(final double[][] map, final double lower, final double upper) {
698        if (map != null && map.length > 0 && width == map.length && height == map[0].length) {
699            Arrays.fill(data, 0L);
700            double[] column;
701            for (int x = 0; x < width; x++) {
702                column = map[x];
703                for (int y = 0; y < height; y++) {
704                    data[x * ySections + (y >> 6)] |= ((column[y] >= lower && column[y] < upper) ? 1L : 0L) << (y & 63);
705                }
706            }
707            tallied = false;
708            return this;
709        } else {
710            width = (map == null) ? 0 : map.length;
711            height = (map == null || map.length <= 0) ? 0 : map[0].length;
712            ySections = (height + 63) >> 6;
713            yEndMask = -1L >>> (64 - (height & 63));
714            data = new long[width * ySections];
715            double[] column;
716            for (int x = 0; x < width; x++) {
717                column = map[x];
718                for (int y = 0; y < height; y++) {
719                    if(column[y] >= lower && column[y] < upper) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
720                }
721            }
722            counts = new int[width * ySections];
723            tallied = false;
724            return this;
725        }
726    }
727
728    /**
729     * Constructs this GreasedRegion using a double[][] that only stores two relevant states: an "on" state for values
730     * between lowerBound (inclusive) and upperBound (exclusive), and an "off" state for anything else. This variant
731     * scales the input so each "on" position in map produces a 2x2 on area if scale is 2, a 3x3 area if scale is 3, and
732     * so on.
733     * @param map a double[][]; depending on scale, the GreasedRegion may have different width and height
734     * @param lowerBound lower inclusive; any double lower than this will be off, any equal to or greater than this,
735     *                   but less than upper, will be on
736     * @param upperBound upper exclusive; any double greater than or equal to this this will be off, any doubles both
737     *                   less than this and equal to or greater than lower will be on
738     * @param scale      the size of the square of cells in this that each "on" value in map will correspond to
739     */
740    public GreasedRegion(final double[][] map, final double lowerBound, final double upperBound, int scale)
741    {
742        scale = Math.min(63, Math.max(1, scale));
743        int baseWidth = map.length, baseHeight = map[0].length;
744        width =  baseWidth * scale;
745        height = baseHeight * scale;
746        ySections = (height + 63) >> 6;
747        yEndMask = -1L >>> (64 - (height & 63));
748        data = new long[width * ySections];
749        long shape = (1L << scale) - 1L, leftover;
750        for (int bx = 0, x = 0; bx < baseWidth; bx++, x += scale) {
751            for (int by = 0, y = 0; by < baseHeight; by++, y += scale) {
752                if(map[bx][by] >= lowerBound && map[bx][by] < upperBound) {
753                    for (int i = 0; i < scale; i++) {
754                        data[(x + i) * ySections + (y >> 6)] |= shape << (y & 63);
755                        if((leftover = (y + scale - 1 & 63) + 1) < (y & 63) + 1 && (y + leftover >> 6) < ySections)
756                        {
757                            data[(x + i) * ySections + (y >> 6) + 1] |= (1L << leftover) - 1L;
758                        }
759                    }
760                }
761            }
762        }
763        counts = new int[width * ySections];
764        tallied = false;
765    }
766    /**
767     * Reassigns this GreasedRegion with the given rectangular double array, reusing the current data storage (without
768     * extra allocations) if {@code this.width == map.length * scale && this.height == map[0].length * scale}. The
769     * current values stored in this are always cleared, then cells are treated as on if they are greater than or equal
770     * to lower and less than upper, or off otherwise, before considering scaling. This variant scales the input so each
771     * "on" position in map produces a 2x2 on area if scale is 2, a 3x3 area if scale is 3, and so on.
772     * @param map a double[][]; depending on scale, the GreasedRegion may have different width and height
773     * @param lowerBound lower inclusive; any double lower than this will be off, any equal to or greater than this,
774     *                   but less than upper, will be on
775     * @param upperBound upper exclusive; any double greater than or equal to this this will be off, any doubles both
776     *                   less than this and equal to or greater than lower will be on
777     * @param scale      the size of the square of cells in this that each "on" value in map will correspond to
778     * @return this for chaining
779     */
780    public GreasedRegion refill(final double[][] map, final double lowerBound, final double upperBound, int scale) {
781        scale = Math.min(63, Math.max(1, scale));
782        if (map != null && map.length > 0 && width == map.length * scale && height == map[0].length * scale) {
783            Arrays.fill(data, 0L);
784            double[] column;
785            int baseWidth = map.length, baseHeight = map[0].length;
786            long shape = (1L << scale) - 1L, leftover;
787            for (int bx = 0, x = 0; bx < baseWidth; bx++, x += scale) {
788                column = map[bx];
789                for (int by = 0, y = 0; by < baseHeight; by++, y += scale) {
790                    if(column[by] >= lowerBound && column[by] < upperBound) {
791                        for (int i = 0; i < scale; i++) {
792                            data[(x + i) * ySections + (y >> 6)] |= shape << (y & 63);
793                            if((leftover = (y + scale - 1 & 63) + 1) < (y & 63) + 1 && (y + leftover >> 6) < ySections)
794                            {
795                                data[(x + i) * ySections + (y >> 6) + 1] |= (1L << leftover) - 1L;
796                            }
797                        }
798                    }
799                }
800            }
801            tallied = false;
802            return this;
803        } else {
804            int baseWidth = (map == null) ? 0 : map.length,
805                    baseHeight = (map == null || map.length <= 0) ? 0 : map[0].length;
806            width =  baseWidth * scale;
807            height = baseHeight * scale;
808            ySections = (height + 63) >> 6;
809            yEndMask = -1L >>> (64 - (height & 63));
810            data = new long[width * ySections];
811            long shape = (1L << scale) - 1L, leftover;
812            for (int bx = 0, x = 0; bx < baseWidth; bx++, x += scale) {
813                for (int by = 0, y = 0; by < baseHeight; by++, y += scale) {
814                    if(map[bx][by] >= lowerBound && map[bx][by] < upperBound) {
815                        for (int i = 0; i < scale; i++) {
816                            data[(x + i) * ySections + (y >> 6)] |= shape << (y & 63);
817                            if((leftover = (y + scale - 1 & 63)) < y && y < height - leftover)
818                            {
819                                data[(x + i) * ySections + (y >> 6) + 1] |= (1L << leftover) - 1L;
820                            }
821                        }
822                    }
823                }
824            }
825            counts = new int[width * ySections];
826            tallied = false;
827            return this;
828        }
829    }
830
831    /**
832     * Constructs a GreasedRegion with the given 1D boolean array, with the given width and height, where an [x][y]
833     * position is obtained from bits given an index n with x = n / height, y = n % height, any value of true
834     * considered "on", and any value of false considered "off."
835     * @param bits a 1D boolean array where true is on and false is off
836     * @param width the width of the desired GreasedRegion; width * height should equal bits.length
837     * @param height the height of the desired GreasedRegion; width * height should equal bits.length
838     */
839    public GreasedRegion(final boolean[] bits, final int width, final int height)
840    {
841        this.width = width;
842        this.height = height;
843        ySections = (height + 63) >> 6;
844        yEndMask = -1L >>> (64 - (height & 63));
845        data = new long[width * ySections];
846        for (int a = 0, x = 0, y = 0; a < bits.length; a++, x = a / height, y = a % height) {
847            if(bits[a]) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
848        }
849        counts = new int[width * ySections];
850        tallied = false;
851    }
852    /**
853     * Reassigns this GreasedRegion with the given 1D boolean array, reusing the current data storage (without
854     * extra allocations) if this.width == width and this.height == height, where an [x][y]
855     * position is obtained from bits given an index n with x = n / height, y = n % height, any value of true
856     * considered "on", and any value of false considered "off."
857     * @param bits a 1D boolean array where true is on and false is off
858     * @param width the width of the desired GreasedRegion; width * height should equal bits.length
859     * @param height the height of the desired GreasedRegion; width * height should equal bits.length
860     * @return this for chaining
861     */
862    public GreasedRegion refill(final boolean[] bits, final int width, final int height) {
863        if (bits != null && this.width == width && this.height == height) {
864            Arrays.fill(data, 0L);
865            for (int a = 0, x = 0, y = 0; a < bits.length; a++, x = a / height, y = a % height) {
866                data[x * ySections + (y >> 6)] |= (bits[a] ? 1L : 0L) << (y & 63);
867            }
868            tallied = false;
869            return this;
870        } else {
871            this.width = (bits == null || width < 0) ? 0 : width;
872            this.height = (bits == null || bits.length <= 0 || height < 0) ? 0 : height;
873            ySections = (this.height + 63) >> 6;
874            yEndMask = -1L >>> (64 - (this.height & 63));
875            data = new long[this.width * ySections];
876            if(bits != null) {
877                for (int a = 0, x = 0, y = 0; a < bits.length; a++, x = a / this.height, y = a % this.height) {
878                    if (bits[a]) data[x * ySections + (y >> 6)] |= 1L << (y & 63);
879                }
880            }
881            counts = new int[width * ySections];
882            tallied = false;
883            return this;
884        }
885    }
886
887    /**
888     * Constructor for an empty GreasedRegion of the given width and height.
889     * GreasedRegions are mutable, so you can add to this with insert() or insertSeveral(), among others.
890     * @param width the maximum width for the GreasedRegion
891     * @param height the maximum height for the GreasedRegion
892     */
893    public GreasedRegion(final int width, final int height)
894    {
895        this.width = width;
896        this.height = height;
897        ySections = (height + 63) >> 6;
898        yEndMask = -1L >>> (64 - (height & 63));
899        data = new long[width * ySections];
900        counts = new int[width  * ySections];
901        ct = 0;
902        tallied = true;
903    }
904
905    /**
906     * If this GreasedRegion has the same width and height passed as parameters, this acts the same as {@link #empty()},
907     * makes no allocations, and returns this GreasedRegion with its contents all "off"; otherwise, this does allocate
908     * a differently-sized amount of internal data to match the new width and height, sets the fields to all match the
909     * new width and height, and returns this GreasedRegion with its new width and height, with all contents "off". This
910     * is meant for cases where a GreasedRegion may be reused effectively, but its size may not always be the same.
911     * @param width the width to potentially resize this GreasedRegion to
912     * @param height the height to potentially resize this GreasedRegion to
913     * @return this GreasedRegion, always with all contents "off", and with the height and width set.
914     */
915    public GreasedRegion resizeAndEmpty(final int width, final int height) {
916        if (width == this.width && height == this.height) {
917            Arrays.fill(data, 0L);
918            Arrays.fill(counts, 0);
919            ct = 0;
920            tallied = true;
921        } else {
922            this.width = Math.max(width, 0);
923            this.height = Math.max(height, 0);
924            ySections = (this.height + 63) >> 6;
925            yEndMask = -1L >>> (64 - (this.height & 63));
926            data = new long[this.width * ySections];
927            counts = new int[this.width * ySections];
928            ct = 0;
929            tallied = true;
930        }
931        return this;
932
933    }
934
935    /**
936     * Constructor for a GreasedRegion that contains a single "on" cell, and has the given width and height.
937     * Note that to avoid confusion with the constructor that takes multiple Coord values, this takes the single "on"
938     * Coord first, while the multiple-Coord constructor takes its vararg or array of Coords last.
939     * @param single the one (x,y) point to store as "on" in this GreasedRegion
940     * @param width the maximum width for the GreasedRegion
941     * @param height the maximum height for the GreasedRegion
942     */
943    public GreasedRegion(final Coord single, final int width, final int height)
944    {
945        this.width = width;
946        this.height = height;
947        ySections = (height + 63) >> 6;
948        yEndMask = -1L >>> (64 - (height & 63));
949        data = new long[width * ySections];
950        counts = new int[width * ySections];
951        if(single.x < width && single.y < height && single.x >= 0 && single.y >= 0)
952        {
953            data[ct = single.x * ySections + (single.y >> 6)] |= 1L << (single.y & 63);
954            counts[ct] = 1;
955            ct = 1;
956            tallied = true;
957        }
958        else
959        {
960            ct = 0;
961            tallied = true;
962        }
963    }
964
965    /**
966     * Constructor for a GreasedRegion that can have several "on" cells specified, and has the given width and height.
967     * Note that to avoid confusion with the constructor that takes one Coord value, this takes the vararg or array of
968     * Coords last, while the single-Coord constructor takes its one Coord first.
969     * @param width the maximum width for the GreasedRegion
970     * @param height the maximum height for the GreasedRegion
971     * @param points an array or vararg of Coord to store as "on" in this GreasedRegion
972     */
973    public GreasedRegion(final int width, final  int height, final Coord... points)
974    {
975        this.width = width;
976        this.height = height;
977        ySections = (height + 63) >> 6;
978        yEndMask = -1L >>> (64 - (height & 63));
979        data = new long[width * ySections];
980        if(points != null)
981        {
982            for (int i = 0, x, y; i < points.length; i++) {
983                x = points[i].x;
984                y = points[i].y;
985                if(x < width && y < height && x >= 0 && y >= 0)
986                    data[x * ySections + (y >> 6)] |= 1L << (y & 63);
987            }
988        }
989        counts = new int[width * ySections];
990        tallied = false;
991    }
992
993    /**
994     * Constructor for a GreasedRegion that can have several "on" cells specified, and has the given width and height.
995     * Note that to avoid confusion with the constructor that takes one Coord value, this takes the Iterable of
996     * Coords last, while the single-Coord constructor takes its one Coord first.
997     * @param width the maximum width for the GreasedRegion
998     * @param height the maximum height for the GreasedRegion
999     * @param points an array or vararg of Coord to store as "on" in this GreasedRegion
1000     */
1001    public GreasedRegion(final int width, final int height, final Iterable<Coord> points)
1002    {
1003        this.width = width;
1004        this.height = height;
1005        ySections = (height + 63) >> 6;
1006        yEndMask = -1L >>> (64 - (height & 63));
1007        data = new long[width * ySections];
1008        if(points != null) {
1009            int x, y;
1010            for (Coord c : points) {
1011                x = c.x;
1012                y = c.y;
1013                if (x < width && y < height && x >= 0 && y >= 0)
1014                    data[x * ySections + (y >> 6)] |= 1L << (y & 63);
1015            }
1016        }
1017        counts = new int[width * ySections];
1018        tallied = false;
1019
1020    }
1021
1022    /**
1023     * Constructor for a random GreasedRegion of the given width and height, typically assigning approximately half of
1024     * the cells in this to "on" and the rest to off. A RandomnessSource can be slightly more efficient than an RNG when
1025     * you're making a lot of calls on it.
1026     * @param random a RandomnessSource that should have a good nextLong() method; DiverRNG is excellent but Lathe32RNG is faster on GWT (only there) 
1027     * @param width the maximum width for the GreasedRegion
1028     * @param height the maximum height for the GreasedRegion
1029     */
1030    public GreasedRegion(final RandomnessSource random, final int width, final int height)
1031    {
1032        this.width = width;
1033        this.height = height;
1034        ySections = (height + 63) >> 6;
1035        yEndMask = -1L >>> (64 - (height & 63));
1036        data = new long[width * ySections];
1037        for (int i = 0; i < width * ySections; i++) {
1038            data[i] = random.nextLong();
1039        }
1040        if(ySections > 0 && yEndMask != -1) {
1041            for (int a = ySections - 1; a < data.length; a += ySections) {
1042                data[a] &= yEndMask;
1043            }
1044        }
1045        counts = new int[width * ySections];
1046        tallied = false;
1047    }
1048    /**
1049     * Reassigns this GreasedRegion by filling it with random values from random, reusing the current data storage
1050     * (without extra allocations) if this.width == width and this.height == height, and typically assigning
1051     * approximately half of the cells in this to "on" and the rest to off. A RandomnessSource can be slightly more
1052     * efficient than an RNG when you're making a lot of calls on it.
1053     * @param random a RandomnessSource that should have a good nextLong() method; DiverRNG is excellent but Lathe32RNG is faster on GWT (only there) 
1054     * @param width the width of the desired GreasedRegion
1055     * @param height the height of the desired GreasedRegion
1056     * @return this for chaining
1057     */
1058    public GreasedRegion refill(final RandomnessSource random, final int width, final int height) {
1059        if (random != null){
1060            if(this.width == width && this.height == height) {
1061                for (int i = 0; i < width * ySections; i++) {
1062                    data[i] = random.nextLong();
1063                }
1064            } else {
1065                this.width = Math.max(width, 0);
1066                this.height = Math.max(height, 0);
1067                ySections = (this.height + 63) >> 6;
1068                yEndMask = -1L >>> (64 - (this.height & 63));
1069                data = new long[this.width * ySections];
1070                for (int i = 0; i < this.width * ySections; i++) {
1071                    data[i] = random.nextLong();
1072                }
1073                counts = new int[this.width * ySections];
1074            }
1075            if(ySections > 0 && yEndMask != -1) {
1076                for (int a = ySections - 1; a < data.length; a += ySections) {
1077                    data[a] &= yEndMask;
1078                }
1079            }
1080            tallied = false;
1081        }
1082        return this;
1083    }
1084
1085    /**
1086     * Constructor for a random GreasedRegion of the given width and height, typically assigning approximately half of
1087     * the cells in this to "on" and the rest to off. A RandomnessSource can be slightly more efficient than an RNG when
1088     * you're making a lot of calls on it, so you may prefer {@link #GreasedRegion(RandomnessSource, int, int)}.
1089     * @param random an IRNG, such as an RNG, that this will use to generate its contents
1090     * @param width the maximum width for the GreasedRegion
1091     * @param height the maximum height for the GreasedRegion
1092     */
1093    public GreasedRegion(final IRNG random, final int width, final int height)
1094    {
1095        this.width = width;
1096        this.height = height;
1097        ySections = (height + 63) >> 6;
1098        yEndMask = -1L >>> (64 - (height & 63));
1099        data = new long[width * ySections];
1100        for (int i = 0; i < width * ySections; i++) {
1101            data[i] = random.nextLong();
1102        }
1103        if(ySections > 0 && yEndMask != -1) {
1104            for (int a = ySections - 1; a < data.length; a += ySections) {
1105                data[a] &= yEndMask;
1106            }
1107        }
1108        counts = new int[width * ySections];
1109        tallied = false;
1110    }
1111    /**
1112     * Reassigns this GreasedRegion by filling it with random values from random, reusing the current data storage
1113     * (without extra allocations) if this.width == width and this.height == height, and typically assigning
1114     * approximately half of the cells in this to "on" and the rest to off.
1115     * @param random an IRNG that should have a good nextLong() method; an RNG constructed with the default RandomnessSource will be fine
1116     * @param width the width of the desired GreasedRegion
1117     * @param height the height of the desired GreasedRegion
1118     * @return this for chaining
1119     */
1120    public GreasedRegion refill(final IRNG random, final int width, final int height) {
1121        if (random != null){
1122            if(this.width == width && this.height == height) {
1123                for (int i = 0; i < width * ySections; i++) {
1124                    data[i] = random.nextLong();
1125                }
1126            } else {
1127                this.width = Math.max(width, 0);
1128                this.height = Math.max(height, 0);
1129                ySections = (this.height + 63) >> 6;
1130                yEndMask = -1L >>> (64 - (this.height & 63));
1131                data = new long[this.width * ySections];
1132                for (int i = 0; i < this.width * ySections; i++) {
1133                    data[i] = random.nextLong();
1134                }
1135                counts = new int[width * ySections];
1136            }
1137            if(ySections > 0 && yEndMask != -1) {
1138                for (int a = ySections - 1; a < data.length; a += ySections) {
1139                    data[a] &= yEndMask;
1140                }
1141            }
1142            tallied = false;
1143        }
1144        return this;
1145    }
1146
1147    /**
1148     * Constructor for a random GreasedRegion of the given width and height, trying to set the given fraction of cells
1149     * to on. Depending on the value of fraction, this makes between 0 and 6 calls to the nextLong() method of random's
1150     * internal RandomnessSource, per 64 cells of this GreasedRegion (if height is not a multiple of 64, round up to get
1151     * the number of calls this makes). As such, this sacrifices the precision of the fraction to obtain significantly
1152     * better speed than generating one random number per cell, although the precision is probably good enough (fraction
1153     * is effectively rounded down to the nearest multiple of 0.015625, and clamped between 0.0 and 1.0). The parameter
1154     * {@code random} can be an object like a {@link DiverRNG}, an {@link RNG} backed by a well-distributed
1155     * RandomnessSource like its default, DiverRNG, a {@link GWTRNG} (especially if you target GWT, where it will
1156     * perform much better than most alternatives), or any of various other RandomnessSource implementations that
1157     * distribute bits well for {@link RandomnessSource#nextLong()}, but should not be intentionally-biased RNGs like
1158     * {@link DharmaRNG} or {@link EditRNG}, nor double-based QRNGs like {@link VanDerCorputQRNG} or {@link SobolQRNG}.
1159     * @param random a RandomnessSource that should produce high-quality long values, like the defaults for {@link RNG}
1160     * @param fraction between 0.0 and 1.0 (clamped), only considering a precision of 1/64.0 (0.015625) between steps
1161     * @param width the maximum width for the GreasedRegion
1162     * @param height the maximum height for the GreasedRegion
1163     */
1164    public GreasedRegion(final RandomnessSource random, final double fraction, final int width, final int height)
1165    {
1166        this.width = width;
1167        this.height = height;
1168        int bitCount = (int) (fraction * 64);
1169        ySections = (height + 63) >> 6;
1170        yEndMask = -1L >>> (64 - (height & 63));
1171        data = new long[width * ySections];
1172        for (int i = 0; i < width * ySections; i++) {
1173            data[i] = approximateBits(random, bitCount);
1174        }
1175        if(ySections > 0 && yEndMask != -1) {
1176            for (int a = ySections - 1; a < data.length; a += ySections) {
1177                data[a] &= yEndMask;
1178            }
1179        }
1180        counts = new int[width * ySections];
1181        tallied = false;
1182    }
1183    /**
1184     * Reassigns this GreasedRegion randomly, reusing the current data storage (without extra allocations) if this.width
1185     * == width and this.height == height, while trying to set the given fraction of cells to on. Depending on the value
1186     * of fraction, this makes between 0 and 6 calls to the nextLong() method of random's internal RandomnessSource, per
1187     * 64 cells of this GreasedRegion (if height is not a multiple of 64, round up to get the number of calls this
1188     * makes). As such, this sacrifices the precision of the fraction to obtain significantly better speed than
1189     * generating one random number per cell, although the precision is probably good enough (fraction is effectively
1190     * rounded down to the nearest multiple of 0.015625, and clamped between 0.0 and 1.0). The parameter {@code random}
1191     * can be an object like a {@link DiverRNG}, an {@link RNG} backed by a well-distributed RandomnessSource like its
1192     * default, DiverRNG, a {@link GWTRNG} (especially if you target GWT, where it will perform much better than most
1193     * alternatives), or any of various other RandomnessSource implementations that distribute bits well for
1194     * {@link RandomnessSource#nextLong()}, but should not be intentionally-biased RNGs like {@link DharmaRNG} or
1195     * {@link EditRNG}, nor double-based QRNGs like {@link VanDerCorputQRNG} or {@link SobolQRNG}.
1196     * @param random a RandomnessSource that should produce high-quality long values, like the defaults for {@link RNG}
1197     * @param fraction between 0.0 and 1.0 (clamped), only considering a precision of 1/64.0 (0.015625) between steps
1198     * @param width the maximum width for the GreasedRegion
1199     * @param height the maximum height for the GreasedRegion
1200     * @return this for chaining
1201     */
1202    public GreasedRegion refill(final RandomnessSource random, final double fraction, final int width, final int height) {
1203        if (random != null){
1204            int bitCount = (int) (fraction * 64);
1205            if(this.width == width && this.height == height) {
1206                for (int i = 0; i < width * ySections; i++) {
1207                    data[i] = approximateBits(random, bitCount);
1208                }
1209            } else {
1210                this.width = Math.max(width, 0);
1211                this.height = Math.max(height, 0);
1212                ySections = (this.height + 63) >> 6;
1213                yEndMask = -1L >>> (64 - (this.height & 63));
1214                data = new long[this.width * ySections];
1215                for (int i = 0; i < this.width * ySections; i++) {
1216                    data[i] = approximateBits(random, bitCount);
1217                }
1218                counts = new int[width * ySections];
1219            }
1220            if(ySections > 0 && yEndMask != -1) {
1221                for (int a = ySections - 1; a < data.length; a += ySections) {
1222                    data[a] &= yEndMask;
1223                }
1224            }
1225        }
1226        tallied = false;
1227        return this;
1228    }
1229
1230    /**
1231     * Copy constructor that takes another GreasedRegion and copies all of its data into this new one.
1232     * If you find yourself frequently using this constructor and assigning it to the same variable, consider using the
1233     * {@link #remake(GreasedRegion)} method on the variable instead, which will, if it has the same width and height
1234     * as the other GreasedRegion, avoid creating garbage and quickly fill the variable with the other's contents.
1235     * @see #copy() for a convenience method that just uses this constructor
1236     * @param other another GreasedRegion that will be copied into this new GreasedRegion
1237     */
1238    public GreasedRegion(final GreasedRegion other)
1239    {
1240        width = other.width;
1241        height = other.height;
1242        ySections = other.ySections;
1243        yEndMask = other.yEndMask;
1244        data = new long[width * ySections];
1245        counts = new int[width * ySections];
1246        System.arraycopy(other.data, 0, data, 0, width * ySections);
1247        System.arraycopy(other.counts, 0, counts, 0, width * ySections);
1248        ct  = other.ct;
1249        tallied = other.tallied;
1250    }
1251
1252    /**
1253     * Primarily for internal use, this constructor copies data2 exactly into the internal long array the new
1254     * GreasedRegion will use, and does not perform any validation steps to ensure that cells that would be "on" but are
1255     * outside the actual height of the GreasedRegion are actually removed (this only matters if height is not a
1256     * multiple of 64).
1257     * @param data2 a long array that is typically from another GreasedRegion, and would be hard to make otherwise
1258     * @param width the width of the GreasedRegion to construct
1259     * @param height the height of the GreasedRegion to construct
1260     */
1261    public GreasedRegion(final long[] data2, final int width, final int height)
1262    {
1263        this.width = width;
1264        this.height = height;
1265        ySections = (height + 63) >> 6;
1266        yEndMask = -1L >>> (64 - (height & 63));
1267        data = new long[width * ySections];
1268        System.arraycopy(data2, 0, data, 0, width * ySections);
1269        if(ySections > 0 && yEndMask != -1) {
1270            for (int a = ySections - 1; a < data.length; a += ySections) {
1271                data[a] &= yEndMask;
1272            }
1273        }
1274        counts = new int[width * ySections];
1275        tallied = false;
1276    }
1277
1278    /**
1279     * Primarily for internal use, this constructor copies data2 into the internal long array the new GreasedRegion will
1280     * use, but treats data2 as having the dimensions [dataWidth][dataHeight], and uses the potentially-different
1281     * dimensions [width][height] for the constructed GreasedRegion. This will truncate data2 on width, height, or both
1282     * if width or height is smaller than dataWidth or dataHeight. It will fill extra space with all "off" if width or
1283     * height is larger than dataWidth or dataHeight. It will interpret data2 as the same 2D shape regardless of the
1284     * width or height it is being assigned to, and data2 will not be reshaped by truncation.
1285     * @param data2 a long array that is typically from another GreasedRegion, and would be hard to make otherwise
1286     * @param dataWidth the width to interpret data2 as having
1287     * @param dataHeight the height to interpret data2 as having
1288     * @param width the width of the GreasedRegion to construct
1289     * @param height the height of the GreasedRegion to construct
1290     */
1291    public GreasedRegion(final long[] data2, final int dataWidth, final int dataHeight, final int width, final int height)
1292    {
1293        this.width = width;
1294        this.height = height;
1295        ySections = (height + 63) >> 6;
1296        yEndMask = -1L >>> (64 - (height & 63));
1297        data = new long[width * ySections];
1298
1299        final int ySections2 = (dataHeight + 63) >> 6;
1300        if(ySections2 == 0)
1301        {
1302            counts = new int[0];
1303            tallied = false;
1304            return;
1305        }
1306        if(ySections == 1) {
1307            System.arraycopy(data2, 0, data, 0, Math.min(dataWidth, width));
1308        }
1309        else
1310        {
1311            if(dataHeight >= height) {
1312                for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) {
1313                    System.arraycopy(data2, i, data, j, ySections);
1314                }
1315            }
1316            else
1317            {
1318                for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) {
1319                    System.arraycopy(data2, i, data, j, ySections2);
1320                }
1321            }
1322        }
1323        if(ySections > 0 && yEndMask != -1) {
1324            for (int a = ySections - 1; a < data.length; a += ySections) {
1325                data[a] &= yEndMask;
1326            }
1327        }
1328        counts = new int[width * ySections];
1329        tallied = false;
1330    }
1331    /**
1332     * Primarily for internal use, this method copies data2 into the internal long array the new GreasedRegion will
1333     * use, but treats data2 as having the dimensions [dataWidth][dataHeight], and uses the potentially-different
1334     * dimensions [width][height] for this GreasedRegion, potentially re-allocating the internal data this uses if width
1335     * and/or height are different from what they were. This will truncate data2 on width, height, or both if width or
1336     * height is smaller than dataWidth or dataHeight. It will fill extra space with all "off" if width or height is
1337     * larger than dataWidth or dataHeight. It will interpret data2 as the same 2D shape regardless of the width or
1338     * height it is being assigned to, and data2 will not be reshaped by truncation.
1339     * @param data2 a long array that is typically from another GreasedRegion, and would be hard to make otherwise
1340     * @param dataWidth the width to interpret data2 as having
1341     * @param dataHeight the height to interpret data2 as having
1342     * @param width the width to set this GreasedRegion to have
1343     * @param height the height to set this GreasedRegion to have
1344     */
1345    public GreasedRegion refill(final long[] data2, final int dataWidth, final int dataHeight, final int width, final int height)
1346    {
1347        if(width != this.width || height != this.height) {
1348            this.width = width;
1349            this.height = height;
1350            ySections = (height + 63) >> 6;
1351            yEndMask = -1L >>> (64 - (height & 63));
1352            data = new long[width * ySections];
1353            counts = new int[width * ySections];
1354        }
1355        else {
1356            Arrays.fill(data, 0L);
1357        }
1358        final int ySections2 = (dataHeight + 63) >> 6;
1359        if(ySections2 == 0)
1360            return this;
1361        if(ySections == 1) {
1362            System.arraycopy(data2, 0, data, 0, Math.min(dataWidth, width));
1363        }
1364        else
1365        {
1366            if(dataHeight >= height) {
1367                for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) {
1368                    System.arraycopy(data2, i, data, j, ySections);
1369                }
1370            }
1371            else
1372            {
1373                for (int i = 0, j = 0; i < width && i < dataWidth; i += ySections2, j += ySections) {
1374                    System.arraycopy(data2, i, data, j, ySections2);
1375                }
1376            }
1377        }
1378        if(ySections > 0 && yEndMask != -1) {
1379            for (int a = ySections - 1; a < data.length; a += ySections) {
1380                data[a] &= yEndMask;
1381            }
1382        }
1383        tallied = false;
1384        return this;
1385    }
1386
1387    /**
1388     * A useful method for efficiency, remake() reassigns this GreasedRegion to have its contents replaced by other. If
1389     * other and this GreasedRegion have identical width and height, this is very efficient and performs no additional
1390     * allocations, simply replacing the cell data in this with the cell data from other. If width and height are not
1391     * both equal between this and other, this does allocate a new data array, but still reassigns this GreasedRegion
1392     * in-place and acts similarly to when width and height are both equal (it just uses some more memory).
1393     * <br>
1394     * Using remake() or the similar refill() methods in chains of operations on multiple GreasedRegions can be key to
1395     * maintaining good performance and memory usage. You often can recycle a no-longer-used GreasedRegion by assigning
1396     * a GreasedRegion you want to keep to it with remake(), then mutating either the remade value or the one that was
1397     * just filled into this but keeping one version around for later usage.
1398     * @param other another GreasedRegion to replace the data in this GreasedRegion with
1399     * @return this for chaining
1400     */
1401    public GreasedRegion remake(GreasedRegion other) {
1402        if (width == other.width && height == other.height) {
1403            System.arraycopy(other.data, 0, data, 0, width * ySections);
1404            System.arraycopy(other.counts, 0, counts, 0, width * ySections);
1405            ct = other.ct;
1406            tallied = other.tallied;
1407            return this;
1408        } else {
1409            width = other.width;
1410            height = other.height;
1411            ySections = other.ySections;
1412            yEndMask = other.yEndMask;
1413            data = new long[width * ySections];
1414            counts = new int[width * ySections];
1415            System.arraycopy(other.data, 0, data, 0, width * ySections);
1416            System.arraycopy(other.counts, 0, counts, 0, width * ySections);
1417            ct = other.ct;
1418            tallied = other.tallied;
1419            return this;
1420        }
1421    }
1422
1423    /**
1424     * Changes the width and/or height of this GreasedRegion, enlarging or shrinking starting at the edges where
1425     * {@code x == width - 1} and {@code y == height - 1}. There isn't an especially efficient way to expand from the
1426     * other edges, but this method is able to copy data in bulk, so at least this method should be very fast. You can
1427     * use {@code insert(int, int, GreasedRegion)} if you want to place one GreasedRegion inside another one,
1428     * potentially with a different size. The space created by any enlargement starts all off; shrinking doesn't change
1429     * the existing data where it isn't removed by the shrink.
1430     * @param widthChange the amount to change width by; can be positive, negative, or zero
1431     * @param heightChange the amount to change height by; can be positive, negative, or zero
1432     * @return this for chaining
1433     */
1434    public GreasedRegion alterBounds(int widthChange, int heightChange)
1435    {
1436        int newWidth = width + widthChange;
1437        int newHeight = height + heightChange;
1438        if(newWidth <= 0 || newHeight <= 0)
1439        {
1440            width = 0;
1441            height = 0;
1442            ySections= 0;
1443            yEndMask = -1;
1444            data = new long[0];
1445            counts = new int[0];
1446            ct = 0;
1447            tallied = true;
1448            return this;
1449        }
1450        int newYSections = (newHeight + 63) >> 6;
1451        yEndMask = -1L >>> (64 - (newHeight & 63));
1452        long[] newData = new long[newWidth * newYSections];         
1453        counts = new int[newWidth * newYSections];
1454        
1455        for (int x = 0; x < width && x < newWidth; x++) {
1456            for (int ys = 0; ys < ySections && ys < newYSections; ys++) {
1457                newData[x * newYSections + ys] = data[x * ySections + ys];
1458            }
1459        }
1460        ySections = newYSections;
1461        width = newWidth;
1462        height = newHeight;
1463        data = newData;
1464        if(ySections > 0 && yEndMask != -1) {
1465            for (int a = ySections - 1; a < data.length; a += ySections) {
1466                data[a] &= yEndMask;
1467            }
1468        }
1469        tallied = false;
1470        return this;
1471    }
1472
1473    /**
1474     * Makes a copy of this GreasedRegion that has been rotated 90 degrees {@code turns} times. If using y-down
1475     * coordinates, then these rotations are clockwise; otherwise, they are counter-clockwise. This uses a copy because
1476     * in many caseswhere the GreasedRegion has non-equal width and height, the rotated version has different
1477     * dimensions, and that requires allocating most of a new GreasedRegion anyway. This GreasedRegion is never modifed
1478     * as a result of this method.
1479     * @param turns how many times to rotate the copy (clockwise if using y-down, counterclockwise otherwise)
1480     * @return a potentially-rotated copy of this GreasedRegion
1481     */
1482    public GreasedRegion copyRotated(int turns)
1483    {
1484        switch (turns & 3) {
1485            case 0: {
1486                return copy();
1487            }
1488            case 1: {
1489                GreasedRegion next = new GreasedRegion(height, width);
1490                for (int x = 0; x < width; x++) {
1491                    for (int y = 0, iy = height - 1; y < height; y++, iy--) {
1492                        if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0L)
1493                            next.data[iy * ySections + (x >> 6)] |= 1L << (x & 63);
1494                    }
1495                }
1496                return next;
1497            }
1498            case 2: {
1499                GreasedRegion next = new GreasedRegion(width, height);
1500                for (int x = 0, ix = width - 1; x < width; x++, ix--) {
1501                    for (int y = 0, iy = height - 1; y < height; y++, iy--) {
1502                        if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0L)
1503                            next.data[ix * ySections + (iy >> 6)] |= 1L << (iy & 63);
1504                    }
1505                }
1506                return next;
1507            }
1508            default: {
1509                GreasedRegion next = new GreasedRegion(height, width);
1510                for (int x = 0, ix = width - 1; x < width; x++, ix--) {
1511                    for (int y = 0; y < height; y++) {
1512                        if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0L)
1513                            next.data[y * ySections + (ix >> 6)] |= 1L << (ix & 63);
1514                    }
1515                }
1516                return next;
1517            }
1518        }
1519    }
1520    
1521    public GreasedRegion flip(boolean leftRight, boolean upDown) {
1522        if(ySections <= 0) return this;
1523        if(leftRight) {
1524            long t;
1525            for (int x = 0, o = width - 1; x < (width >>> 1); x++, o--) {
1526                for (int y = 0; y < ySections; y++) {
1527                    t = data[x * ySections + y];
1528                    data[x * ySections + y] = data[o * ySections + y];
1529                    data[o * ySections + y] = t;
1530                }
1531            }
1532        }
1533        if(upDown) {
1534            if(yEndMask == -1L) {
1535                long t;
1536                for (int x = 0; x < width; x++) {
1537                    for (int y = 0, o = ySections - 1; y < (ySections >>> 1); y++, o--) {
1538                        t = Long.reverse(data[x * ySections + y]);
1539                        data[x * ySections + y] = Long.reverse(data[x * ySections + o]);
1540                        data[x * ySections + o] = t;
1541                    }
1542                    if((ySections & 1) == 1){
1543                        data[x * ySections + (ySections >>> 1)] = Long.reverse(data[x * ySections + (ySections >>> 1)]);
1544                    }
1545                }
1546            }
1547            else {
1548                int shift = Long.numberOfLeadingZeros(yEndMask);
1549                if (ySections == 1) {
1550                    for (int x = 0; x < width; x++) {
1551                        data[x] = Long.reverse(data[x]) >>> shift;
1552                    }
1553                } else {
1554                    for (int x = 0; x < width; x++) {
1555                        int ie = x * ySections + ySections - 1;
1556                        int ib = x * ySections + ySections - 2;
1557                        int il = x * ySections + 1;
1558                        int is = x * ySections;
1559                        long end = Long.reverse(data[ie]);
1560                        long big = Long.reverse(data[ib]);
1561                        long little = Long.reverse(data[il]);
1562                        long start = Long.reverse(data[is]);
1563                        data[ie] = start >>> shift;
1564                        data[is] = end >>> shift;
1565                        data[is] |= big << 64 - shift;
1566                        data[ib] = start << 64 - shift;
1567                        data[ib] |= little >>> shift;
1568                        
1569                        for (int y = 1; y < (ySections >>> 1); y++) {
1570                            end = big;
1571                            start = little;
1572                            big = Long.reverse(data[--ib]);
1573                            little = Long.reverse(data[++il]);
1574                            ++is;
1575                            data[is] = end >>> shift;
1576                            data[is] |= big << 64 - shift;
1577                            data[ib] = start << 64 - shift;
1578                            data[ib] |= little >>> shift;
1579                        }
1580                    }
1581                }
1582            }
1583        }
1584        return this;
1585    }
1586
1587    /**
1588     * Sets the cell at x,y to on if value is true or off if value is false. Does nothing if x,y is out of bounds.
1589     * @param value the value to set in the cell
1590     * @param x the x-position of the cell
1591     * @param y the y-position of the cell
1592     * @return this for chaining
1593     */
1594    public GreasedRegion set(boolean value, int x, int y)
1595    {
1596        if(x < width && y < height && x >= 0 && y >= 0) {
1597            if(value)
1598                data[x * ySections + (y >> 6)] |= 1L << (y & 63);
1599            else
1600                data[x * ySections + (y >> 6)] &= ~(1L << (y & 63));
1601            tallied = false;
1602        }
1603        return this;
1604    }
1605
1606    /**
1607     * Sets the cell at point to on if value is true or off if value is false. Does nothing if point is out of bounds,
1608     * or if point is null.
1609     * @param value the value to set in the cell
1610     * @param point the x,y Coord of the cell to set
1611     * @return this for chaining
1612     */
1613    public GreasedRegion set(boolean value, Coord point)
1614    {
1615        if(point == null) return this;
1616        return set(value, point.x, point.y);
1617    }
1618
1619    /**
1620     * Sets the cell at x,y to "on". Does nothing if x,y is out of bounds.
1621     * More efficient, slightly, than {@link #set(boolean, int, int)} if you just need to set a cell to "on".
1622     * @param x the x-position of the cell
1623     * @param y the y-position of the cell
1624     * @return this for chaining
1625     */
1626    public GreasedRegion insert(int x, int y)
1627    {
1628        if(x < width && y < height && x >= 0 && y >= 0)
1629        {
1630            data[x * ySections + (y >> 6)] |= 1L << (y & 63);
1631            tallied = false;
1632        }
1633        return this;
1634    }
1635
1636    /**
1637     * Sets the given cell, "tightly" encoded for a specific width/height as by {@link #asTightEncoded()}, to "on".
1638     * Does nothing if the cell is out of bounds.
1639     * @param tight a cell tightly encoded for this GreasedRegion's width and height
1640     * @return this for chaining
1641     */
1642    public GreasedRegion insert(int tight)
1643    {
1644        if(tight < width * height && tight >= 0)
1645        {
1646            data[(tight % width) * ySections + ((tight / width) >>> 6)] |= 1L << ((tight / width) & 63);
1647            tallied = false;
1648        }
1649        return this;
1650    }
1651    /**
1652     * Sets the cell at point to "on". Does nothing if point is out of bounds, or if point is null.
1653     * More efficient, slightly, than {@link #set(boolean, Coord)} if you just need to set a cell to "on".
1654     * @param point the x,y Coord of the cell
1655     * @return this for chaining
1656     */
1657    public GreasedRegion insert(Coord point)
1658    {
1659
1660        if(point == null) return this;
1661        return insert(point.x, point.y);
1662    }
1663
1664    /**
1665     * Takes another GreasedRegion, called other, with potentially different size and inserts its "on" cells into thi
1666     * GreasedRegion at the given x,y offset, allowing negative x and/or y to put only part of other in this.
1667     * <br>
1668     * This is a rather complex method internally, but should be about as efficient as a general insert-region method
1669     * can be.
1670     * @param x the x offset to start inserting other at; may be negative
1671     * @param y the y offset to start inserting other at; may be negative
1672     * @param other the other GreasedRegion to insert
1673     * @return this for chaining
1674     */
1675    public GreasedRegion insert(int x, int y, GreasedRegion other)
1676    {
1677        if(other == null || other.ySections <= 0 || other.width <= 0)
1678            return this;
1679
1680        int start = Math.max(0, x), len = Math.min(width, Math.min(other.width, other.width + x) - start),
1681        oys = other.ySections, jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63),
1682        originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump);
1683        long[] data2 = new long[other.width * ySections];
1684
1685        long prev, tmp;
1686        if(oys == ySections) {
1687            if (x < 0) {
1688                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1689                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
1690                        data2[jj * ySections + i] = other.data[j * oys + oi];
1691                    }
1692                }
1693            } else if (x > 0) {
1694                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1695                    for (int j = 0, jj = start; j < len; j++, jj++) {
1696                        data2[jj * ySections + i] = other.data[j * ySections + oi];
1697                    }
1698                }
1699            } else {
1700                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1701                    for (int j = 0; j < len; j++) {
1702                        data2[j * ySections + i] = other.data[j * ySections + oi];
1703                    }
1704                }
1705            }
1706        }
1707        else if(oys < ySections)
1708        {
1709            if (x < 0) {
1710                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1711                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
1712                        data2[jj * ySections + i] = other.data[j * oys + oi];
1713                    }
1714                }
1715            } else if (x > 0) {
1716                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {// oi < oys - Math.max(0, jump)
1717                    for (int j = 0, jj = start; j < len; j++, jj++) {
1718                        data2[jj * ySections + i] = other.data[j * oys + oi];
1719                    }
1720                }
1721            } else {
1722                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1723                    for (int j = 0; j < len; j++) {
1724                        data2[j * ySections + i] = other.data[j * oys + oi];
1725                    }
1726                }
1727            }
1728        }
1729        else
1730        {
1731            if (x < 0) {
1732                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1733                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
1734                        data2[jj * ySections + i] = other.data[j * oys + oi];
1735                    }
1736                }
1737            } else if (x > 0) {
1738                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1739                    for (int j = 0, jj = start; j < len; j++, jj++) {
1740                        data2[jj * ySections + i] = other.data[j * oys + oi];
1741                    }
1742                }
1743            } else {
1744                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1745                    for (int j = 0; j < len; j++) {
1746                        data2[j * ySections + i] = other.data[j * oys + oi];
1747                    }
1748                }
1749            }
1750        }
1751
1752        if(lily < 0) {
1753            for (int i = start; i < len; i++) {
1754                prev = 0L;
1755                for (int j = 0; j < ySections; j++) {
1756                    tmp = prev;
1757                    prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily);
1758                    data2[i * ySections + j] >>>= -lily;
1759                    data2[i * ySections + j] |= tmp;
1760                }
1761            }
1762        }
1763        else if(lily > 0) {
1764            for (int i = start; i < start + len; i++) {
1765                prev = 0L;
1766                for (int j = 0; j < ySections; j++) {
1767                    tmp = prev;
1768                    prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily);
1769                    data2[i * ySections + j] <<= lily;
1770                    data2[i * ySections + j] |= tmp;
1771                }
1772            }
1773        }
1774        len = Math.min(width, start + len);
1775        for (int i = start; i < len; i++) {
1776            for (int j = 0; j < ySections; j++) {
1777                data[i * ySections + j] |= data2[i * ySections + j];
1778            }
1779        }
1780
1781        if(ySections > 0 && yEndMask != -1) {
1782            for (int a = ySections - 1; a < data.length; a += ySections) {
1783                data[a] &= yEndMask;
1784            }
1785        }
1786        tallied = false;
1787        return this;
1788    }
1789
1790    public GreasedRegion insertSeveral(Coord... points)
1791    {
1792        for (int i = 0, x, y; i < points.length; i++) {
1793            x = points[i].x;
1794            y = points[i].y;
1795            if(x < width && y < height && x >= 0 && y >= 0)
1796            {
1797                data[x * ySections + (y >> 6)] |= 1L << (y & 63);
1798                tallied = false;
1799            }
1800        }
1801        return this;
1802    }
1803
1804    public GreasedRegion insertSeveral(final int[] points)
1805    {
1806        for (int i = 0, tight; i < points.length; i++) {
1807            tight = points[i];
1808            if(tight < width * height && tight >= 0)
1809            {
1810                data[(tight % width) * ySections + ((tight / width) >>> 6)] |= 1L << ((tight / width) & 63);
1811                tallied = false;
1812            }
1813        }
1814        return this;
1815    }
1816
1817    public GreasedRegion insertSeveral(Iterable<Coord> points)
1818    {
1819        int x, y;
1820        for (Coord pt : points) {
1821            x = pt.x;
1822            y = pt.y;
1823            if(x < width && y < height && x >= 0 && y >= 0)
1824            {
1825                data[x * ySections + (y >> 6)] |= 1L << (y & 63);
1826                tallied = false;
1827            }
1828        }
1829        return this;
1830    }
1831
1832    public GreasedRegion insertRectangle(int startX, int startY, int rectangleWidth, int rectangleHeight)
1833    {
1834        if(rectangleWidth < 1 || rectangleHeight < 1 || ySections <= 0)
1835            return this;
1836        if(startX < 0)
1837            startX = 0;
1838        else if(startX >= width)
1839            startX = width - 1;
1840        if(startY < 0)
1841            startY = 0;
1842        else if(startY >= height)
1843            startY = height - 1;
1844        int endX = Math.min(width, startX + rectangleWidth) - 1,
1845                endY = Math.min(height, startY + rectangleHeight) - 1,
1846                startSection = startY >> 6, endSection = endY >> 6;
1847        if(startSection < endSection)
1848        {
1849            long startMask = -1L << (startY & 63),
1850                    endMask = -1L >>> (~endY & 63);
1851            for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) {
1852                data[a] |= startMask;
1853            }
1854            if(endSection - startSection > 1)
1855            {
1856                for (int b = 1; b < endSection - startSection; b++) {
1857                    for (int a = startX * ySections + startSection + b; a < endX * ySections + ySections; a += ySections) {
1858                        data[a] = -1;
1859                    }
1860                }
1861            }
1862            for (int a = startX * ySections + endSection; a <= endX * ySections + endSection; a += ySections) {
1863                data[a] |= endMask;
1864            }
1865        }
1866        else
1867        {
1868            long mask = (-1L << (startY & 63)) & (-1L >>> (~endY & 63));
1869            for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) {
1870                data[a] |= mask;
1871            }
1872        }
1873
1874        if(yEndMask != -1L) {
1875            for (int a = ySections - 1; a < data.length; a += ySections) {
1876                data[a] &= yEndMask;
1877            }
1878        }
1879        tallied = false;
1880        return this;
1881    }
1882
1883    public GreasedRegion insertCircle(Coord center, int radius)
1884    {
1885        float high, changedX;
1886        int rndX, rndY;
1887        for (int dx = -radius; dx <= radius; ++dx) {
1888            changedX = dx - 0.25f * (dx >> 31 | -dx >>> 31); // project nayuki signum
1889            rndX = Math.round(changedX);
1890            high = (float) Math.sqrt(radius * radius - changedX * changedX);             
1891            insert(center.x + rndX, center.y);
1892            for (float dy = high; dy >= 0.75f; --dy) {
1893                rndY = Math.round(dy - 0.25f);                 
1894                insert(center.x + rndX, center.y + rndY);
1895                insert(center.x + rndX, center.y - rndY);
1896            }
1897        }
1898        return this;
1899    }
1900
1901    public GreasedRegion remove(int x, int y)
1902    {
1903        if(x < width && y < height && x >= 0 && y >= 0)
1904        {
1905            data[x * ySections + (y >> 6)] &= ~(1L << (y & 63));
1906            tallied = false;
1907        }
1908        return this;
1909    }
1910    public GreasedRegion remove(Coord point)
1911    {
1912        return remove(point.x, point.y);
1913    }
1914    /**
1915     * Takes another GreasedRegion, called other, with potentially different size and removes its "on" cells from this
1916     * GreasedRegion at the given x,y offset, allowing negative x and/or y to remove only part of other in this.
1917     * <br>
1918     * This is a rather complex method internally, but should be about as efficient as a general remove-region method
1919     * can be. The code is identical to {@link #insert(int, int, GreasedRegion)} except that where insert only adds
1920     * cells, this only removes cells. Essentially, insert() is to {@link #or(GreasedRegion)} as remove() is to
1921     * {@link #andNot(GreasedRegion)}.
1922     * @param x the x offset to start removing other from; may be negative
1923     * @param y the y offset to start removing other from; may be negative
1924     * @param other the other GreasedRegion to remove
1925     * @return this for chaining
1926     */
1927    public GreasedRegion remove(int x, int y, GreasedRegion other)
1928    {
1929        if(other == null || other.ySections <= 0 || other.width <= 0)
1930            return this;
1931
1932        int start = Math.max(0, x), len = Math.min(width, Math.min(other.width, other.width + x) - start),
1933                oys = other.ySections, jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y-1 >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63),
1934                originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump);
1935        long[] data2 = new long[other.width * ySections];
1936
1937        long prev, tmp;
1938        if(oys == ySections) {
1939            if (x < 0) {
1940                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1941                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
1942                        data2[jj * ySections + i] = other.data[j * oys + oi];
1943                    }
1944                }
1945            } else if (x > 0) {
1946                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1947                    for (int j = 0, jj = start; j < len; j++, jj++) {
1948                        data2[jj * ySections + i] = other.data[j * ySections + oi];
1949                    }
1950                }
1951            } else {
1952                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1953                    for (int j = 0; j < len; j++) {
1954                        data2[j * ySections + i] = other.data[j * ySections + oi];
1955                    }
1956                }
1957            }
1958        }
1959        else if(oys < ySections)
1960        {
1961            if (x < 0) {
1962                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1963                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
1964                        data2[jj * ySections + i] = other.data[j * oys + oi];
1965                    }
1966                }
1967            } else if (x > 0) {
1968                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {// oi < oys - Math.max(0, jump)
1969                    for (int j = 0, jj = start; j < len; j++, jj++) {
1970                        data2[jj * ySections + i] = other.data[j * oys + oi];
1971                    }
1972                }
1973            } else {
1974                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1975                    for (int j = 0; j < len; j++) {
1976                        data2[j * ySections + i] = other.data[j * oys + oi];
1977                    }
1978                }
1979            }
1980        }
1981        else
1982        {
1983            if (x < 0) {
1984                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1985                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
1986                        data2[jj * ySections + i] = other.data[j * oys + oi];
1987                    }
1988                }
1989            } else if (x > 0) {
1990                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1991                    for (int j = 0, jj = start; j < len; j++, jj++) {
1992                        data2[jj * ySections + i] = other.data[j * oys + oi];
1993                    }
1994                }
1995            } else {
1996                for (int i = alterJump, oi = originalJump; i < ySections && oi < oys; i++, oi++) {
1997                    for (int j = 0; j < len; j++) {
1998                        data2[j * ySections + i] = other.data[j * oys + oi];
1999                    }
2000                }
2001            }
2002        }
2003
2004        if(lily < 0) {
2005            for (int i = start; i < len; i++) {
2006                prev = 0L;
2007                for (int j = 0; j < ySections; j++) {
2008                    tmp = prev;
2009                    prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily);
2010                    data2[i * ySections + j] >>>= -lily;
2011                    data2[i * ySections + j] |= tmp;
2012                }
2013            }
2014        }
2015        else if(lily > 0) {
2016            for (int i = start; i < start + len; i++) {
2017                prev = 0L;
2018                for (int j = 0; j < ySections; j++) {
2019                    tmp = prev;
2020                    prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily);
2021                    data2[i * ySections + j] <<= lily;
2022                    data2[i * ySections + j] |= tmp;
2023                }
2024            }
2025        }
2026        len = Math.min(width, start + len);
2027        for (int i = start; i < len; i++) {
2028            for (int j = 0; j < ySections; j++) {
2029                data[i * ySections + j] &= ~data2[i * ySections + j];
2030            }
2031        }
2032
2033        if(ySections > 0 && yEndMask != -1) {
2034            for (int a = ySections - 1; a < data.length; a += ySections) {
2035                data[a] &= yEndMask;
2036            }
2037        }
2038        tallied = false;
2039
2040        return this;
2041    }
2042    public GreasedRegion removeSeveral(Coord... points)
2043    {
2044        for (int i = 0, x, y; i < points.length; i++) {
2045            x = points[i].x;
2046            y = points[i].y;
2047            if(x < width && y < height && x >= 0 && y >= 0)
2048            {
2049                data[x * ySections + (y >> 6)] &= ~(1L << (y & 63));
2050                tallied = false;
2051            }
2052        }
2053        return this;
2054    }
2055
2056    public GreasedRegion removeSeveral(Iterable<Coord> points)
2057    {
2058        int x, y;
2059        for (Coord pt : points) {
2060            x = pt.x;
2061            y = pt.y;
2062            if(x < width && y < height && x >= 0 && y >= 0)
2063            {
2064                data[x * ySections + (y >> 6)] &= ~(1L << (y & 63));
2065                tallied = false;
2066            }
2067        }
2068        return this;
2069    }
2070
2071    /**
2072     * Removes all "on" cells from (startX, startY) inclusive 
2073     * to (startX+rectangleWidth, startY+rectangleHeight) exclusive, removing a total width of rectangleWidth and a
2074     * total height of rectangleHeight in cells.
2075     * @param startX left side x-coordinate
2076     * @param startY top side (or bottom if positive y is up) y-coordinate
2077     * @param rectangleWidth how many cells wide the area to remove is
2078     * @param rectangleHeight how many cells tal the area to remove is
2079     * @return this, after modification, for chaining
2080     */
2081    public GreasedRegion removeRectangle(int startX, int startY, int rectangleWidth, int rectangleHeight)
2082    {
2083        if(startX < 0)
2084        {
2085            rectangleWidth += startX;
2086            startX = 0;
2087        }
2088        else if(startX >= width)
2089        {
2090            rectangleWidth = 1;
2091            startX = width - 1;
2092        }
2093        if(startY < 0)
2094        {
2095            rectangleHeight += startY;
2096            startY = 0;
2097        }
2098        else if(startY >= height)
2099        {
2100            rectangleHeight = 1;
2101            startY = height - 1;
2102        }
2103        if(rectangleWidth < 1 || rectangleHeight < 1 || ySections <= 0)
2104            return this;
2105        int endX = Math.min(width, startX + rectangleWidth) - 1,
2106                endY = Math.min(height, startY + rectangleHeight) - 1,
2107                startSection = startY >> 6, endSection = endY >> 6;
2108        if(startSection < endSection)
2109        {
2110            long startMask = ~(-1L << (startY & 63)),
2111                    endMask = ~(-1L >>> (~endY & 63));
2112            for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) {
2113                data[a] &= startMask;
2114            }
2115            if(endSection - startSection > 1)
2116            {
2117                for (int b = 1; b < endSection - startSection; b++) {
2118                    for (int a = startX * ySections + startSection + b; a < endX * ySections + ySections; a += ySections) {
2119                        data[a] = 0;
2120                    }
2121                }
2122            }
2123            for (int a = startX * ySections + endSection; a <= endX * ySections + endSection; a += ySections) {
2124                data[a] &= endMask;
2125            }
2126        }
2127        else
2128        {
2129            long mask = ~((-1L << (startY & 63)) & (-1L >>> (~endY & 63)));
2130            for (int a = startX * ySections + startSection; a <= endX * ySections + startSection; a += ySections) {
2131                data[a] &= mask;
2132            }
2133        }
2134        tallied = false;
2135        return this;
2136    }
2137
2138    public GreasedRegion removeCircle(Coord center, int radius)
2139    {
2140        float high, changedX;
2141        int rndX, rndY;
2142        for (int dx = -radius; dx <= radius; ++dx) {
2143            changedX = dx - 0.25f * (dx >> 31 | -dx >>> 31); // project nayuki signum
2144            rndX = Math.round(changedX);
2145            high = (float) Math.sqrt(radius * radius - changedX * changedX);
2146            remove(center.x + rndX, center.y);
2147            for (float dy = high; dy >= 0.75f; --dy) {
2148                rndY = Math.round(dy - 0.25f);
2149                remove(center.x + rndX, center.y + rndY);
2150                remove(center.x + rndX, center.y - rndY);
2151            }
2152        }
2153        return this;
2154    }
2155
2156    /**
2157     * Equivalent to {@link #clear()}, setting all cells to "off," but also returns this for chaining.
2158     * @return this for chaining
2159     */
2160    public GreasedRegion empty()
2161    {
2162        Arrays.fill(data, 0L);
2163        Arrays.fill(counts, 0);
2164        ct = 0;
2165        tallied = true;
2166        return this;
2167    }
2168
2169    /**
2170     * Sets all cells in this to "on."
2171     * @return this for chaining
2172     */
2173    public GreasedRegion allOn()
2174    {
2175        if(ySections > 0)
2176        {
2177            if(yEndMask == -1) {
2178                Arrays.fill(data, -1);
2179                Arrays.fill(counts, 64);
2180                ct = ySections * width << 6;
2181                tallied = true;
2182            }
2183            else
2184            {
2185                ct = Long.bitCount(yEndMask);
2186                for (int a = ySections - 1; a < data.length; a += ySections) {
2187                    data[a] = yEndMask;
2188                    counts[a] = ct;
2189                    for (int i = 0; i < ySections - 1; i++) {
2190                        data[a-i-1] = -1;
2191                        counts[a-i-1] = 64;
2192                    }
2193                }
2194                ct *= width;
2195                ct += (ySections - 1) * width << 6;
2196                tallied = true;
2197            }
2198        }
2199        return this;
2200    }
2201
2202    /**
2203     * Sets all cells in this to "on" if contents is true, or "off" if contents is false.
2204     * @param contents true to set all cells to on, false to set all cells to off
2205     * @return this for chaining
2206     */
2207    public GreasedRegion fill(boolean contents)
2208    {
2209        if(contents)
2210        {
2211            if(ySections > 0)
2212            {
2213                if(yEndMask == -1) {
2214                    Arrays.fill(data, -1);
2215                    Arrays.fill(counts, 64);
2216                    ct = ySections * width << 6;
2217                    tallied = true;
2218                }
2219                else
2220                {
2221                    ct = Long.bitCount(yEndMask);
2222                    for (int a = ySections - 1; a < data.length; a += ySections) {
2223                        data[a] = yEndMask;
2224                        counts[a] = ct;
2225                        for (int i = 0; i < ySections - 1; i++) {
2226                            data[a-i-1] = -1;
2227                            counts[a-i-1] = 64;
2228                        }
2229                    }
2230                    ct *= width;
2231                    ct += (ySections - 1) * width << 6;
2232                    tallied = true;
2233                }
2234            }
2235            //else... what, if ySections is 0 there's nothing to do
2236        }
2237        else
2238        {
2239            Arrays.fill(data, 0L);
2240            Arrays.fill(counts, 0);
2241            ct = 0;
2242            tallied = true;
2243        }
2244        return this;
2245    }
2246    /**
2247     * Turns all cells that are adjacent to the boundaries of the GreasedRegion to "off".
2248     * @return this for chaining
2249     */
2250    public GreasedRegion removeEdges()
2251    {
2252        if(ySections > 0) {
2253            for (int i = 0; i < ySections; i++) {
2254                data[i] = 0L;
2255                data[width * ySections - 1 - i] = 0L;
2256            }
2257            if (ySections == 1) {
2258                for (int i = 0; i < width; i++) {
2259                    data[i] &= yEndMask >>> 1 & -2L;
2260                }
2261            } else {
2262                for (int i = ySections; i < data.length - ySections; i += ySections) {
2263                    data[i] &= -2L;
2264                }
2265                for (int a = ySections * 2 - 1; a < data.length - ySections; a += ySections) {
2266                    data[a] &= yEndMask >>> 1;
2267                }
2268            }
2269            tallied = false;
2270        }
2271        return this;
2272    }
2273
2274    /**
2275     * Simple method that returns a newly-allocated copy of this GreasedRegion; modifications to one won't change the
2276     * other, and this method returns the copy while leaving the original unchanged.
2277     * @return a copy of this GreasedRegion; the copy can be changed without altering the original
2278     */
2279    public GreasedRegion copy()
2280    {
2281        return new GreasedRegion(this);
2282    }
2283
2284    /**
2285     * Returns this GreasedRegion's data as a 2D boolean array, [width][height] in size, with on treated as true and off
2286     * treated as false.
2287     * @return a 2D boolean array that represents this GreasedRegion's data
2288     */
2289    public boolean[][] decode()
2290    {
2291        boolean[][] bools = new boolean[width][height];
2292        for (int x = 0; x < width; x++) {
2293            for (int y = 0; y < height; y++) {
2294                bools[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0;
2295            }
2296        }
2297        return bools;
2298    }
2299
2300    /**
2301     * Fills this GreasedRegion's data into the given 2D char array, modifying it and returning it, with "on" cells
2302     * filled with the char parameter {@code on} and "off" cells with the parameter {@code off}.
2303     * @param chars a 2D char array that will be modified; must not be null, nor can it contain null elements
2304     * @param on the char to use for "on" cells
2305     * @param off the char to use for "off" cells
2306     * @return a 2D char array that represents this GreasedRegion's data
2307     */
2308    public char[][] intoChars(char[][] chars, char on, char off)
2309    {
2310        for (int x = 0; x < width && x < chars.length; x++) {
2311            for (int y = 0; y < height && y < chars[x].length; y++) {
2312                chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? on : off;
2313            }
2314        }
2315        return chars;
2316    }
2317
2318    /**
2319     * Fills this GreasedRegion's data into the given 2D char array, modifying it and returning it, with "on" cells
2320     * filled with the char parameter {@code on} and "off" cells left as-is.
2321     * @param chars a 2D char array that will be modified; must not be null, nor can it contain null elements
2322     * @param on the char to use for "on" cells
2323     * @return a 2D char array that represents the "on" cells in this GreasedRegion's data written over chars
2324     */
2325    public char[][] intoChars(char[][] chars, char on)
2326    {
2327        for (int x = 0; x < width && x < chars.length; x++) {
2328            for (int y = 0; y < height && y < chars[x].length; y++) {
2329                if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0)
2330                    chars[x][y] = on;
2331            }
2332        }
2333        return chars;
2334    }
2335
2336    /**
2337     * Returns this GreasedRegion's data as a 2D char array,  [width][height] in size, with "on" cells filled with the
2338     * char parameter on and "off" cells with the parameter off.
2339     * @param on the char to use for "on" cells
2340     * @param off the char to use for "off" cells
2341     * @return a 2D char array that represents this GreasedRegion's data
2342     */
2343    public char[][] toChars(char on, char off)
2344    {
2345        char[][] chars = new char[width][height];
2346        for (int x = 0; x < width; x++) {
2347            for (int y = 0; y < height; y++) {
2348                chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? on : off;
2349            }
2350        }
2351        return chars;
2352    }
2353    /**
2354     * Returns this GreasedRegion's data as a 2D char array,  [width][height] in size, with "on" cells filled with '.'
2355     * and "off" cells with '#'.
2356     * @return a 2D char array that represents this GreasedRegion's data
2357     */
2358
2359    public char[][] toChars()
2360    {
2361        return toChars('.', '#');
2362    }
2363
2364    /**
2365     * Returns this GreasedRegion's data as a StringBuilder, with each row made of the parameter on for "on" cells and
2366     * the parameter off for "off" cells, separated by newlines, with no trailing newline at the end.
2367     * @param on the char to use for "on" cells
2368     * @param off the char to use for "off" cells
2369     * @return a StringBuilder that stores each row of this GreasedRegion as chars, with rows separated by newlines.
2370     */
2371    public StringBuilder show(char on, char off)
2372    {
2373        StringBuilder sb = new StringBuilder((width+1) * height);
2374        for (int y = 0; y < height;) {
2375            for (int x = 0; x < width; x++) {
2376                sb.append((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? on : off);
2377            }
2378            if(++y < height)
2379                sb.append('\n');
2380        }
2381        return sb;
2382    }
2383
2384    /**
2385     * Returns a legible String representation of this that can be printed over multiple lines, with all "on" cells
2386     * represented by '.' and all "off" cells by '#', in roguelike floors-on walls-off convention, separating each row
2387     * by newlines (without a final trailing newline, so you could append text right after this).
2388     * @return a String representation of this GreasedRegion using '.' for on, '#' for off, and newlines between rows
2389     */
2390    @Override
2391    public String toString()
2392    {
2393        return show('.', '#').toString();
2394    }
2395
2396    /**
2397     * Returns a copy of map where if a cell is "on" in this GreasedRegion, this keeps the value in map intact,
2398     * and where a cell is "off", it instead writes the char filler.
2399     * @param map a 2D char array that will not be modified
2400     * @param filler the char to use where this GreasedRegion stores an "off" cell
2401     * @return a masked copy of map
2402     */
2403    public char[][] mask(char[][] map, char filler)
2404    {
2405        if(map == null || map.length == 0)
2406            return new char[0][0];
2407        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2408        char[][] chars = new char[width2][height2];
2409        for (int x = 0; x < width2; x++) {
2410            for (int y = 0; y < height2; y++) {
2411                chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? map[x][y] : filler;
2412            }
2413        }
2414        return chars;
2415    }
2416
2417    /**
2418     * Returns a copy of map where if a cell is "on" in this GreasedRegion, this keeps the value in map intact,
2419     * and where a cell is "off", it instead writes the short filler. Meant for use with MultiSpill, but may be
2420     * used anywhere you have a 2D short array. {@link #mask(char[][], char)} is more likely to be useful.
2421     * @param map a 2D short array that will not be modified
2422     * @param filler the short to use where this GreasedRegion stores an "off" cell
2423     * @return a masked copy of map
2424     */
2425    public short[][] mask(short[][] map, short filler)
2426    {
2427        if(map == null || map.length == 0)
2428            return new short[0][0];
2429        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2430        short[][] shorts = new short[width2][height2];
2431        for (int x = 0; x < width2; x++) {
2432            for (int y = 0; y < height2; y++) {
2433                shorts[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? map[x][y] : filler;
2434            }
2435        }
2436        return shorts;
2437    }
2438
2439    /**
2440     * Returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps the value in map intact,
2441     * and where a cell is "on", it instead writes the char toWrite.
2442     * @param map a 2D char array that will not be modified
2443     * @param toWrite the char to use where this GreasedRegion stores an "on" cell
2444     * @return a masked copy of map
2445     */
2446    public char[][] inverseMask(char[][] map, char toWrite)
2447    {
2448        if(map == null || map.length == 0)
2449            return new char[0][0];
2450        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2451        char[][] chars = new char[width2][height2];
2452        for (int x = 0; x < width2; x++) {
2453            for (int y = 0; y < height2; y++) {
2454                chars[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? toWrite : map[x][y];
2455            }
2456        }
2457        return chars;
2458    }
2459
2460    /**
2461     * "Inverse mask for ints;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps
2462     * the value in map intact, and where a cell is "on", it instead writes the int toWrite.
2463     * @param map a 2D int array that will not be modified
2464     * @param toWrite the int to use where this GreasedRegion stores an "on" cell
2465     * @return an altered copy of map
2466     */
2467    public int[][] writeInts(int[][] map, int toWrite)
2468    {
2469        if(map == null || map.length == 0)
2470            return new int[0][0];
2471        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2472        int[][] ints = new int[width2][height2];
2473        for (int x = 0; x < width2; x++) {
2474            for (int y = 0; y < height2; y++) {
2475                ints[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? toWrite : map[x][y];
2476            }
2477        }
2478        return ints;
2479    }
2480
2481    /**
2482     * "Inverse mask for ints;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps
2483     * the value in map intact, and where a cell is "on", it instead writes the int toWrite. Modifies map in-place,
2484     * unlike {@link #writeInts(int[][], int)}.
2485     * @param map a 2D int array that <b>will</b> be modified
2486     * @param toWrite the int to use where this GreasedRegion stores an "on" cell
2487     * @return map, with the changes applied; not a copy
2488     */
2489    public int[][] writeIntsInto(int[][] map, int toWrite)
2490    {
2491        if(map == null || map.length == 0)
2492            return map;
2493        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2494        for (int x = 0; x < width2; x++) {
2495            for (int y = 0; y < height2; y++) {
2496                if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0)
2497                    map[x][y] = toWrite;
2498            }
2499        }
2500        return map;
2501    }
2502    /**
2503     * "Inverse mask for doubles;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps
2504     * the value in map intact, and where a cell is "on", it instead writes the double toWrite.
2505     * @param map a 2D double array that will not be modified
2506     * @param toWrite the double to use where this GreasedRegion stores an "on" cell
2507     * @return an altered copy of map
2508     */
2509    public double[][] writeDoubles(double[][] map, double toWrite)
2510    {
2511        if(map == null || map.length == 0)
2512            return new double[0][0];
2513        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2514        double[][] doubles = new double[width2][height2];
2515        for (int x = 0; x < width2; x++) {
2516            for (int y = 0; y < height2; y++) {
2517                doubles[x][y] = (data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0 ? toWrite : map[x][y];
2518            }
2519        }
2520        return doubles;
2521    }
2522
2523    /**
2524     * "Inverse mask for doubles;" returns a copy of map where if a cell is "off" in this GreasedRegion, this keeps
2525     * the value in map intact, and where a cell is "on", it instead writes the double toWrite. Modifies map in-place,
2526     * unlike {@link #writeDoubles(double[][], double)}.
2527     * @param map a 2D double array that <b>will</b> be modified
2528     * @param toWrite the double to use where this GreasedRegion stores an "on" cell
2529     * @return map, with the changes applied; not a copy
2530     */
2531    public double[][] writeDoublesInto(double[][] map, double toWrite)
2532    {
2533        if(map == null || map.length == 0)
2534            return map;
2535        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2536        for (int x = 0; x < width2; x++) {
2537            for (int y = 0; y < height2; y++) {
2538                if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0)
2539                    map[x][y] = toWrite;
2540            }
2541        }
2542        return map;
2543    }
2544    /**
2545     * Like {@link #inverseMask(char[][], char)}, but modifies {@code map} in-place and returns it. If a cell is "off"
2546     * in this GreasedRegion, this keeps the value in map intact, and where a cell is "on", it instead writes the char
2547     * toWrite. Modifies map in-place, unlike {@link #inverseMask(char[][], char)}.
2548     * @param map a 2D char array that <b>will</b> be modified
2549     * @param toWrite the char to use where this GreasedRegion stores an "on" cell
2550     * @return map, with the changes applied; not a copy
2551     */
2552    public char[][] writeCharsInto(char[][] map, char toWrite)
2553    {
2554        if(map == null || map.length == 0)
2555            return map;
2556        int width2 = Math.min(width, map.length), height2 = Math.min(height, map[0].length);
2557        for (int x = 0; x < width2; x++) {
2558            for (int y = 0; y < height2; y++) {
2559                if((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0)
2560                    map[x][y] = toWrite;
2561            }
2562        }
2563        return map;
2564    }
2565
2566    /**
2567     * Union of two GreasedRegions, assigning the result into this GreasedRegion. Any cell that is "on" in either
2568     * GreasedRegion will be made "on" in this GreasedRegion.
2569     * @param other another GreasedRegion that will not be modified
2570     * @return this, after modification, for chaining
2571     */
2572    public GreasedRegion or(GreasedRegion other)
2573    {
2574        for (int x = 0; x < width && x < other.width; x++) {
2575            for (int y = 0; y < ySections && y < other.ySections; y++) {
2576                data[x * ySections + y] |= other.data[x * other.ySections + y];
2577            }
2578        }
2579
2580        if(ySections > 0 && yEndMask != -1) {
2581            for (int a = ySections - 1; a < data.length; a += ySections) {
2582                data[a] &= yEndMask;
2583            }
2584        }
2585        tallied = false;
2586
2587        return this;
2588    }
2589
2590    /**
2591     * Intersection of two GreasedRegions, assigning the result into this GreasedRegion. Any cell that is "on" in both
2592     * GreasedRegions will be kept "on" in this GreasedRegion, but all other cells will be made "off."
2593     * @param other another GreasedRegion that will not be modified
2594     * @return this, after modification, for chaining
2595     */
2596    public GreasedRegion and(GreasedRegion other)
2597    {
2598        for (int x = 0; x < width && x < other.width; x++) {
2599            for (int y = 0; y < ySections && y < other.ySections; y++) {
2600                data[x * ySections + y] &= other.data[x * other.ySections + y];
2601            }
2602        }
2603        tallied = false;
2604        return this;
2605    }
2606
2607    /**
2608     * Intersection of two GreasedRegions, assigning the result into this GreasedRegion, with the special requirement
2609     * that other must be a 64x64 area, and the special property that other will be considered tiled to cover all of the
2610     * area of this GreasedRegion. Any cell that is "on" in both GreasedRegions (treating other as tiling) will be kept
2611     * "on" in this GreasedRegion, but all other cells will be made "off."
2612     * @param other another GreasedRegion that will not be modified but must be 64x64 in size; will act as if it tiles
2613     * @return this, after modification, for chaining
2614     */
2615    public GreasedRegion andWrapping64(GreasedRegion other)
2616    {
2617        for (int x = 0; x < width; x++) {
2618            for (int y = 0; y < ySections; y++) {
2619                data[x * ySections + y] &= other.data[x & 63];
2620            }
2621        }
2622        tallied = false;
2623        return this;
2624    }
2625    /**
2626     * Difference of two GreasedRegions, assigning the result into this GreasedRegion. Any cell that is "on" in this
2627     * GreasedRegion and "off" in other will be kept "on" in this GreasedRegion, but all other cells will be made "off."
2628     * @param other another GreasedRegion that will not be modified
2629     * @return this, after modification, for chaining
2630     * @see #notAnd(GreasedRegion) notAnd is a very similar method that acts sort-of in reverse of this method
2631     */
2632    public GreasedRegion andNot(GreasedRegion other)
2633    {
2634        for (int x = 0; x < width && x < other.width; x++) {
2635            for (int y = 0; y < ySections && y < other.ySections; y++) {
2636                data[x * ySections + y] &= ~other.data[x * other.ySections + y];
2637            }
2638        }
2639        tallied = false;
2640        return this;
2641    }
2642
2643    /**
2644     * Like andNot, but subtracts this GreasedRegion from other and stores the result in this GreasedRegion, without
2645     * mutating other.
2646     * @param other another GreasedRegion that will not be modified
2647     * @return this, after modification, for chaining
2648     * @see #andNot(GreasedRegion) andNot is a very similar method that acts sort-of in reverse of this method
2649     */
2650    public GreasedRegion notAnd(GreasedRegion other)
2651    {
2652        for (int x = 0; x < width && x < other.width; x++) {
2653            for (int y = 0; y < ySections && y < other.ySections; y++) {
2654                data[x * ySections + y] = other.data[x * other.ySections + y] & ~data[x * ySections + y];
2655            }
2656        }
2657        tallied = false;
2658        return this;
2659    }
2660
2661    /**
2662     * Symmetric difference (more commonly known as exclusive or, hence the name) of two GreasedRegions, assigning the
2663     * result into this GreasedRegion. Any cell that is "on" in this and "off" in other, or "off" in this and "on" in
2664     * other, will be made "on" in this; all other cells will be made "off." Useful to find cells that are "on" in
2665     * exactly one of two GreasedRegions (not "on" in both, or "off" in both).
2666     * @param other another GreasedRegion that will not be modified
2667     * @return this, after modification, for chaining
2668     */
2669    public GreasedRegion xor(GreasedRegion other)
2670    {
2671        for (int x = 0; x < width && x < other.width; x++) {
2672            for (int y = 0; y < ySections && y < other.ySections; y++) {
2673                data[x * ySections + y] ^= other.data[x * other.ySections + y];
2674            }
2675        }
2676
2677        if(ySections > 0 && yEndMask != -1) {
2678            for (int a = ySections - 1; a < data.length; a += ySections) {
2679                data[a] &= yEndMask;
2680            }
2681        }
2682        tallied = false;
2683        return this;
2684    }
2685
2686    /**
2687     * Negates this GreasedRegion, turning "on" to "off" and "off" to "on."
2688     * @return this, after modification, for chaining
2689     */
2690    public GreasedRegion not()
2691    {
2692        for (int a = 0; a < data.length; a++)
2693        {
2694            data[a] = ~data[a];
2695        }
2696
2697        if(ySections > 0 && yEndMask != -1) {
2698            for (int a = ySections - 1; a < data.length; a += ySections) {
2699                data[a] &= yEndMask;
2700            }
2701        }
2702        tallied = false;
2703        return this;
2704    }
2705
2706    /**
2707     * Moves the "on" cells in this GreasedRegion to the given x and y offset, removing cells that move out of bounds.
2708     * @param x the x offset to translate by; can be negative
2709     * @param y the y offset to translate by; can be negative
2710     * @return this for chaining
2711     */
2712    public GreasedRegion translate(int x, int y) {
2713        GreasedRegion result = this;
2714        if (width < 1 || ySections <= 0 || (x == 0 && y == 0)) {
2715        } else {
2716            int start = Math.max(0, x), len = Math.min(width, width + x) - start,
2717                    jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63),
2718                    originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump);
2719            long[] data2 = new long[width * ySections];
2720            long prev, tmp;
2721            if (x < 0) {
2722                for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) {
2723                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
2724                        data2[jj * ySections + i] = data[j * ySections + oi];
2725                    }
2726                }
2727            } else if (x > 0) {
2728                for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) {
2729                    for (int j = 0, jj = start; j < len; j++, jj++) {
2730                        data2[jj * ySections + i] = data[j * ySections + oi];
2731                    }
2732                }
2733            } else {
2734                for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) {
2735                    for (int j = 0; j < len; j++) {
2736                        data2[j * ySections + i] = data[j * ySections + oi];
2737                    }
2738                }
2739            }
2740            if (lily < 0) {
2741                for (int i = start; i < len; i++) {
2742                    prev = 0L;
2743                    for (int j = 0; j < ySections; j++) {
2744                        tmp = prev;
2745                        prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily);
2746                        data2[i * ySections + j] >>>= -lily;
2747                        data2[i * ySections + j] |= tmp;
2748                    }
2749                }
2750            } else if (lily > 0) {
2751                for (int i = start; i < start + len; i++) {
2752                    prev = 0L;
2753                    for (int j = 0; j < ySections; j++) {
2754                        tmp = prev;
2755                        prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily);
2756                        data2[i * ySections + j] <<= lily;
2757                        data2[i * ySections + j] |= tmp;
2758                    }
2759                }
2760            }
2761            if (yEndMask != -1) {
2762                for (int a = ySections - 1; a < data2.length; a += ySections) {
2763                    data2[a] &= yEndMask;
2764                }
2765            }
2766            data = data2;
2767            tallied = false;
2768        }
2769
2770
2771        return result;
2772    }
2773
2774    /**
2775     * Adds to this GreasedRegion with a moved set of its own "on" cells, moved to the given x and y offset.
2776     * Ignores cells that would be added out of bounds. Keeps all cells that are currently "on" unchanged.
2777     * @param x the x offset to translate by; can be negative
2778     * @param y the y offset to translate by; can be negative
2779     * @return this for chaining
2780     */
2781    public GreasedRegion insertTranslation(int x, int y) {
2782        GreasedRegion result = this;
2783        if (width < 1 || ySections <= 0 || (x == 0 && y == 0)) {
2784        } else {
2785            int start = Math.max(0, x), len = Math.min(width, width + x) - start,
2786                    jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63),
2787                    originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump);
2788            long[] data2 = new long[width * ySections];
2789            long prev, tmp;
2790            if (x < 0) {
2791                for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) {
2792                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
2793                        data2[jj * ySections + i] = data[j * ySections + oi];
2794                    }
2795                }
2796            } else if (x > 0) {
2797                for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) {
2798                    for (int j = 0, jj = start; j < len; j++, jj++) {
2799                        data2[jj * ySections + i] = data[j * ySections + oi];
2800                    }
2801                }
2802            } else {
2803                for (int i = alterJump, oi = originalJump; i < ySections && oi < ySections; i++, oi++) {
2804                    for (int j = 0; j < len; j++) {
2805                        data2[j * ySections + i] = data[j * ySections + oi];
2806                    }
2807                }
2808            }
2809            if (lily < 0) {
2810                for (int i = start; i < len; i++) {
2811                    prev = 0L;
2812                    for (int j = 0; j < ySections; j++) {
2813                        tmp = prev;
2814                        prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily);
2815                        data2[i * ySections + j] >>>= -lily;
2816                        data2[i * ySections + j] |= tmp;
2817                    }
2818                }
2819            } else if (lily > 0) {
2820                for (int i = start; i < start + len; i++) {
2821                    prev = 0L;
2822                    for (int j = 0; j < ySections; j++) {
2823                        tmp = prev;
2824                        prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily);
2825                        data2[i * ySections + j] <<= lily;
2826                        data2[i * ySections + j] |= tmp;
2827                    }
2828                }
2829            }
2830            for (int i = 0; i < width * ySections; i++) {
2831                data2[i] |= data[i];
2832            }
2833            if (yEndMask != -1) {
2834                for (int a = ySections - 1; a < data2.length; a += ySections) {
2835                    data2[a] &= yEndMask;
2836                }
2837            }
2838            data = data2;
2839            tallied = false;
2840        }
2841
2842
2843        return result;
2844    }
2845
2846    /**
2847     * Effectively doubles the x and y values of each cell this contains (not scaling each cell to be larger, so each
2848     * "on" cell will be surrounded by "off" cells), and re-maps the positions so the given x and y in the doubled space
2849     * become 0,0 in the resulting GreasedRegion (which is this, assigning to itself).
2850     * @param x in the doubled coordinate space, the x position that should become 0 x in the result; can be negative
2851     * @param y in the doubled coordinate space, the y position that should become 0 y in the result; can be negative
2852     * @return this for chaining
2853     */
2854    public GreasedRegion zoom(int x, int y) {
2855        GreasedRegion result = this;
2856        if (width < 1 || ySections <= 0) {
2857        } else {
2858            x = -x;
2859            y = -y;
2860            int
2861                    width2 = width + 1 >>> 1, ySections2 = ySections + 1 >>> 1,
2862                    start = Math.max(0, x), len = Math.min(width, width + x) - start,
2863                    //tall = (Math.min(height, height + y) - Math.max(0, y)) + 63 >> 6,
2864                    jump = (y == 0) ? 0 : (y < 0) ? -(-y >>> 6) : (y >>> 6), lily = (y < 0) ? -(-y & 63) : (y & 63),
2865                    originalJump = Math.max(0, -jump), alterJump = Math.max(0, jump),
2866                    oddX = (x & 1), oddY = (y & 1);
2867            long[] data2 = new long[width * ySections];
2868            long prev, tmp, yEndMask2 = -1L >>> (64 - ((height + 1 >>> 1) & 63));
2869            if (x < 0) {
2870                for (int i = alterJump, oi = originalJump; i <= ySections2 && oi < ySections; i++, oi++) {
2871                    for (int j = Math.max(0, -x), jj = 0; jj < len; j++, jj++) {
2872                        data2[jj * ySections + i] = data[j * ySections + oi];
2873                    }
2874                }
2875            } else if (x > 0) {
2876                for (int i = alterJump, oi = originalJump; i <= ySections2 && oi < ySections; i++, oi++) {
2877                    for (int j = 0, jj = start; j < len; j++, jj++) {
2878                        data2[jj * ySections + i] = data[j * ySections + oi];
2879                    }
2880                }
2881            } else {
2882                for (int i = alterJump, oi = originalJump; i <= ySections2 && oi < ySections; i++, oi++) {
2883                    for (int j = 0; j < len; j++) {
2884                        data2[j * ySections + i] = data[j * ySections + oi];
2885                    }
2886                }
2887            }
2888            if (lily < 0) {
2889                for (int i = start; i < len; i++) {
2890                    prev = 0L;
2891                    for (int j = ySections2; j >= 0; j--) {
2892                        tmp = prev;
2893                        prev = (data2[i * ySections + j] & ~(-1L << -lily)) << (64 + lily);
2894                        data2[i * ySections + j] >>>= -lily;
2895                        data2[i * ySections + j] |= tmp;
2896                    }
2897                }
2898            } else if (lily > 0) {
2899                for (int i = start; i < start + len; i++) {
2900                    prev = 0L;
2901                    for (int j = 0; j < ySections2; j++) {
2902                        tmp = prev;
2903                        prev = (data2[i * ySections + j] & ~(-1L >>> lily)) >>> (64 - lily);
2904                        data2[i * ySections + j] <<= lily;
2905                        data2[i * ySections + j] |= tmp;
2906                    }
2907                }
2908            }
2909            if (yEndMask2 != -1) {
2910                for (int a = ySections2 - 1; a < data2.length; a += ySections) {
2911                    data2[a] &= yEndMask2;
2912                    if (ySections2 < ySections)
2913                        data2[a + 1] = 0L;
2914                }
2915            }
2916            for (int i = 0; i < width2; i++) {
2917                for (int j = 0; j < ySections2; j++) {
2918                    prev = data2[i * ySections + j];
2919                    tmp = prev >>> 32;
2920                    prev &= 0xFFFFFFFFL;
2921                    prev = (prev | (prev << 16)) & 0x0000FFFF0000FFFFL;
2922                    prev = (prev | (prev << 8)) & 0x00FF00FF00FF00FFL;
2923                    prev = (prev | (prev << 4)) & 0x0F0F0F0F0F0F0F0FL;
2924                    prev = (prev | (prev << 2)) & 0x3333333333333333L;
2925                    prev = (prev | (prev << 1)) & 0x5555555555555555L;
2926                    prev <<= oddY;
2927                    if (oddX == 1) {
2928                        if (i * 2 + 1 < width)
2929                            data[(i * ySections + j) * 2 + ySections] = prev;
2930                        if (i * 2 < width)
2931                            data[(i * ySections + j) * 2] = 0L;
2932                    } else {
2933                        if (i * 2 < width)
2934                            data[(i * ySections + j) * 2] = prev;
2935                        if (i * 2 + 1 < width)
2936                            data[(i * ySections + j) * 2 + ySections] = 0L;
2937                    }
2938                    if (j * 2 + 1 < ySections) {
2939                        tmp = (tmp | (tmp << 16)) & 0x0000FFFF0000FFFFL;
2940                        tmp = (tmp | (tmp << 8)) & 0x00FF00FF00FF00FFL;
2941                        tmp = (tmp | (tmp << 4)) & 0x0F0F0F0F0F0F0F0FL;
2942                        tmp = (tmp | (tmp << 2)) & 0x3333333333333333L;
2943                        tmp = (tmp | (tmp << 1)) & 0x5555555555555555L;
2944                        tmp <<= oddY;
2945                        if (oddX == 1) {
2946                            if (i * 2 + 1 < width)
2947                                data[(i * ySections + j) * 2 + ySections + 1] = tmp;
2948                            if (i * 2 < width)
2949                                data[(i * ySections + j) * 2 + 1] = 0L;
2950                        } else {
2951                            if (i * 2 < width)
2952                                data[(i * ySections + j) * 2 + 1] = tmp;
2953                            if (i * 2 + 1 < width)
2954                                data[(i * ySections + j) * 2 + ySections + 1] = 0L;
2955                        }
2956                    }
2957                }
2958            }
2959            if (yEndMask != -1) {
2960                for (int a = ySections - 1; a < data.length; a += ySections) {
2961                    data[a] &= yEndMask;
2962                }
2963            }
2964            tallied = false;
2965        }
2966
2967
2968        return result;
2969    }
2970
2971    /**
2972     * Takes the pairs of "on" cells in this GreasedRegion that are separated by exactly one cell in an orthogonal line,
2973     * and changes the gap cells to "on" as well.
2974     * <br>
2975     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
2976     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
2977     * very well by operating in bulk on up to 64 cells at a time.
2978     * @return this for chaining
2979     */
2980    public GreasedRegion connect() {
2981        GreasedRegion result = this;
2982        if (width < 2 || ySections == 0) {
2983        } else {
2984            final long[] next = new long[width * ySections];
2985            System.arraycopy(data, 0, next, 0, width * ySections);
2986            for (int a = 0; a < ySections; a++) {
2987                next[a] |= ((data[a] << 1) & (data[a] >>> 1)) | data[a + ySections];
2988                next[(width - 1) * ySections + a] |= ((data[(width - 1) * ySections + a] << 1) & (data[(width - 1) * ySections + a] >>> 1)) | data[(width - 2) * ySections + a];
2989
2990                for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
2991                    next[i] |= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections]);
2992                }
2993
2994                if (a > 0) {
2995                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
2996                        next[i] |= (data[i - 1] & 0x8000000000000000L) >>> 63 & (data[i] >>> 1);
2997                    }
2998                } else {
2999                    for (int i = ySections; i < (width - 1) * ySections; i += ySections) {
3000                        next[i] |= (data[i] >>> 1 & 1L);
3001                    }
3002                }
3003
3004                if (a < ySections - 1) {
3005                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3006                        next[i] |= (data[i + 1] & 1L) << 63 & (data[i] << 1);
3007                    }
3008                } else {
3009                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3010                        next[i] |= (data[i] << 1 & 0x8000000000000000L);
3011                    }
3012
3013                }
3014            }
3015            if (ySections > 0 && yEndMask != -1) {
3016                for (int a = ySections - 1; a < next.length; a += ySections) {
3017                    next[a] &= yEndMask;
3018                }
3019            }
3020            data = next;
3021            tallied = false;
3022        }
3023
3024        return result;
3025    }
3026
3027    /**
3028     * Takes the pairs of "on" cells in this GreasedRegion that are separated by exactly one cell in an orthogonal or
3029     * diagonal line, and changes the gap cells to "on" as well.
3030     * <br>
3031     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3032     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3033     * very well by operating in bulk on up to 64 cells at a time.
3034     * @return this for chaining
3035     */
3036    public GreasedRegion connect8way() {
3037        GreasedRegion result = this;
3038        if (width < 2 || ySections == 0) {
3039        } else {
3040            final long[] next = new long[width * ySections];
3041            System.arraycopy(data, 0, next, 0, width * ySections);
3042            for (int a = 0; a < ySections; a++) {
3043                next[a] |= ((data[a] << 1) & (data[a] >>> 1)) | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1);
3044                next[(width - 1) * ySections + a] |= ((data[(width - 1) * ySections + a] << 1) & (data[(width - 1) * ySections + a] >>> 1))
3045                        | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1);
3046
3047                for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3048                    next[i] |= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections])
3049                            | ((data[i - ySections] << 1) & (data[i + ySections] >>> 1))
3050                            | ((data[i + ySections] << 1) & (data[i - ySections] >>> 1));
3051                }
3052
3053                if (a > 0) {
3054                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3055                        next[i] |= ((data[i - 1] & 0x8000000000000000L) >>> 63 & (data[i] >>> 1)) |
3056                                ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i + ySections] >>> 1)) |
3057                                ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i - ySections] >>> 1));
3058                    }
3059                } else {
3060                    for (int i = ySections; i < (width - 1) * ySections; i += ySections) {
3061                        next[i] |= (data[i] >>> 1 & 1L) | (data[i - ySections] >>> 1 & 1L) | (data[i + ySections] >>> 1 & 1L);
3062                    }
3063                }
3064
3065                if (a < ySections - 1) {
3066                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3067                        next[i] |= ((data[i + 1] & 1L) << 63 & (data[i] << 1)) |
3068                                ((data[i - ySections + 1] & 1L) << 63 & (data[i + ySections] << 1)) |
3069                                ((data[i + ySections + 1] & 1L) << 63 & (data[i - ySections] << 1));
3070                    }
3071                } else {
3072                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3073                        next[i] |= (data[i] << 1 & 0x8000000000000000L)
3074                                | (data[i - ySections] << 1 & 0x8000000000000000L) | (data[i + ySections] << 1 & 0x8000000000000000L);
3075                    }
3076
3077                }
3078            }
3079            if (ySections > 0 && yEndMask != -1) {
3080                for (int a = ySections - 1; a < next.length; a += ySections) {
3081                    next[a] &= yEndMask;
3082                }
3083            }
3084            data = next;
3085            tallied = false;
3086        }
3087
3088        return result;
3089    }
3090    /**
3091     * Takes the pairs of "on" cells in this GreasedRegion that are separated by exactly one cell in an orthogonal or
3092     * diagonal line, and changes the gap cells to "on" as well. As a special case, this requires diagonals to either
3093     * have no "on" cells adjacent along the perpendicular diagonal, or both cells on that perpendicular diagonal need
3094     * to be "on." This is useful to counteract some less-desirable behavior of {@link #connect8way()}, where a right
3095     * angle would always get the inner corners filled because it was considered a diagonal.
3096     * <br>
3097     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3098     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3099     * very well by operating in bulk on up to 64 cells at a time.
3100     * @return this for chaining
3101     */
3102    public GreasedRegion connectLines() {
3103        GreasedRegion result = this;
3104        if (width < 2 || ySections == 0) {
3105        } else {
3106            final long[] next = new long[width * ySections];
3107            System.arraycopy(data, 0, next, 0, width * ySections);
3108            for (int a = 0; a < ySections; a++) {
3109                next[a] |= ((data[a] << 1) & (data[a] >>> 1)) | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1);
3110                next[(width - 1) * ySections + a] |= ((data[(width - 1) * ySections + a] << 1) & (data[(width - 1) * ySections + a] >>> 1))
3111                        | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1);
3112
3113                for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3114                    next[i] |= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections])
3115                            | (((data[i - ySections] << 1) & (data[i + ySections] >>> 1))
3116                            ^ ((data[i + ySections] << 1) & (data[i - ySections] >>> 1)));
3117                }
3118
3119                if (a > 0) {
3120                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3121                        next[i] |= ((data[i - 1] & 0x8000000000000000L) >>> 63 & (data[i] >>> 1))
3122                                | (((data[i - ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i + ySections] >>> 1))
3123                                ^ ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63 & (data[i - ySections] >>> 1)));
3124                    }
3125                } else {
3126                    for (int i = ySections; i < (width - 1) * ySections; i += ySections) {
3127                        next[i] |= (data[i] >>> 1 & 1L) | (data[i - ySections] >>> 1 & 1L) | (data[i + ySections] >>> 1 & 1L);
3128                    }
3129                }
3130
3131                if (a < ySections - 1) {
3132                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3133                        next[i] |= ((data[i + 1] & 1L) << 63 & (data[i] << 1))
3134                                | (((data[i - ySections + 1] & 1L) << 63 & (data[i + ySections] << 1))
3135                                ^ ((data[i + ySections + 1] & 1L) << 63 & (data[i - ySections] << 1)));
3136                    }
3137                } else {
3138                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3139                        next[i] |= (data[i] << 1 & 0x8000000000000000L)
3140                                | (data[i - ySections] << 1 & 0x8000000000000000L) | (data[i + ySections] << 1 & 0x8000000000000000L);
3141                    }
3142
3143                }
3144            }
3145            if (ySections > 0 && yEndMask != -1) {
3146                for (int a = ySections - 1; a < next.length; a += ySections) {
3147                    next[a] &= yEndMask;
3148                }
3149            }
3150            data = next;
3151            tallied = false;
3152        }
3153
3154        return result;
3155    }
3156
3157    /**
3158     * Like {@link #retract()}, this reduces the width of thick areas of this GreasedRegion, but thin() will not remove
3159     * areas that would be identical in a subsequent call to retract(), such as if the area would be eliminated. This
3160     * is useful primarily for adjusting areas so they do not exceed a width of 2 cells, though their length (the longer
3161     * of the two dimensions) will be unaffected by this. Especially wide, irregularly-shaped areas may have unintended
3162     * appearances if you call this repeatedly or use {@link #thinFully()}; consider using this sparingly, or primarily
3163     * when an area has just gotten thicker than desired.
3164     * <br>
3165     * This currently uses 4-way adjacency, but had previously used 8-way; if you want the behavior this previously had,
3166     * you can use {@link #thin8way()}, but it may be a good idea to try this method as well (some of the old behavior
3167     * had problems where it yielded significantly larger minimum widths in some areas).
3168     * @return this for chaining
3169     */
3170    public GreasedRegion thin() {
3171        GreasedRegion result = this;
3172        if (width <= 2 || ySections <= 0) {
3173        } else {
3174            GreasedRegion c1 = new GreasedRegion(this).retract(),
3175                    c2 = new GreasedRegion(c1).expand().xor(this).expand().and(this);
3176            remake(c1).or(c2);/*
3177        System.out.println("\n\nc1:\n" + c1.toString() + "\n");
3178        System.out.println("\n\nc2:\n" + c2.toString() + "\n");
3179        System.out.println("\n\nthis:\n" + toString() + "\n");
3180        */
3181        }
3182        return result;
3183    }
3184
3185    /**
3186     * Calls {@link #thin()} repeatedly, until the result is unchanged from the last call. Consider using the idiom
3187     * {@code expand8way().retract().thinFully()} to help change a possibly-strange appearance when the GreasedRegion
3188     * this is called on touches the edges of the grid. In general, this method is likely to go too far when it tries to
3189     * thin a round or irregular area, and this often results in many diagonal lines spanning the formerly-thick area.
3190     * <br>
3191     * This currently uses 4-way adjacency, but had previously used 8-way; if you want the behavior this previously had,
3192     * you can use {@link #thinFully8way()}, but it may be a good idea to try this method as well (some of the old
3193     * behavior had problems where it yielded significantly larger minimum widths in some areas).
3194     * @return this for chaining
3195     */
3196    public GreasedRegion thinFully()
3197    {
3198        while (size() != thin().size());
3199        return this;
3200    }
3201
3202
3203    /**
3204     * Like {@link #retract8way()}, this reduces the width of thick areas of this GreasedRegion, but thin8way() will not
3205     * remove areas that would be identical in a subsequent call to retract8way(), such as if the area would be
3206     * eliminated. This is useful primarily for adjusting areas so they do not exceed a width of 2 cells, though their
3207     * length (the longer of the two dimensions) will be unaffected by this. Especially wide, irregularly-shaped areas
3208     * may have unintended appearances if you call this repeatedly or use {@link #thinFully8way()}; consider using this
3209     * sparingly, or primarily when an area has just gotten thicker than desired.
3210     * <br>
3211     * This method was called {@link #thin()}, but now that name refers to a variant that uses 4-way adjacency.
3212     * @return this for chaining
3213     */
3214    public GreasedRegion thin8way() {
3215        GreasedRegion result = this;
3216        if (width <= 2 || ySections <= 0) {
3217        } else {
3218            GreasedRegion c1 = new GreasedRegion(this).retract8way(),
3219                    c2 = new GreasedRegion(c1).expand8way().xor(this).expand8way().and(this);
3220            remake(c1).or(c2);
3221        }
3222        return result;
3223    }
3224
3225    /**
3226     * Calls {@link #thin8way()} repeatedly, until the result is unchanged from the last call. Consider using the idiom
3227     * {@code expand8way().retract().thinFully8way()} to help change a strange appearance when the GreasedRegion this is
3228     * called on touches the edges of the grid. In general, this method is likely to go too far when it tries to thin a
3229     * round or irregular area, and this often results in many diagonal lines spanning the formerly-thick area.
3230     * <br>
3231     * This method was called {@link #thinFully()}, but now that name refers to a variant that uses 4-way adjacency.
3232     * @return this for chaining
3233     */
3234    public GreasedRegion thinFully8way()
3235    {
3236        while (size() != thin8way().size());
3237        return this;
3238    }
3239
3240
3241    /**
3242     * Removes "on" cells that are orthogonally adjacent to other "on" cells, keeping at least one cell in a group "on."
3243     * Uses a "checkerboard" pattern to determine which cells to turn  off, with all cells that would be black on a
3244     * checkerboard turned off and all others kept as-is.
3245     * @return this for chaining
3246     */
3247    public GreasedRegion disperse() {
3248        GreasedRegion result = this;
3249        if (width < 1 || ySections <= 0) {
3250        } else {
3251            long mask = 0x5555555555555555L;
3252            for (int i = 0; i < width; i++) {
3253                for (int j = 0; j < ySections; j++) {
3254                    data[j] &= mask;
3255                }
3256                mask = ~mask;
3257            }
3258            tallied = false;
3259        }
3260        return result;
3261    }
3262    /**
3263     * Removes "on" cells that are 8-way adjacent to other "on" cells, keeping at least one cell in a group "on."
3264     * Uses a "grid-like" pattern to determine which cells to turn off, with all cells with even x and even y kept as-is
3265     * but all other cells (with either or both odd x or odd y) turned off.
3266     * @return this for chaining
3267     */
3268    public GreasedRegion disperse8way() {
3269        GreasedRegion result = this;
3270        if (width < 1 || ySections <= 0) {
3271        } else {
3272            int len = data.length;
3273            long mask = 0x5555555555555555L;
3274            for (int j = 0; j < len - 1; j += 2) {
3275                data[j] &= mask;
3276                data[j + 1] = 0;
3277            }
3278            tallied = false;
3279        }
3280        return result;
3281    }
3282    /**
3283     * Removes "on" cells that are nearby other "on" cells, with a random factor to which bits are actually turned off
3284     * that still ensures exactly half of the bits are kept as-is (the one exception is when height is an odd number,
3285     * which makes the bottom row slightly random).
3286     * @param random the RNG used for a random factor
3287     * @return this for chaining
3288     */
3289    public GreasedRegion disperseRandom(RandomnessSource random) {
3290        GreasedRegion result = this;
3291        if (width < 1 || ySections <= 0) {
3292        } else {
3293            int len = data.length;
3294            for (int j = 0; j < len; j++) {
3295                data[j] &= randomInterleave(random);
3296            }
3297            tallied = false;
3298        }
3299        return result;
3300    }
3301    /**
3302     * Generates a random 64-bit long with a number of '1' bits (Hamming weight) equal on average to bitCount.
3303     * For example, calling this with a parameter of 32 will be equivalent to calling nextLong() on the given
3304     * RandomnessSource, which could be an {@link RNG} or any RandomnessSource with good 64-bit generation quality.
3305     * Calling this with a parameter of 16 will have on average 16 of the 64 bits in the returned long set to '1',
3306     * distributed pseudo-randomly, while a parameter of 47 will have on average 47 bits set. This can be useful for
3307     * certain code that uses bits to represent data but needs a different ratio of set bits to unset bits than 1:1.
3308     * <br>
3309     * The parameter {@code random} can be an object like a {@link DiverRNG}, an {@link RNG} backed by a
3310     * well-distributed RandomnessSource like its default, DiverRNG, a {@link GWTRNG} (especially if you target GWT,
3311     * where it will perform much better than most alternatives), or any of various other RandomnessSource
3312     * implementations that distribute bits well for {@link RandomnessSource#nextLong()}, but should not be
3313     * intentionally-biased RNGs like {@link DharmaRNG} or {@link EditRNG}, nor double-based QRNGs like
3314     * {@link VanDerCorputQRNG} or {@link SobolQRNG}.
3315     * @param random used to determine random factors; likely to be an {@link RNG}, {@link DiverRNG}, or {@link GWTRNG}
3316     * @param bitCount an int, only considered if between 0 and 64, that is the average number of bits to set
3317     * @return a 64-bit long that, on average, should have bitCount bits set to 1, potentially anywhere in the long
3318     */
3319    public static long approximateBits(RandomnessSource random, int bitCount) {
3320        long result;
3321        if (bitCount <= 0) {
3322            result = 0L;
3323        } else if (bitCount >= 64) {
3324            result = -1L;
3325        } else if (bitCount == 32) {
3326            result = random.nextLong();
3327        } else {
3328            boolean high = bitCount > 32;
3329            int altered = (high ? 64 - bitCount : bitCount), lsb = NumberTools.lowestOneBit(altered);
3330            long data = random.nextLong();
3331            for (int i = lsb << 1; i <= 16; i <<= 1) {
3332                if ((altered & i) == 0)
3333                    data &= random.nextLong();
3334                else
3335                    data |= random.nextLong();
3336            }
3337            result = high ? ~(random.nextLong() & data) : (random.nextLong() & data);
3338        }
3339        return result;
3340    }
3341
3342    /**
3343     * Gets a somewhat-random long with exactly 32 bits set; in each pair of bits starting at bit 0 and bit 1, then bit
3344     * 2 and bit 3, up to bit 62 and bit 3, one bit will be 1 and one bit will be 0 in each pair.
3345     * <br>
3346     * Not exactly general-use; meant for generating data for GreasedRegion.
3347     * @return a random long with 32 "1" bits, distributed so exactly one bit is "1" for each pair of bits
3348     */
3349    public static long randomInterleave(RandomnessSource random) {
3350        long bits = random.nextLong() & 0xFFFFFFFFL, ib = ~bits & 0xFFFFFFFFL;
3351        bits |= (bits << 16);
3352        ib |= (ib << 16);
3353        bits &= 0x0000FFFF0000FFFFL;
3354        ib &= 0x0000FFFF0000FFFFL;
3355        bits |= (bits << 8);
3356        ib |= (ib << 8);
3357        bits &= 0x00FF00FF00FF00FFL;
3358        ib &= 0x00FF00FF00FF00FFL;
3359        bits |= (bits << 4);
3360        ib |= (ib << 4);
3361        bits &= 0x0F0F0F0F0F0F0F0FL;
3362        ib &= 0x0F0F0F0F0F0F0F0FL;
3363        bits |= (bits << 2);
3364        ib |= (ib << 2);
3365        bits &= 0x3333333333333333L;
3366        ib &= 0x3333333333333333L;
3367        bits |= (bits << 1);
3368        ib |= (ib << 1);
3369        bits &= 0x5555555555555555L;
3370        ib &= 0x5555555555555555L;
3371        return (bits | (ib << 1));
3372    }
3373
3374    /**
3375     * Takes the "on" cells in this GreasedRegion and expands them by one cell in the 4 orthogonal directions, making
3376     * each "on" cell take up a plus-shaped area that may overlap with other "on" cells (which is just a normal "on"
3377     * cell then).
3378     * <br>
3379     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3380     * methods (including expand(), {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3381     * very well by operating in bulk on up to 64 cells at a time.
3382     * @return this for chaining
3383     */
3384    public GreasedRegion expand() {
3385        GreasedRegion result = this;
3386        if (width < 2 || ySections == 0) {
3387        } else {
3388            final long[] next = new long[width * ySections];
3389            System.arraycopy(data, 0, next, 0, width * ySections);
3390            for (int a = 0; a < ySections; a++) {
3391                next[a] |= (data[a] << 1) | (data[a] >>> 1) | data[a + ySections];
3392                next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a] << 1) | (data[(width - 1) * ySections + a] >>> 1) | data[(width - 2) * ySections + a];
3393
3394                for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3395                    next[i] |= (data[i] << 1) | (data[i] >>> 1) | data[i - ySections] | data[i + ySections];
3396                }
3397
3398                if (a > 0) {
3399                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3400                        next[i] |= (data[i - 1] & 0x8000000000000000L) >>> 63;
3401                    }
3402                }
3403
3404                if (a < ySections - 1) {
3405                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3406                        next[i] |= (data[i + 1] & 1L) << 63;
3407                    }
3408                }
3409            }
3410            if (ySections > 0 && yEndMask != -1) {
3411                for (int a = ySections - 1; a < next.length; a += ySections) {
3412                    next[a] &= yEndMask;
3413                }
3414            }
3415            data = next;
3416            tallied = false;
3417        }
3418
3419        return result;
3420    }
3421    /**
3422     * Takes the "on" cells in this GreasedRegion and expands them by amount cells in the 4 orthogonal directions,
3423     * making each "on" cell take up a plus-shaped area that may overlap with other "on" cells (which is just a normal
3424     * "on" cell then).
3425     * <br>
3426     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3427     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3428     * very well by operating in bulk on up to 64 cells at a time.
3429     * @return this for chaining
3430     */
3431    @Override
3432    public GreasedRegion expand(int amount)
3433    {
3434        for (int i = 0; i < amount; i++) {
3435            expand();
3436        }
3437        return this;
3438    }
3439    /**
3440     * Takes the "on" cells in this GreasedRegion and produces amount GreasedRegions, each one expanded by 1 cell in
3441     * the 4 orthogonal directions relative to the previous GreasedRegion, making each "on" cell take up a plus-shaped
3442     * area that may overlap with other "on" cells (which is just a normal "on" cell then). This returns an array of
3443     * GreasedRegions with progressively greater expansions, and does not modify this GreasedRegion.
3444     * <br>
3445     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3446     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3447     * very well by operating in bulk on up to 64 cells at a time.
3448     * @return an array of new GreasedRegions, length == amount, where each one is expanded by 1 relative to the last
3449     */
3450    public GreasedRegion[] expandSeries(int amount) {
3451        GreasedRegion[] result;
3452        if (amount <= 0) {
3453            result = new GreasedRegion[0];
3454        } else {
3455            GreasedRegion[] regions = new GreasedRegion[amount];
3456            GreasedRegion temp = new GreasedRegion(this);
3457            for (int i = 0; i < amount; i++) {
3458                regions[i] = new GreasedRegion(temp.expand());
3459            }
3460            result = regions;
3461        }
3462        return result;
3463    }
3464
3465    public ArrayList<GreasedRegion> expandSeriesToLimit()
3466    {
3467        ArrayList<GreasedRegion> regions = new ArrayList<>();
3468        GreasedRegion temp = new GreasedRegion(this);
3469        while (temp.size() != temp.expand().size()) {
3470            regions.add(new GreasedRegion(temp));
3471        }
3472        return regions;
3473    }
3474    /**
3475     * Takes the "on" cells in this GreasedRegion and expands them by one cell in the 4 orthogonal directions, producing
3476     * a diamoond shape, then removes the original area before expansion, producing only the cells that were "off" in
3477     * this and within 1 cell (orthogonal-only) of an "on" cell. This method is similar to {@link #surface()}, but
3478     * surface finds cells inside the current GreasedRegion, while fringe finds cells outside it.
3479     * <br>
3480     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3481     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3482     * very well by operating in bulk on up to 64 cells at a time. The surface and fringe methods do allocate one
3483     * temporary GreasedRegion to store the original before modification, but the others generally don't.
3484     * @return this for chaining
3485     */
3486    public GreasedRegion fringe()
3487    {
3488        GreasedRegion cpy = new GreasedRegion(this);
3489        expand();
3490        return andNot(cpy);
3491    }
3492    /**
3493     * Takes the "on" cells in this GreasedRegion and expands them by amount cells in the 4 orthogonal directions
3494     * (iteratively, producing a diamond shape), then removes the original area before expansion, producing only the
3495     * cells that were "off" in this and within amount cells (orthogonal-only) of an "on" cell. This method is similar
3496     * to {@link #surface()}, but surface finds cells inside the current GreasedRegion, while fringe finds cells outside
3497     * it.
3498     * <br>
3499     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3500     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3501     * very well by operating in bulk on up to 64 cells at a time. The surface and fringe methods do allocate one
3502     * temporary GreasedRegion to store the original before modification, but the others generally don't.
3503     * @return this for chaining
3504     */
3505
3506    public GreasedRegion fringe(int amount)
3507    {
3508        GreasedRegion cpy = new GreasedRegion(this);
3509        expand(amount);
3510        return andNot(cpy);
3511    }
3512
3513    /**
3514     * Takes the "on" cells in this GreasedRegion and produces amount GreasedRegions, each one expanded by 1 cell in
3515     * the 4 orthogonal directions relative to the previous GreasedRegion, making each "on" cell take up a diamond-
3516     * shaped area. After producing the expansions, this removes the previous GreasedRegion from the next GreasedRegion
3517     * in the array, making each "fringe" in the series have 1 "thickness," which can be useful for finding which layer
3518     * of expansion a cell lies in. This returns an array of GreasedRegions with progressively greater expansions
3519     * without the cells of this GreasedRegion, and does not modify this GreasedRegion.
3520     * <br>
3521     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3522     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3523     * very well by operating in bulk on up to 64 cells at a time.
3524     * @return an array of new GreasedRegions, length == amount, where each one is a 1-depth fringe pushed further out from this
3525     */
3526    public GreasedRegion[] fringeSeries(int amount) {
3527        GreasedRegion[] result;
3528        if (amount <= 0) {
3529            result = new GreasedRegion[0];
3530        } else {
3531            GreasedRegion[] regions = new GreasedRegion[amount];
3532            GreasedRegion temp = new GreasedRegion(this);
3533            regions[0] = new GreasedRegion(temp);
3534            for (int i = 1; i < amount; i++) {
3535                regions[i] = new GreasedRegion(temp.expand());
3536            }
3537            for (int i = 0; i < amount - 1; i++) {
3538                regions[i].xor(regions[i + 1]);
3539            }
3540            regions[amount - 1].fringe();
3541            result = regions;
3542        }
3543        return result;
3544    }
3545    public ArrayList<GreasedRegion> fringeSeriesToLimit()
3546    {
3547        ArrayList<GreasedRegion> regions = expandSeriesToLimit();
3548        for (int i = regions.size() - 1; i > 0; i--) {
3549            regions.get(i).xor(regions.get(i-1));
3550        }
3551        regions.get(0).xor(this);
3552        return regions;
3553    }
3554
3555    /**
3556     * Takes the "on" cells in this GreasedRegion and retracts them by one cell in the 4 orthogonal directions,
3557     * making each "on" cell that was orthogonally adjacent to an "off" cell into an "off" cell.
3558     * <br>
3559     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3560     * methods (including {@link #expand()}, retract(), {@link #fringe()}, and {@link #surface()}) all perform
3561     * very well by operating in bulk on up to 64 cells at a time.
3562     * @return this for chaining
3563     */
3564    public GreasedRegion retract() {
3565        GreasedRegion result = this;
3566        if (width <= 2 || ySections <= 0) {
3567        } else {
3568            final long[] next = new long[width * ySections];
3569            System.arraycopy(data, ySections, next, ySections, (width - 2) * ySections);
3570            for (int a = 0; a < ySections; a++) {
3571                if (a > 0 && a < ySections - 1) {
3572                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3573                        next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63))
3574                                & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63))
3575                                & data[i - ySections]
3576                                & data[i + ySections];
3577                    }
3578                } else if (a > 0) {
3579                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3580                        next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63))
3581                                & (data[i] >>> 1)
3582                                & data[i - ySections]
3583                                & data[i + ySections];
3584                    }
3585                } else if (a < ySections - 1) {
3586                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3587                        next[i] &= (data[i] << 1)
3588                                & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63))
3589                                & data[i - ySections]
3590                                & data[i + ySections];
3591                    }
3592                } else // only the case when ySections == 1
3593                {
3594                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3595                        next[i] &= (data[i] << 1) & (data[i] >>> 1) & data[i - ySections] & data[i + ySections];
3596                    }
3597                }
3598            }
3599            if (yEndMask != -1) {
3600                for (int a = ySections - 1; a < next.length; a += ySections) {
3601                    next[a] &= yEndMask;
3602                }
3603            }
3604            data = next;
3605            tallied = false;
3606        }
3607
3608        return result;
3609    }
3610
3611    /**
3612     * Takes the "on" cells in this GreasedRegion and retracts them by one cell in the 4 orthogonal directions, doing
3613     * this iteeratively amount times, making each "on" cell that was within amount orthogonal distance to an "off" cell
3614     * into an "off" cell.
3615     * <br>
3616     * This method is very efficient due to how the class is implemented, and the various spatial increase/decrease
3617     * methods (including {@link #expand()}, {@link #retract()}, {@link #fringe()}, and {@link #surface()}) all perform
3618     * very well by operating in bulk on up to 64 cells at a time.
3619     * @return this for chaining
3620     */
3621    public GreasedRegion retract(int amount)
3622    {
3623        for (int i = 0; i < amount; i++) {
3624            retract();
3625        }
3626        return this;
3627    }
3628
3629    public GreasedRegion[] retractSeries(int amount) {
3630        GreasedRegion[] result;
3631        if (amount <= 0) {
3632            result = new GreasedRegion[0];
3633        } else {
3634            GreasedRegion[] regions = new GreasedRegion[amount];
3635            GreasedRegion temp = new GreasedRegion(this);
3636            for (int i = 0; i < amount; i++) {
3637                regions[i] = new GreasedRegion(temp.retract());
3638            }
3639            result = regions;
3640        }
3641        return result;
3642    }
3643
3644    public ArrayList<GreasedRegion> retractSeriesToLimit()
3645    {
3646        ArrayList<GreasedRegion> regions = new ArrayList<>();
3647        GreasedRegion temp = new GreasedRegion(this);
3648        while (!temp.retract().isEmpty()) {
3649            regions.add(new GreasedRegion(temp));
3650        }
3651        return regions;
3652    }
3653
3654    public GreasedRegion surface()
3655    {
3656        GreasedRegion cpy = new GreasedRegion(this).retract();
3657        return xor(cpy);
3658    }
3659    public GreasedRegion surface(int amount)
3660    {
3661        GreasedRegion cpy = new GreasedRegion(this).retract(amount);
3662        return xor(cpy);
3663    }
3664
3665    public GreasedRegion[] surfaceSeries(int amount)
3666    {
3667        if(amount <= 0) return new GreasedRegion[0];
3668        GreasedRegion[] regions = new GreasedRegion[amount];
3669        GreasedRegion temp = new GreasedRegion(this);
3670        regions[0] = new GreasedRegion(temp);
3671        for (int i = 1; i < amount; i++) {
3672            regions[i] = new GreasedRegion(temp.retract());
3673        }
3674        for (int i = 0; i < amount - 1; i++) {
3675            regions[i].xor(regions[i + 1]);
3676        }
3677        regions[amount - 1].surface();
3678        return regions;
3679    }
3680
3681    public ArrayList<GreasedRegion> surfaceSeriesToLimit() {
3682        ArrayList<GreasedRegion> result;
3683        ArrayList<GreasedRegion> regions = retractSeriesToLimit();
3684        if (regions.isEmpty()) {
3685            result = regions;
3686        } else {
3687            regions.add(0, regions.get(0).copy().xor(this));
3688            for (int i = 1; i < regions.size() - 1; i++) {
3689                regions.get(i).xor(regions.get(i + 1));
3690            }
3691            result = regions;
3692        }
3693        return result;
3694    }
3695    public GreasedRegion expand8way() {
3696        GreasedRegion result = this;
3697        if (width < 2 || ySections <= 0) {
3698        } else {
3699            final long[] next = new long[width * ySections];
3700            System.arraycopy(data, 0, next, 0, width * ySections);
3701            for (int a = 0; a < ySections; a++) {
3702                next[a] |= (data[a] << 1) | (data[a] >>> 1)
3703                        | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1);
3704                next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a] << 1) | (data[(width - 1) * ySections + a] >>> 1)
3705                        | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1);
3706
3707                for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3708                    next[i] |= (data[i] << 1) | (data[i] >>> 1)
3709                            | data[i - ySections] | (data[i - ySections] << 1) | (data[i - ySections] >>> 1)
3710                            | data[i + ySections] | (data[i + ySections] << 1) | (data[i + ySections] >>> 1);
3711                }
3712
3713                if (a > 0) {
3714                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3715                        next[i] |= ((data[i - 1] & 0x8000000000000000L) >>> 63) |
3716                                ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63) |
3717                                ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63);
3718                    }
3719                }
3720
3721                if (a < ySections - 1) {
3722                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3723                        next[i] |= ((data[i + 1] & 1L) << 63) |
3724                                ((data[i - ySections + 1] & 1L) << 63) |
3725                                ((data[i + ySections + 1] & 1L) << 63);
3726                    }
3727                }
3728            }
3729            if (ySections > 0 && yEndMask != -1) {
3730                for (int a = ySections - 1; a < next.length; a += ySections) {
3731                    next[a] &= yEndMask;
3732                }
3733            }
3734            data = next;
3735            tallied = false;
3736        }
3737
3738        return result;
3739    }
3740
3741    @Override
3742    public GreasedRegion expand8way(int amount)
3743    {
3744        for (int i = 0; i < amount; i++) {
3745            expand8way();
3746        }
3747        return this;
3748    }
3749
3750    public GreasedRegion[] expandSeries8way(int amount) {
3751        GreasedRegion[] result;
3752        if (amount <= 0) {
3753            result = new GreasedRegion[0];
3754        } else {
3755            GreasedRegion[] regions = new GreasedRegion[amount];
3756            GreasedRegion temp = new GreasedRegion(this);
3757            for (int i = 0; i < amount; i++) {
3758                regions[i] = new GreasedRegion(temp.expand8way());
3759            }
3760            result = regions;
3761        }
3762        return result;
3763    }
3764    public ArrayList<GreasedRegion> expandSeriesToLimit8way()
3765    {
3766        ArrayList<GreasedRegion> regions = new ArrayList<>();
3767        GreasedRegion temp = new GreasedRegion(this);
3768        while (temp.size() != temp.expand8way().size()) {
3769            regions.add(new GreasedRegion(temp));
3770        }
3771        return regions;
3772    }
3773
3774    public GreasedRegion fringe8way()
3775    {
3776        GreasedRegion cpy = new GreasedRegion(this);
3777        expand8way();
3778        return andNot(cpy);
3779    }
3780    public GreasedRegion fringe8way(int amount)
3781    {
3782        GreasedRegion cpy = new GreasedRegion(this);
3783        expand8way(amount);
3784        return andNot(cpy);
3785    }
3786
3787    public GreasedRegion[] fringeSeries8way(int amount) {
3788        GreasedRegion[] result;
3789        if (amount <= 0) {
3790            result = new GreasedRegion[0];
3791        } else {
3792            GreasedRegion[] regions = new GreasedRegion[amount];
3793            GreasedRegion temp = new GreasedRegion(this);
3794            regions[0] = new GreasedRegion(temp);
3795            for (int i = 1; i < amount; i++) {
3796                regions[i] = new GreasedRegion(temp.expand8way());
3797            }
3798            for (int i = 0; i < amount - 1; i++) {
3799                regions[i].xor(regions[i + 1]);
3800            }
3801            regions[amount - 1].fringe8way();
3802            result = regions;
3803        }
3804        return result;
3805    }
3806    public ArrayList<GreasedRegion> fringeSeriesToLimit8way()
3807    {
3808        ArrayList<GreasedRegion> regions = expandSeriesToLimit8way();
3809        for (int i = regions.size() - 1; i > 0; i--) {
3810            regions.get(i).xor(regions.get(i-1));
3811        }
3812        regions.get(0).xor(this);
3813        return regions;
3814    }
3815
3816    public GreasedRegion retract8way() {
3817        GreasedRegion result = this;
3818        if (width <= 2 || ySections <= 0) {
3819        } else {
3820            final long[] next = new long[width * ySections];
3821            System.arraycopy(data, ySections, next, ySections, (width - 2) * ySections);
3822            for (int a = 0; a < ySections; a++) {
3823                if (a > 0 && a < ySections - 1) {
3824                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3825                        next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63))
3826                                & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63))
3827                                & data[i - ySections]
3828                                & data[i + ySections]
3829                                & ((data[i - ySections] << 1) | ((data[i - 1 - ySections] & 0x8000000000000000L) >>> 63))
3830                                & ((data[i + ySections] << 1) | ((data[i - 1 + ySections] & 0x8000000000000000L) >>> 63))
3831                                & ((data[i - ySections] >>> 1) | ((data[i + 1 - ySections] & 1L) << 63))
3832                                & ((data[i + ySections] >>> 1) | ((data[i + 1 + ySections] & 1L) << 63));
3833                    }
3834                } else if (a > 0) {
3835                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3836                        next[i] &= ((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63))
3837                                & (data[i] >>> 1)
3838                                & data[i - ySections]
3839                                & data[i + ySections]
3840                                & ((data[i - ySections] << 1) | ((data[i - 1 - ySections] & 0x8000000000000000L) >>> 63))
3841                                & ((data[i + ySections] << 1) | ((data[i - 1 + ySections] & 0x8000000000000000L) >>> 63))
3842                                & (data[i - ySections] >>> 1)
3843                                & (data[i + ySections] >>> 1);
3844                    }
3845                } else if (a < ySections - 1) {
3846                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3847                        next[i] &= (data[i] << 1)
3848                                & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63))
3849                                & data[i - ySections]
3850                                & data[i + ySections]
3851                                & (data[i - ySections] << 1)
3852                                & (data[i + ySections] << 1)
3853                                & ((data[i - ySections] >>> 1) | ((data[i + 1 - ySections] & 1L) << 63))
3854                                & ((data[i + ySections] >>> 1) | ((data[i + 1 + ySections] & 1L) << 63));
3855                    }
3856                } else // only the case when ySections == 1
3857                {
3858                    for (int i = ySections + a; i < (width - 1) * ySections; i += ySections) {
3859                        next[i] &= (data[i] << 1)
3860                                & (data[i] >>> 1)
3861                                & data[i - ySections]
3862                                & data[i + ySections]
3863                                & (data[i - ySections] << 1)
3864                                & (data[i + ySections] << 1)
3865                                & (data[i - ySections] >>> 1)
3866                                & (data[i + ySections] >>> 1);
3867                    }
3868                }
3869            }
3870            if (yEndMask != -1) {
3871                for (int a = ySections - 1; a < next.length; a += ySections) {
3872                    next[a] &= yEndMask;
3873                }
3874            }
3875            data = next;
3876            tallied = false;
3877        }
3878
3879        return result;
3880    }
3881
3882    public GreasedRegion retract8way(int amount)
3883    {
3884        for (int i = 0; i < amount; i++) {
3885            retract8way();
3886        }
3887        return this;
3888    }
3889
3890    public GreasedRegion[] retractSeries8way(int amount) {
3891        GreasedRegion[] result;
3892        if (amount <= 0) {
3893            result = new GreasedRegion[0];
3894        } else {
3895            GreasedRegion[] regions = new GreasedRegion[amount];
3896            GreasedRegion temp = new GreasedRegion(this);
3897            for (int i = 0; i < amount; i++) {
3898                regions[i] = new GreasedRegion(temp.retract8way());
3899            }
3900            result = regions;
3901        }
3902        return result;
3903    }
3904
3905    public ArrayList<GreasedRegion> retractSeriesToLimit8way()
3906    {
3907        ArrayList<GreasedRegion> regions = new ArrayList<>();
3908        GreasedRegion temp = new GreasedRegion(this);
3909        while (!temp.retract8way().isEmpty()) {
3910            regions.add(new GreasedRegion(temp));
3911        }
3912        return regions;
3913    }
3914
3915    public GreasedRegion surface8way()
3916    {
3917        GreasedRegion cpy = new GreasedRegion(this).retract8way();
3918        return xor(cpy);
3919    }
3920
3921    public GreasedRegion surface8way(int amount)
3922    {
3923        GreasedRegion cpy = new GreasedRegion(this).retract8way(amount);
3924        return xor(cpy);
3925    }
3926
3927    public GreasedRegion[] surfaceSeries8way(int amount) {
3928        GreasedRegion[] result;
3929        if (amount <= 0) {
3930            result = new GreasedRegion[0];
3931        } else {
3932            GreasedRegion[] regions = new GreasedRegion[amount];
3933            GreasedRegion temp = new GreasedRegion(this);
3934            regions[0] = new GreasedRegion(temp);
3935            for (int i = 1; i < amount; i++) {
3936                regions[i] = new GreasedRegion(temp.retract8way());
3937            }
3938            for (int i = 0; i < amount - 1; i++) {
3939                regions[i].xor(regions[i + 1]);
3940            }
3941            regions[amount - 1].surface8way();
3942            result = regions;
3943        }
3944        return result;
3945    }
3946    public ArrayList<GreasedRegion> surfaceSeriesToLimit8way() {
3947        ArrayList<GreasedRegion> result;
3948        ArrayList<GreasedRegion> regions = retractSeriesToLimit8way();
3949        if (regions.isEmpty()) {
3950            result = regions;
3951        } else {
3952            regions.add(0, regions.get(0).copy().xor(this));
3953            for (int i = 1; i < regions.size() - 1; i++) {
3954                regions.get(i).xor(regions.get(i + 1));
3955            }
3956            result = regions;
3957        }
3958        return result;
3959    }
3960
3961    /**
3962     * Like {@link #expand()}, but limits expansion to the "on" cells of {@code bounds}. Expands in all 4-way directions
3963     * by one cell simultaneously, and only successfully affects the cells that are adjacent to this and are in bounds.
3964     * @param bounds the set of "on" cells that limits where this can expand into
3965     * @return this, after expanding, for chaining
3966     */
3967    public GreasedRegion flood(GreasedRegion bounds) {
3968        GreasedRegion result = this;
3969        if (width < 2 || ySections <= 0 || bounds == null || bounds.width < 2 || bounds.ySections <= 0) {
3970        } else {
3971            final long[] next = new long[width * ySections];
3972            for (int a = 0; a < ySections && a < bounds.ySections; a++) {
3973                next[a] |= (data[a] | (data[a] << 1) | (data[a] >>> 1) | data[a + ySections]) & bounds.data[a];
3974                next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a] | (data[(width - 1) * ySections + a] << 1)
3975                        | (data[(width - 1) * ySections + a] >>> 1) | data[(width - 2) * ySections + a]) & bounds.data[(width - 1) * bounds.ySections + a];
3976
3977                for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections &&
3978                        j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) {
3979                    next[i] |= (data[i] | (data[i] << 1) | (data[i] >>> 1) | data[i - ySections] | data[i + ySections]) & bounds.data[j];
3980                }
3981
3982                if (a > 0) {
3983                    for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections;
3984                         i += ySections, j += bounds.ySections) {
3985                        next[i] |= (data[i] | ((data[i - 1] & 0x8000000000000000L) >>> 63)) & bounds.data[j];
3986                    }
3987                }
3988
3989                if (a < ySections - 1 && a < bounds.ySections - 1) {
3990                    for (int i = ySections + a, j = bounds.ySections + a;
3991                         i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) {
3992                        next[i] |= (data[i] | ((data[i + 1] & 1L) << 63)) & bounds.data[j];
3993                    }
3994                }
3995            }
3996            if (yEndMask != -1 && bounds.yEndMask != -1) {
3997                if (ySections == bounds.ySections) {
3998                    long mask = ((yEndMask >>> 1) <= (bounds.yEndMask >>> 1))
3999                            ? yEndMask : bounds.yEndMask;
4000                    for (int a = ySections - 1; a < next.length; a += ySections) {
4001                        next[a] &= mask;
4002                    }
4003                } else if (ySections < bounds.ySections) {
4004                    for (int a = ySections - 1; a < next.length; a += ySections) {
4005                        next[a] &= yEndMask;
4006                    }
4007                } else {
4008                    for (int a = bounds.ySections - 1; a < next.length; a += ySections) {
4009                        next[a] &= bounds.yEndMask;
4010                    }
4011                }
4012            }
4013            data = next;
4014            tallied = false;
4015        }
4016
4017        return result;
4018    }
4019
4020    /**
4021     * Like {@link #expand(int)}, but limits expansion to the "on" cells of {@code bounds}. Repeatedly expands in the
4022     * 4-way directions by one cell simultaneously, and only successfully affects the cells that are adjacent to the
4023     * previous expansion and are in bounds. This won't skip over gaps in bounds, even if amount is high enough that a
4024     * call to {@link #expand(int)} would reach past the gap; it will stop at the gap and only pass it if expansion
4025     * takes it around.
4026     * @param bounds the set of "on" cells that limits where this can expand into
4027     * @param amount how far to expand this outward by, in cells
4028     * @return this, after expanding, for chaining
4029     */
4030    public GreasedRegion flood(GreasedRegion bounds, int amount)
4031    {
4032        int ct = size(), ct2;
4033        for (int i = 0; i < amount; i++) {
4034            flood(bounds);
4035            if(ct == (ct2 = size()))
4036                break;
4037            else
4038                ct = ct2;
4039
4040        }
4041        return this;
4042    }
4043
4044    /**
4045     * Repeatedly calls {@link #flood(GreasedRegion)} {@code amount} times and returns the intermediate steps in a
4046     * GreasedRegion array of size {@code amount}. Doesn't modify this GreasedRegion, and doesn't return it in the array
4047     * (it may return a copy of it if and only if no flood8way() calls can expand the area). If this fills
4048     * {@code bounds} as fully as possible and still has steps left, the remaining steps are all copies of the
4049     * fully-filled area.
4050     * @param bounds the set of "on" cells that this will attempt to fill in steps
4051     * @param amount how many steps to flood outward, and the size of the array to return
4052     * @return an array of GreasedRegion, {@code amount} in size, containing larger and larger expansions of this
4053     */
4054    public GreasedRegion[] floodSeries(GreasedRegion bounds, int amount) {
4055        GreasedRegion[] result;
4056        if (amount <= 0) {
4057            result = new GreasedRegion[0];
4058        } else {
4059            int ct = size(), ct2;
4060            GreasedRegion[] regions = new GreasedRegion[amount];
4061            boolean done = false;
4062            GreasedRegion temp = new GreasedRegion(this);
4063            for (int i = 0; i < amount; i++) {
4064                if (done) {
4065                    regions[i] = new GreasedRegion(temp);
4066                } else {
4067                    regions[i] = new GreasedRegion(temp.flood(bounds));
4068                    if (ct == (ct2 = temp.size()))
4069                        done = true;
4070                    else
4071                        ct = ct2;
4072                }
4073            }
4074            result = regions;
4075        }
4076        return result;
4077    }
4078
4079    /**
4080     * Repeatedly generates new GreasedRegions, each one cell expanded in 4 directions from the previous GreasedRegion
4081     * and staying inside the "on" cells of {@code bounds}, until it can't expand any more. Returns an ArrayList of the
4082     * GreasedRegion steps this generated; this list does not include this GreasedRegion (or any unmodified copy of this
4083     * GreasedRegion), and this method does not modify it.
4084     * @param bounds the set of "on" cells that this will attempt to fill in steps
4085     * @return an ArrayList of steps from one {@link #flood(GreasedRegion)} call to possibly many chained after it
4086     */
4087    public ArrayList<GreasedRegion> floodSeriesToLimit(GreasedRegion bounds) {
4088        int ct = size(), ct2;
4089        ArrayList<GreasedRegion> regions = new ArrayList<>();
4090        GreasedRegion temp = new GreasedRegion(this);
4091        while (true) {
4092            temp.flood(bounds);
4093            if (ct == (ct2 = temp.size()))
4094                return regions;
4095            else {
4096                ct = ct2;
4097                regions.add(new GreasedRegion(temp));
4098            }
4099        }
4100    }
4101
4102    /**
4103     * Like {@link #expand8way()}, but limits expansion to the "on" cells of {@code bounds}. Expands in all directions
4104     * by one cell simultaneously, and only successfully affects the cells that are adjacent to this and are in bounds.
4105     * @param bounds the set of "on" cells that limits where this can expand into
4106     * @return this, after expanding, for chaining
4107     */
4108    public GreasedRegion flood8way(GreasedRegion bounds) {
4109        GreasedRegion result = this;
4110        if (width < 2 || ySections <= 0 || bounds == null || bounds.width < 2 || bounds.ySections <= 0) {
4111        } else {
4112            final long[] next = new long[width * ySections];
4113            for (int a = 0; a < ySections && a < bounds.ySections; a++) {
4114                next[a] |= (data[a] | (data[a] << 1) | (data[a] >>> 1)
4115                        | data[a + ySections] | (data[a + ySections] << 1) | (data[a + ySections] >>> 1)) & bounds.data[a];
4116                next[(width - 1) * ySections + a] |= (data[(width - 1) * ySections + a]
4117                        | (data[(width - 1) * ySections + a] << 1) | (data[(width - 1) * ySections + a] >>> 1)
4118                        | data[(width - 2) * ySections + a] | (data[(width - 2) * ySections + a] << 1) | (data[(width - 2) * ySections + a] >>> 1))
4119                        & bounds.data[(width - 1) * bounds.ySections + a];
4120
4121                for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections &&
4122                        j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) {
4123                    next[i] |= (data[i] | (data[i] << 1) | (data[i] >>> 1)
4124                            | data[i - ySections] | (data[i - ySections] << 1) | (data[i - ySections] >>> 1)
4125                            | data[i + ySections] | (data[i + ySections] << 1) | (data[i + ySections] >>> 1))
4126                            & bounds.data[j];
4127                }
4128
4129                if (a > 0) {
4130                    for (int i = ySections + a, j = bounds.ySections + a; i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections;
4131                         i += ySections, j += bounds.ySections) {
4132                        next[i] |= (data[i] | ((data[i - 1] & 0x8000000000000000L) >>> 63) |
4133                                ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63) |
4134                                ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63)) & bounds.data[j];
4135                    }
4136                }
4137
4138                if (a < ySections - 1 && a < bounds.ySections - 1) {
4139                    for (int i = ySections + a, j = bounds.ySections + a;
4140                         i < (width - 1) * ySections && j < (bounds.width - 1) * bounds.ySections; i += ySections, j += bounds.ySections) {
4141                        next[i] |= (data[i] | ((data[i + 1] & 1L) << 63) |
4142                                ((data[i - ySections + 1] & 1L) << 63) |
4143                                ((data[i + ySections + 1] & 1L) << 63)) & bounds.data[j];
4144                    }
4145                }
4146            }
4147            if (yEndMask != -1 && bounds.yEndMask != -1) {
4148                if (ySections == bounds.ySections) {
4149                    long mask = ((yEndMask >>> 1) <= (bounds.yEndMask >>> 1))
4150                            ? yEndMask : bounds.yEndMask;
4151                    for (int a = ySections - 1; a < next.length; a += ySections) {
4152                        next[a] &= mask;
4153                    }
4154                } else if (ySections < bounds.ySections) {
4155                    for (int a = ySections - 1; a < next.length; a += ySections) {
4156                        next[a] &= yEndMask;
4157                    }
4158                } else {
4159                    for (int a = bounds.ySections - 1; a < next.length; a += ySections) {
4160                        next[a] &= bounds.yEndMask;
4161                    }
4162                }
4163            }
4164            data = next;
4165            tallied = false;
4166        }
4167
4168        return result;
4169    }
4170    /**
4171     * Like {@link #expand8way(int)}, but limits expansion to the "on" cells of {@code bounds}. Repeatedly expands in
4172     * all directions by one cell simultaneously, and only successfully affects the cells that are adjacent to the
4173     * previous expansion and are in bounds. This won't skip over gaps in bounds, even if amount is high enough that a
4174     * call to {@link #expand8way(int)} would reach past the gap; it will stop at the gap and only pass it if expansion
4175     * takes it around.
4176     * @param bounds the set of "on" cells that limits where this can expand into
4177     * @param amount how far to expand this outward by, in cells
4178     * @return this, after expanding, for chaining
4179     */
4180    public GreasedRegion flood8way(GreasedRegion bounds, int amount)
4181    {
4182        int ct = size(), ct2;
4183        for (int i = 0; i < amount; i++) {
4184            flood8way(bounds);
4185            if(ct == (ct2 = size()))
4186                break;
4187            else
4188                ct = ct2;
4189        }
4190        return this;
4191    }
4192
4193    /**
4194     * Repeatedly calls {@link #flood8way(GreasedRegion)} {@code amount} times and returns the intermediate steps in a
4195     * GreasedRegion array of size {@code amount}. Doesn't modify this GreasedRegion, and doesn't return it in the array
4196     * (it may return a copy of it if and only if no flood8way() calls can expand the area). If this fills
4197     * {@code bounds} as fully as possible and still has steps left, the remaining steps are all copies of the
4198     * fully-filled area.
4199     * @param bounds the set of "on" cells that this will attempt to fill in steps
4200     * @param amount how many steps to flood outward, and the size of the array to return
4201     * @return an array of GreasedRegion, {@code amount} in size, containing larger and larger expansions of this
4202     */
4203    public GreasedRegion[] floodSeries8way(GreasedRegion bounds, int amount) {
4204        GreasedRegion[] result;
4205        if (amount <= 0) {
4206            result = new GreasedRegion[0];
4207        } else {
4208            int ct = size(), ct2;
4209            GreasedRegion[] regions = new GreasedRegion[amount];
4210            boolean done = false;
4211            GreasedRegion temp = new GreasedRegion(this);
4212            for (int i = 0; i < amount; i++) {
4213                if (done) {
4214                    regions[i] = new GreasedRegion(temp);
4215                } else {
4216                    regions[i] = new GreasedRegion(temp.flood8way(bounds));
4217                    if (ct == (ct2 = temp.size()))
4218                        done = true;
4219                    else
4220                        ct = ct2;
4221                }
4222            }
4223            result = regions;
4224        }
4225        return result;
4226    }
4227
4228    /**
4229     * Repeatedly generates new GreasedRegions, each one cell expanded in 8 directions from the previous GreasedRegion
4230     * and staying inside the "on" cells of {@code bounds}, until it can't expand any more. Returns an ArrayList of the
4231     * GreasedRegion steps this generated; this list does not include this GreasedRegion (or any unmodified copy of this
4232     * GreasedRegion), and this method does not modify it.
4233     * @param bounds the set of "on" cells that this will attempt to fill in steps
4234     * @return an ArrayList of steps from one {@link #flood8way(GreasedRegion)} call to possibly many chained after it
4235     */
4236    public ArrayList<GreasedRegion> floodSeriesToLimit8way(GreasedRegion bounds) {
4237        int ct = size(), ct2;
4238        ArrayList<GreasedRegion> regions = new ArrayList<>();
4239        GreasedRegion temp = new GreasedRegion(this);
4240        while (true) {
4241            temp.flood8way(bounds);
4242            if (ct == (ct2 = temp.size()))
4243                return regions;
4244            else {
4245                ct = ct2;
4246                regions.add(new GreasedRegion(temp));
4247            }
4248        }
4249    }
4250
4251    /**
4252     * A randomized flood-fill that modifies this GreasedRegion so it randomly adds adjacent cells while staying inside
4253     * the "on" cells of {@code bounds}, until {@link #size()} is equal to {@code volume} or there are no more cells
4254     * this can expand into. This GreasedRegion acts as the initial state, and often contains just one cell before this
4255     * is called. This method is useful for imitating the movement of fluids like water or smoke within some boundaries.
4256     * @param bounds this GreasedRegion will only expand to cells that are "on" in bounds; bounds should overlap with this
4257     * @param volume the maximum {@link #size()} this GreasedRegion can reach before this stops expanding
4258     * @param rng a random number generator, like {@link RNG} or {@link GWTRNG}
4259     * @return this, after expanding randomly, for chaining
4260     */
4261    public GreasedRegion spill(GreasedRegion bounds, int volume, IRNG rng) {
4262        GreasedRegion result = this;
4263        if (width < 2 || ySections <= 0 || bounds == null || bounds.width < 2 || bounds.ySections <= 0) {
4264        } else {
4265            int current = size();
4266            if (current >= volume) {
4267            } else {
4268                GreasedRegion t = new GreasedRegion(this).notAnd(bounds);
4269                long[] b2 = new long[t.data.length];
4270                System.arraycopy(t.data, 0, b2, 0, b2.length);
4271                t.remake(this).fringe().and(bounds).tally();
4272                if (t.ct <= 0) {
4273                } else {
4274                    Coord c;
4275                    int x, y, p;
4276                    for (int i = current; i < volume; i++) {
4277                        c = t.singleRandom(rng);
4278                        x = c.x;
4279                        y = c.y;
4280                        if (data[p = x * ySections + (y >> 6)] != (data[p] |= 1L << (y & 63))) {
4281                            counts[p]++;
4282                            for (int j = p + 1; j < data.length; j++) {
4283                                if (counts[j] > 0) ++counts[j];
4284                            }
4285                            ct++;
4286                            t.data[p] &= ~(1L << (y & 63));
4287                            if (x < width - 1 && (b2[p = (x + 1) * ySections + (y >> 6)] & 1L << (y & 63)) != 0) {
4288                                t.data[p] |= 1L << (y & 63);
4289                            }
4290                            if (y < height - 1 && (b2[p = x * ySections + (y + 1 >> 6)] & 1L << (y + 1 & 63)) != 0) {
4291                                t.data[p] |= 1L << (y + 1 & 63);
4292                            }
4293                            if (y > 0 && (b2[p = x * ySections + (y - 1 >> 6)] & 1L << (y - 1 & 63)) != 0) {
4294                                t.data[p] |= 1L << (y - 1 & 63);
4295                            }
4296                            if (x > 0 && (b2[p = (x - 1) * ySections + (y >> 6)] & 1L << (y & 63)) != 0) {
4297
4298                                t.data[p] |= 1L << (y & 63);
4299                            }
4300                            t.tally();
4301                            if (t.ct <= 0) break;
4302                        }
4303                    }
4304                    tallied = false;
4305                }
4306            }
4307        }
4308        return result;
4309    }
4310
4311    /**
4312     * Where a cell is "on" but forms a right-angle with exactly two orthogonally-adjacent "on" cells and exactly two
4313     * orthogonally-adjacent "off" cells, this turns each of those cells "off." This won't affect east-west lines of
4314     * flat "on" cells, nor north-south lines.
4315     * @return this, after removing right-angle corner "on" cells, for chaining
4316     */
4317    public GreasedRegion removeCorners()
4318    {
4319        if(width <= 2 || ySections <= 0)
4320            return this;
4321
4322        final long[] next = new long[width * ySections];
4323        System.arraycopy(data, 0, next, 0, width * ySections);
4324        for (int a = 0; a < ySections; a++) {
4325            if(a > 0 && a < ySections - 1) {
4326                next[a] &= (((data[a] << 1) | ((data[a - 1] & 0x8000000000000000L) >>> 63))
4327                        & ((data[a] >>> 1) | ((data[a + 1] & 1L) << 63)));
4328                next[(width - 1) * ySections + a] &= (((data[(width - 1) * ySections + a] << 1)
4329                        | ((data[(width - 1) * ySections + a - 1] & 0x8000000000000000L) >>> 63))
4330                        & ((data[(width - 1) * ySections + a] >>> 1)
4331                        | ((data[(width - 1) * ySections + a + 1] & 1L) << 63)));
4332                for (int i = ySections+a; i < (width - 1) * ySections; i+= ySections) {
4333                    next[i] &= (((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63))
4334                            & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63)))
4335                            | (data[i - ySections]
4336                            & data[i + ySections]);
4337                }
4338            }
4339            else if(a > 0) {
4340                next[a] &= (((data[a] << 1) | ((data[a - 1] & 0x8000000000000000L) >>> 63))
4341                        & (data[a] >>> 1));
4342                next[(width - 1) * ySections + a] &= (((data[(width - 1) * ySections + a] << 1)
4343                        | ((data[(width - 1) * ySections + a - 1] & 0x8000000000000000L) >>> 63))
4344                        & (data[(width - 1) * ySections + a] >>> 1));
4345                for (int i = ySections+a; i < (width - 1) * ySections; i+= ySections) {
4346                    next[i] &= (((data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63))
4347                            & (data[i] >>> 1))
4348                            | (data[i - ySections]
4349                            & data[i + ySections]);
4350                }
4351            }
4352            else if(a < ySections - 1) {
4353                next[a] &= ((data[a] << 1)
4354                        & ((data[a] >>> 1)
4355                        | ((data[a + 1] & 1L) << 63)));
4356                next[(width - 1) * ySections + a] &= ((data[(width - 1) * ySections + a] << 1)
4357                        & ((data[(width - 1) * ySections + a] >>> 1)
4358                        | ((data[(width - 1) * ySections + a + 1] & 1L) << 63)));
4359                for (int i = ySections+a; i < (width - 1) * ySections; i+= ySections) {
4360                    next[i] &= ((data[i] << 1)
4361                            & ((data[i] >>> 1) | ((data[i + 1] & 1L) << 63)))
4362                            | (data[i - ySections]
4363                            & data[i + ySections]);
4364                }
4365            }
4366            else // only the case when ySections == 1
4367            {
4368                next[0] &= (data[0] << 1) & (data[0] >>> 1);
4369                next[width-1] &= (data[width-1] << 1) & (data[width-1] >>> 1);
4370                for (int i = 1+a; i < (width - 1); i++) {
4371                    next[i] &= ((data[i] << 1) & (data[i] >>> 1)) | (data[i - ySections] & data[i + ySections]);
4372                }
4373            }
4374        }
4375
4376        if(yEndMask != -1) {
4377            for (int a = ySections - 1; a < next.length; a += ySections) {
4378                next[a] &= yEndMask;
4379            }
4380        }
4381        data = next;
4382        tallied = false;
4383        return this;
4384    }
4385
4386    /**
4387     * If this GreasedRegion stores multiple unconnected "on" areas, this finds each isolated area (areas that
4388     * are only adjacent diagonally are considered separate from each other) and returns it as an element in an
4389     * ArrayList of GreasedRegion, with one GreasedRegion per isolated area. Not to be confused with
4390     * {@link #split8way()}, which considers diagonally-adjacent cells as part of one region, while this method requires
4391     * cells to be orthogonally adjacent.
4392     * <br>
4393     * Useful when you have, for example, all the rooms in a dungeon with their connecting corridors removed, but want
4394     * to separate the rooms. You can get the aforementioned data assuming a bare dungeon called map using:
4395     * <br>
4396     * {@code GreasedRegion floors = new GreasedRegion(map, '.'),
4397     * rooms = floors.copy().retract8way().flood(floors, 2),
4398     * corridors = floors.copy().andNot(rooms),
4399     * doors = rooms.copy().and(corridors.copy().fringe());}
4400     * <br>
4401     * You can then get all rooms as separate regions with {@code List<GreasedRegion> apart = split(rooms);}, or
4402     * substitute {@code split(corridors)} to get the corridors. The room-finding technique works by shrinking floors
4403     * by a radius of 1 (8-way), which causes thin areas like corridors of 2 or less width to be removed, then
4404     * flood-filling the floors out from the area that produces by 2 cells (4-way this time) to restore the original
4405     * size of non-corridor areas (plus some extra to ensure odd shapes are kept). Corridors are obtained by removing
4406     * the rooms from floors. The example code also gets the doors (which overlap with rooms, not corridors) by finding
4407     * where the a room and a corridor are adjacent. This technique is used with some enhancements in the RoomFinder
4408     * class.
4409     * @see squidpony.squidgrid.mapping.RoomFinder for a class that uses this technique without exposing GreasedRegion
4410     * @return an ArrayList containing each unconnected area from packed as a GreasedRegion element
4411     */
4412    public ArrayList<GreasedRegion> split()
4413    {
4414        ArrayList<GreasedRegion> scattered = new ArrayList<>(32);
4415        int fst = firstTight();
4416        GreasedRegion remaining = new GreasedRegion(this);
4417        while (fst >= 0) {
4418            GreasedRegion filled = new GreasedRegion(width, height).insert(fst).flood(remaining, width * height);
4419            scattered.add(filled);
4420            remaining.andNot(filled);
4421            fst = remaining.firstTight();
4422        }
4423        return scattered;
4424    }
4425    /**
4426     * If this GreasedRegion stores multiple unconnected "on" areas, this finds each isolated area (areas that
4427     * are only adjacent diagonally are considered <b>one area</b> with this) and returns it as an element in an
4428     * ArrayList of GreasedRegion, with one GreasedRegion per isolated area. This should not be confused with
4429     * {@link #split()}, which is almost identical except that split() considers only orthogonal connections, while this
4430     * method considers both orthogonal and diagonal connections between cells as joining an area.
4431     * <br>
4432     * Useful when you have, for example, all the rooms in a dungeon with their connecting corridors removed, but want
4433     * to separate the rooms. You can get the aforementioned data assuming a bare dungeon called map using:
4434     * <br>
4435     * {@code GreasedRegion floors = new GreasedRegion(map, '.'),
4436     * rooms = floors.copy().retract8way().flood(floors, 2),
4437     * corridors = floors.copy().andNot(rooms),
4438     * doors = rooms.copy().and(corridors.copy().fringe());}
4439     * <br>
4440     * You can then get all rooms as separate regions with {@code List<GreasedRegion> apart = split(rooms);}, or
4441     * substitute {@code split(corridors)} to get the corridors. The room-finding technique works by shrinking floors
4442     * by a radius of 1 (8-way), which causes thin areas like corridors of 2 or less width to be removed, then
4443     * flood-filling the floors out from the area that produces by 2 cells (4-way this time) to restore the original
4444     * size of non-corridor areas (plus some extra to ensure odd shapes are kept). Corridors are obtained by removing
4445     * the rooms from floors. The example code also gets the doors (which overlap with rooms, not corridors) by finding
4446     * where the a room and a corridor are adjacent. This technique is used with some enhancements in the RoomFinder
4447     * class.
4448     * @see squidpony.squidgrid.mapping.RoomFinder for a class that uses this technique without exposing GreasedRegion
4449     * @return an ArrayList containing each unconnected area from packed as a GreasedRegion element
4450     */
4451    public ArrayList<GreasedRegion> split8way()
4452    {
4453        ArrayList<GreasedRegion> scattered = new ArrayList<>(32);
4454        int fst = firstTight();
4455        GreasedRegion remaining = new GreasedRegion(this);
4456        while (fst >= 0) {
4457            GreasedRegion filled = new GreasedRegion(width, height).insert(fst).flood8way(remaining, width * height);
4458            scattered.add(filled);
4459            remaining.andNot(filled);
4460            fst = remaining.firstTight();
4461        }
4462        return scattered;
4463    }
4464
4465    /**
4466     * Finds the largest contiguous area of "on" cells in this GreasedRegion and returns it; does not modify this
4467     * GreasedRegion. If there are multiple areas that are all equally large with no larger area, this returns the
4468     * region it checks first and still is largest (first determined by the same ordering {@link #nth(int)} takes).
4469     * This may return an empty GreasedRegion if there are no "on" cells, but it will never return null.
4470     * Here, contiguous means adjacent on an orthogonal direction, and this doesn't consider diagonally-connected cells
4471     * as contiguous unless they also have an orthogonal connection.
4472     * @return a new GreasedRegion that corresponds to the largest contiguous sub-region of "on" cells in this.
4473     */
4474    public GreasedRegion largestPart()
4475    {
4476        int fst = firstTight(), bestSize = 0, currentSize;
4477        GreasedRegion remaining = new GreasedRegion(this), filled = new GreasedRegion(width, height),
4478                choice = new GreasedRegion(width, height);
4479        while (fst >= 0) {
4480            filled.empty().insert(fst).flood(remaining, width * height);
4481            if((currentSize = filled.size()) > bestSize)
4482            {
4483                bestSize = currentSize;
4484                choice.remake(filled);
4485            }
4486            remaining.andNot(filled);
4487            fst = remaining.firstTight();
4488        }
4489        return choice;
4490    }
4491
4492    /**
4493     * Finds the largest contiguous area of "on" cells in this GreasedRegion and returns it; does not modify this
4494     * GreasedRegion. If there are multiple areas that are all equally large with no larger area, this returns the
4495     * region it checks first and still is largest (first determined by the same ordering {@link #nth(int)} takes).
4496     * This may return an empty GreasedRegion if there are no "on" cells, but it will never return null.
4497     * Here, contiguous means adjacent on any 8-way direction, and considers cells as part of a contiguous area even if
4498     * all connections but one, which can be orthogonal or diagonal, are blocked by "off" cells.
4499     * @return a new GreasedRegion that corresponds to the largest contiguous sub-region of "on" cells in this.
4500     */
4501    public GreasedRegion largestPart8way()
4502    {
4503        int fst = firstTight(), bestSize = 0, currentSize;
4504        GreasedRegion remaining = new GreasedRegion(this), filled = new GreasedRegion(width, height),
4505                choice = new GreasedRegion(width, height);
4506        while (fst >= 0) {
4507            filled.empty().insert(fst).flood8way(remaining, width * height);
4508            if((currentSize = filled.size()) > bestSize)
4509            {
4510                bestSize = currentSize;
4511                choice.remake(filled);
4512            }
4513            remaining.andNot(filled);
4514            fst = remaining.firstTight();
4515        }
4516        return choice;
4517    }
4518
4519    /**
4520     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor upwards when this is called.
4521     * Up is defined as negative y. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor
4522     * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction.
4523     * @return this, after modifications, for chaining
4524     */
4525    public GreasedRegion neighborUp() {
4526        GreasedRegion result = this;
4527        if (width < 2 || ySections <= 0) {
4528        } else {
4529            for (int a = ySections - 1; a >= 0; a--) {
4530                if (a > 0) {
4531                    for (int i = a; i < width * ySections; i += ySections) {
4532                        data[i] = (data[i] << 1) | ((data[i - 1] & 0x8000000000000000L) >>> 63);
4533                    }
4534                } else {
4535                    for (int i = a; i < width * ySections; i += ySections) {
4536                        data[i] = (data[i] << 1);
4537                    }
4538                }
4539            }
4540            tallied = false;
4541        }
4542        return result;
4543    }
4544
4545    /**
4546     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor downwards when this is called.
4547     * Down is defined as positive y. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor
4548     * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction.
4549     * @return this, after modifications, for chaining
4550     */
4551    public GreasedRegion neighborDown() {
4552        GreasedRegion result = this;
4553        if (width < 2 || ySections <= 0) {
4554        } else {
4555            for (int a = 0; a < ySections; a++) {
4556                if (a < ySections - 1) {
4557                    for (int i = a; i < width * ySections; i += ySections) {
4558                        data[i] = (data[i] >>> 1) | ((data[i + 1] & 1L) << 63);
4559                    }
4560                } else {
4561                    for (int i = a; i < width * ySections; i += ySections) {
4562                        data[i] = (data[i] >>> 1);
4563                    }
4564                }
4565            }
4566            tallied = false;
4567        }
4568        return result;
4569    }
4570
4571    /**
4572     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor to the left when this is called.
4573     * Left is defined as negative x. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor
4574     * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction.
4575     * @return this, after modifications, for chaining
4576     */
4577    public GreasedRegion neighborLeft() {
4578        GreasedRegion result = this;
4579        if (width < 2 || ySections <= 0) {
4580        } else {
4581            for (int a = 0; a < ySections; a++) {
4582                for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) {
4583                    data[i] = data[i - ySections];
4584                }
4585                data[a] = 0L;
4586            }
4587            tallied = false;
4588        }
4589        return result;
4590    }
4591
4592    /**
4593     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor to the right when this is called.
4594     * Right is defined as positive x. Neighbors are "on" cells exactly one cell away. A cell can have a neighbor
4595     * without itself being on; this is useful when finding the "shadow" cast away from "on" cells in one direction.
4596     * @return this, after modifications, for chaining
4597     */
4598    public GreasedRegion neighborRight() {
4599        GreasedRegion result = this;
4600        if (width < 2 || ySections <= 0) {
4601        } else {
4602            for (int a = 0; a < ySections; a++) {
4603                for (int i = a; i < (width - 1) * ySections; i += ySections) {
4604                    data[i] = data[i + ySections];
4605                }
4606                data[(width - 1) * ySections + a] = 0L;
4607            }
4608            tallied = false;
4609        }
4610        return result;
4611    }
4612
4613    /**
4614     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor upwards and to the left when this
4615     * is called. Up is defined as negative y, left as negative x. Neighbors are "on" cells exactly one cell away. A
4616     * cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from "on"
4617     * cells in one direction.
4618     * @return this, after modifications, for chaining
4619     */
4620    public GreasedRegion neighborUpLeft() {
4621        GreasedRegion result = this;
4622        if (width < 2 || ySections <= 0) {
4623        } else {
4624            for (int a = ySections - 1; a >= 0; a--) {
4625                if (a > 0) {
4626                    for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) {
4627                        data[i] = (data[i - ySections] << 1) | ((data[i - ySections - 1] & 0x8000000000000000L) >>> 63);
4628                    }
4629                    data[a] = 0L;
4630                } else {
4631                    for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) {
4632                        data[i] = (data[i - ySections] << 1);
4633                    }
4634                    data[a] = 0L;
4635                }
4636            }
4637        }
4638        return result;
4639    }
4640
4641    /**
4642     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor upwards and to the right when
4643     * this is called. Up is defined as negative y, right as positive x. Neighbors are "on" cells exactly one cell away.
4644     * A cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from
4645     * "on" cells in one direction.
4646     * @return this, after modifications, for chaining
4647     */
4648    public GreasedRegion neighborUpRight() {
4649        GreasedRegion result = this;
4650        if (width < 2 || ySections <= 0) {
4651        } else {
4652            for (int a = ySections - 1; a >= 0; a--) {
4653                if (a > 0) {
4654                    for (int i = a; i < (width - 1) * ySections; i += ySections) {
4655                        data[i] = (data[i + ySections] << 1) | ((data[i + ySections - 1] & 0x8000000000000000L) >>> 63);
4656                    }
4657                    data[(width - 1) * ySections + a] = 0L;
4658                } else {
4659                    for (int i = a; i < (width - 1) * ySections; i += ySections) {
4660                        data[i] = (data[i + ySections] << 1);
4661                    }
4662                    data[(width - 1) * ySections + a] = 0L;
4663                }
4664            }
4665            tallied = false;
4666        }
4667        return result;
4668    }
4669
4670    /**
4671     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor downwards and to the left when
4672     * this is called. Down is defined as positive y, left as negative x. Neighbors are "on" cells exactly one cell
4673     * away. A cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from
4674     * "on" cells in one direction.
4675     * @return this, after modifications, for chaining
4676     */
4677    public GreasedRegion neighborDownLeft() {
4678        GreasedRegion result = this;
4679        if (width < 2 || ySections <= 0) {
4680        } else {
4681            for (int a = 0; a < ySections; a++) {
4682                if (a < ySections - 1) {
4683                    for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) {
4684                        data[i] = (data[i - ySections] >>> 1) | ((data[i - ySections + 1] & 1L) << 63);
4685                    }
4686                    data[a] = 0L;
4687                } else {
4688                    for (int i = ySections * (width - 1) + a; i >= ySections; i -= ySections) {
4689                        data[i] = (data[i - ySections] >>> 1);
4690                    }
4691                    data[a] = 0L;
4692                }
4693            }
4694            tallied = false;
4695        }
4696        return result;
4697    }
4698
4699    /**
4700     * Modifies this GreasedRegion so the only cells that will be "on" have a neighbor downwards and to the right when
4701     * this is called. Down is defined as positive y, right as positive x. Neighbors are "on" cells exactly one cell
4702     * away. A cell can have a neighbor without itself being on; this is useful when finding the "shadow" cast away from
4703     * "on" cells in one direction.
4704     * @return this, after modifications, for chaining
4705     */
4706    public GreasedRegion neighborDownRight() {
4707        GreasedRegion result = this;
4708        if (width < 2 || ySections <= 0) {
4709        } else {
4710            for (int a = 0; a < ySections; a++) {
4711                if (a < ySections - 1) {
4712                    for (int i = a; i < (width - 1) * ySections; i += ySections) {
4713                        data[i] = (data[i + ySections] >>> 1) | ((data[i + ySections + 1] & 1L) << 63);
4714                    }
4715                    data[(width - 1) * ySections + a] = 0L;
4716                } else {
4717                    for (int i = a; i < (width - 1) * ySections; i += ySections) {
4718                        data[i] = (data[i + ySections] >>> 1);
4719                    }
4720                    data[(width - 1) * ySections + a] = 0L;
4721                }
4722            }
4723            tallied = false;
4724        }
4725        return result;
4726    }
4727
4728    public GreasedRegion removeIsolated()
4729    {
4730        int fst = firstTight();
4731        GreasedRegion remaining = new GreasedRegion(this), filled = new GreasedRegion(this);
4732        while (fst >= 0) {
4733            filled.empty().insert(fst).flood(remaining, 8);
4734            if(filled.size() <= 4)
4735                andNot(filled);
4736            remaining.andNot(filled);
4737            fst = remaining.firstTight();
4738        }
4739        return this;
4740    }
4741
4742    /**
4743     * Returns true if any cell is "on" in both this GreasedRegion and in other; returns false otherwise. For example,
4744     * if (1,1) is "on" in this and (1,1) is "on" in other, this would return true, regardless of other cells.
4745     * @param other another GreasedRegion; its size does not have to match this GreasedRegion's size
4746     * @return true if this shares any "on" cells with other
4747     */
4748    public boolean intersects(GreasedRegion other) {
4749        boolean result = false;
4750        if (other != null) {
4751            OUTER:
4752            for (int x = 0; x < width && x < other.width; x++) {
4753                for (int y = 0; y < ySections && y < other.ySections; y++) {
4754                    if ((data[x * ySections + y] & other.data[x * ySections + y]) != 0) {
4755                        result = true;
4756                        break OUTER;
4757                    }
4758                }
4759            }
4760        }
4761        return result;
4762    }
4763
4764    public static OrderedSet<GreasedRegion> whichContain(int x, int y, GreasedRegion ... packed)
4765    {
4766        OrderedSet<GreasedRegion> found = new OrderedSet<>(packed.length);
4767        GreasedRegion tmp;
4768        for (int i = 0; i < packed.length; i++) {
4769            if((tmp = packed[i]) != null && tmp.contains(x, y))
4770                found.add(tmp);
4771        }
4772        return found;
4773    }
4774
4775    public static OrderedSet<GreasedRegion> whichContain(int x, int y, Collection<GreasedRegion> packed)
4776    {
4777        OrderedSet<GreasedRegion> found = new OrderedSet<>(packed.size());
4778        for (GreasedRegion tmp : packed) {
4779            if(tmp != null && tmp.contains(x, y))
4780                found.add(tmp);
4781        }
4782        return found;
4783    }
4784    /**
4785     * Tries to look up the position x,y in each GreasedRegion in packed; each GreasedRegion that contains that x,y
4786     * point is appended into the Collection {@code into}.
4787     * @param into a Collection of GreasedRegion that will be modified if this succeeds
4788     * @param x the x-coordinate to look up
4789     * @param y the y-coordinate to look up
4790     * @param packed the array or varargs of GreasedRegion to try to look up the given position in
4791     * @return {@code into}, potentially modified
4792     */
4793    public static Collection<GreasedRegion> appendContaining(Collection<GreasedRegion> into, int x, int y, GreasedRegion ... packed)
4794    {
4795        GreasedRegion tmp;
4796        for (int i = 0; i < packed.length; i++) {
4797            if((tmp = packed[i]) != null && tmp.contains(x, y))
4798                into.add(tmp);
4799        }
4800        return into;
4801    }
4802
4803    /**
4804     * Tries to look up the position x,y in each GreasedRegion in packed; each GreasedRegion that contains that x,y
4805     * point is appended into the Collection {@code into}.
4806     * @param into a Collection of GreasedRegion that will be modified if this succeeds
4807     * @param x the x-coordinate to look up
4808     * @param y the y-coordinate to look up
4809     * @param packed the Collection of GreasedRegion to try to look up the given position in
4810     * @return {@code into}, potentially modified
4811     */
4812    public static Collection<GreasedRegion> appendContaining(Collection<GreasedRegion> into, int x, int y, Collection<GreasedRegion> packed)
4813    {
4814        for (GreasedRegion tmp : packed) {
4815            if(tmp != null && tmp.contains(x, y))
4816                into.add(tmp);
4817        }
4818        return into;
4819    }
4820
4821
4822    public int size()
4823    {
4824        if(!tallied)
4825            tally();
4826        return ct;
4827    }
4828
4829    public Coord fit(double xFraction, double yFraction) {
4830        Coord result = null;
4831        boolean finished = false;
4832        int tmp, xTotal = 0, yTotal = 0, xTarget, yTarget, bestX = -1;
4833        long t;
4834        int[] xCounts = new int[width];
4835        for (int x = 0; x < width; x++) {
4836            for (int s = 0; s < ySections; s++) {
4837                t = data[x * ySections + s] | 0L;
4838                if (t != 0) {
4839                    tmp = Long.bitCount(t);
4840                    xCounts[x] += tmp;
4841                    xTotal += tmp;
4842                }
4843            }
4844        }
4845        xTarget = (int) (xTotal * xFraction);
4846        for (int x = 0; x < width; x++) {
4847            if ((xTarget -= xCounts[x]) < 0) {
4848                bestX = x;
4849                yTotal = xCounts[x];
4850                break;
4851            }
4852        }
4853        if (bestX < 0) {
4854            result = Coord.get(-1, -1);
4855        } else {
4856            yTarget = (int) (yTotal * yFraction);
4857            for (int s = 0, y = 0; s < ySections; s++) {
4858                t = data[bestX * ySections + s] | 0L;
4859                for (long cy = 1L; cy != 0L && y < height; y++, cy <<= 1) {
4860                    if ((t & (cy | 0L)) != 0 && --yTarget < 0) {
4861                        result = Coord.get(bestX, y);
4862                        finished = true;
4863                        break;
4864                    }
4865                }
4866                if (finished) break;
4867            }
4868            if (!finished) {
4869                result = Coord.get(-1, -1);
4870            }
4871        }
4872
4873        return result;
4874    }
4875
4876    public int[][] fit(int[][] basis, int defaultValue) {
4877        int[][] result;
4878        int[][] next = ArrayTools.fill(defaultValue, width, height);
4879        if (basis == null || basis.length <= 0 || basis[0] == null || basis[0].length <= 0) {
4880            result = next;
4881        } else {
4882            int tmp, xTotal = 0, yTotal, xTarget, yTarget, bestX, oX = basis.length, oY = basis[0].length, ao;
4883            long t;
4884            int[] xCounts = new int[width];
4885            for (int x = 0; x < width; x++) {
4886                for (int s = 0; s < ySections; s++) {
4887                    t = data[x * ySections + s] | 0L;
4888                    if (t != 0) {
4889                        tmp = Long.bitCount(t);
4890                        xCounts[x] += tmp;
4891                        xTotal += tmp;
4892                    }
4893                }
4894            }
4895            if (xTotal <= 0) {
4896                result = next;
4897            } else {
4898                for (int aX = 0; aX < oX; aX++) {
4899                    CELL_WISE:
4900                    for (int aY = 0; aY < oY; aY++) {
4901                        if ((ao = basis[aX][aY]) == defaultValue)
4902                            continue;
4903                        xTarget = xTotal * aX / oX;
4904                        for (int x = 0; x < width; x++) {
4905                            if ((xTarget -= xCounts[x]) < 0) {
4906                                bestX = x;
4907                                yTotal = xCounts[x];
4908                                yTarget = yTotal * aY / oY;
4909                                for (int s = 0, y = 0; s < ySections; s++) {
4910                                    t = data[bestX * ySections + s] | 0L;
4911                                    for (long cy = 1L; cy != 0L && y < height; y++, cy <<= 1) {
4912                                        if ((t & (cy | 0L)) != 0 && --yTarget < 0) {
4913                                            next[bestX][y] = ao;
4914                                            continue CELL_WISE;
4915                                        }
4916                                    }
4917                                }
4918                                continue CELL_WISE;
4919                            }
4920                        }
4921
4922                    }
4923                }
4924                result = next;
4925            }
4926        }
4927
4928        return result;
4929    }
4930    
4931    /**
4932     * Don't use this in new code; prefer {@link #mixedRandomSeparated(double)}, {@link #quasiRandomSeparated(double)},
4933     * or {@link #separatedZCurve(double)}. This method has issues with being unable to fill the requested fraction, but
4934     * the others mentioned don't. See their documentation for what all these group of methods do.
4935     * @param fraction between 0.0 and 1.0
4936     * @return a Coord array that may not have the full fraction used; you have been advised
4937     */
4938    public Coord[] separatedPortion(double fraction) {
4939        Coord[] result;
4940        if (fraction < 0) {
4941            result = new Coord[0];
4942        } else {
4943            if (fraction > 1)
4944                fraction = 1;
4945            int ct, tmp, xTotal = 0, yTotal = 0, xTarget, yTarget, bestX = -1;
4946            long t;
4947            int[] xCounts = new int[width];
4948            for (int s = 0; s < ySections; s++) {
4949                for (int x = 0; x < width; x++) {
4950                    t = data[x * ySections + s] | 0L;
4951                    if (t != 0) {
4952                        tmp = Long.bitCount(t);
4953                        xCounts[x] += tmp;
4954                        xTotal += tmp;
4955                    }
4956                }
4957            }
4958            Coord[] vl = new Coord[ct = (int) (fraction * xTotal)];
4959            double[] vec = new double[2];
4960            sobol.skipTo(1337);
4961            EACH_SOBOL:
4962            for (int i = 0; i < ct; i++) {
4963                sobol.fillVector(vec);
4964                xTarget = (int) (xTotal * vec[0]);
4965                for (int x = 0; x < width; x++) {
4966                    if ((xTarget -= xCounts[x]) < 0) {
4967                        bestX = x;
4968                        yTotal = xCounts[x];
4969                        break;
4970                    }
4971                }
4972                yTarget = (int) (yTotal * vec[1]);
4973
4974                for (int s = 0, y = 0; s < ySections; s++) {
4975                    t = data[bestX * ySections + s] | 0L;
4976                    for (long cy = 1L; cy != 0L && y < height; y++, cy <<= 1) {
4977                        if ((t & (cy | 0L)) != 0 && --yTarget < 0) {
4978                            vl[i] = Coord.get(bestX, y);
4979                            continue EACH_SOBOL;
4980                        }
4981                    }
4982                }
4983            }
4984            result = vl;
4985        }
4986
4987        return result;
4988    }
4989    /**
4990     * Don't use this in new code; prefer {@link #mixedRandomSeparated(double, int, long)} with a random long as the
4991     * last parameter. This method has issues with being unable to fill the requested fraction, but mixedRandomSeparated
4992     * does not. See its documentation for what this method is supposed to do.
4993     * @param fraction between 0.0 and 1.0
4994     * @param rng an IRNG that will be used to get a random starting point in the Sobol sequence this uses internally
4995     * @return a Coord array that may not have the full fraction used; you have been advised
4996     */
4997    public Coord[] randomSeparated(double fraction, IRNG rng)
4998    {
4999        return randomSeparated(fraction, rng, -1);
5000    }
5001    /**
5002     * Don't use this in new code; prefer {@link #mixedRandomSeparated(double, int, long)} with a random long as the
5003     * last parameter. This method has issues with being unable to fill the requested fraction, but mixedRandomSeparated
5004     * does not. See its documentation for what this method is supposed to do.
5005     * @param fraction between 0.0 and 1.0
5006     * @param rng an IRNG that will be used to get a random starting point in the Sobol sequence this uses internally
5007     * @param limit how many Coord values this should return, at most; typically this will return less
5008     * @return a Coord array that may not have the full fraction used; you have been advised
5009     */
5010    public Coord[] randomSeparated(double fraction, IRNG rng, int limit) {
5011        Coord[] result;
5012        if (fraction < 0) {
5013            result = new Coord[0];
5014        } else {
5015            if (fraction > 1)
5016                fraction = 1;
5017            int ct, tmp, xTotal = 0, yTotal = 0, xTarget, yTarget, bestX = -1;
5018            long t;
5019            int[] xCounts = new int[width];
5020            for (int x = 0; x < width; x++) {
5021                for (int s = 0; s < ySections; s++) {
5022                    t = data[x * ySections + s] | 0L;
5023                    if (t != 0) {
5024                        tmp = Long.bitCount(t);
5025                        xCounts[x] += tmp;
5026                        xTotal += tmp;
5027                    }
5028                }
5029            }
5030            ct = (int) (fraction * xTotal);
5031            if (limit >= 0 && limit < ct)
5032                ct = limit;
5033            Coord[] vl = new Coord[ct];
5034            double[] vec = new double[2];
5035            sobol.skipTo(rng.between(1000, 65000));
5036            EACH_SOBOL:
5037            for (int i = 0; i < ct; i++) {
5038                sobol.fillVector(vec);
5039                xTarget = (int) (xTotal * vec[0]);
5040                for (int x = 0; x < width; x++) {
5041                    if ((xTarget -= xCounts[x]) < 0) {
5042                        bestX = x;
5043                        yTotal = xCounts[x];
5044                        break;
5045                    }
5046                }
5047                yTarget = (int) (yTotal * vec[1]);
5048
5049                for (int s = 0, y = 0; s < ySections; s++) {
5050                    t = data[bestX * ySections + s] | 0L;
5051                    for (long cy = 1; cy != 0 && y < height; y++, cy <<= 1) {
5052                        if ((t & (cy | 0L)) != 0 && --yTarget < 0) {
5053                            vl[i] = Coord.get(bestX, y);
5054                            continue EACH_SOBOL;
5055                        }
5056                    }
5057                }
5058            }
5059            result = vl;
5060        }
5061
5062        return result;
5063    }
5064
5065    /**
5066     * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming
5067     * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells
5068     * in this. This is pseudo-random with a fixed seed, but is very good at avoiding overlap (just as good as
5069     * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a
5070     * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in
5071     * testing. Does not restrict the size of the returned array other than only using up to
5072     * {@code fraction * size()} cells.
5073     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5074     * @return a freshly-allocated Coord array containing the quasi-random cells
5075     */
5076    public Coord[] mixedRandomSeparated(double fraction)
5077    {
5078        return mixedRandomSeparated(fraction, -1, 1L);
5079    }
5080
5081    /**
5082     * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming
5083     * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells
5084     * in this. This is pseudo-random with a fixed seed, but is very good at avoiding overlap (just as good as
5085     * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a
5086     * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in
5087     * testing. Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells
5088     * are "on"). If limit is negative, this will not restrict the size.
5089     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5090     * @param limit the maximum size of the array to return
5091     * @return a freshly-allocated Coord array containing the pseudo-random cells
5092     */
5093    public Coord[] mixedRandomSeparated(double fraction, int limit)
5094    {
5095        return mixedRandomSeparated(fraction, limit, 1L);
5096    }
5097
5098    /**
5099     * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming
5100     * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells
5101     * in this. This is pseudo-random with the given seed (which will be made into an odd number if it is not one
5102     * already), and is very good at avoiding overlap (just as good as {@link #separatedZCurve(double, int)}, and
5103     * probably faster). If you request too many cells (too high of a value for fraction), it will start to overlap, but
5104     * a fraction value of 0.4 reliably has had no overlap in testing. Restricts the total size of the returned array to
5105     * a maximum of {@code limit} (minimum is 0 if no cells are "on"). If limit is negative, this will not restrict the
5106     * size.
5107     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5108     * @param limit the maximum size of the array to return
5109     * @param seed a long seed to change the points; the most significant 21 bits (except the sign bit) and least significant bit are ignored
5110     * @return a freshly-allocated Coord array containing the pseudo-random cells
5111     */
5112    public Coord[] mixedRandomSeparated(double fraction, int limit, long seed) {
5113        Coord[] result;
5114        if (fraction < 0) {
5115            result = new Coord[0];
5116        } else {
5117            if (fraction > 1)
5118                fraction = 1;
5119            int tmp, ic;
5120            long t, w;
5121            seed |= 1L;
5122            final int total = size();
5123            int ct = (int) (total * fraction);
5124            if (limit >= 0 && limit < ct)
5125                ct = limit;
5126            Coord[] vl = new Coord[ct];
5127            EACH_QUASI:
5128            for (int i = 0; i < ct; i++) {
5129                tmp = (int) (VanDerCorputQRNG.altDetermine(seed, i + 1) * total);
5130
5131                for (int s = 0; s < ySections; s++) {
5132                    for (int x = 0; x < width; x++) {
5133                        if ((ic = counts[x * ySections + s]) > tmp) {
5134                            t = data[x * ySections + s] | 0L;
5135                            for (--ic; t != 0; ic--) {
5136                                w = NumberTools.lowestOneBit(t);
5137                                if (ic == tmp) {
5138                                    vl[i] = Coord.get(x, (s << 6) | Long.bitCount(w - 1));
5139                                    continue EACH_QUASI;
5140                                }
5141                                t ^= w;
5142                            }
5143                        }
5144                    }
5145                }
5146            }
5147            result = vl;
5148        }
5149        return result;
5150    }
5151
5152
5153
5154    /**
5155     * Modifies this GreasedRegion so it contains a deterministic but random-seeming subset of its previous contents,
5156     * choosing cells so that the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells
5157     * in this. This is pseudo-random with a fixed seed, and is very good at avoiding overlap (just as good as
5158     * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a
5159     * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in
5160     * testing. Does not restrict the count of "on" cells after this returns other than by only using up to
5161     * {@code fraction * size()} cells.
5162     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5163     * @return this for chaining
5164     */
5165    public GreasedRegion mixedRandomRegion(double fraction) {
5166        return mixedRandomRegion(fraction, -1, 1L);
5167    }
5168    /**
5169     * Modifies this GreasedRegion so it contains a deterministic but random-seeming subset of its previous contents,
5170     * choosing cells so that the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells
5171     * in this. This is pseudo-random with a fixed seed, and is very good at avoiding overlap (just as good as
5172     * {@link #separatedRegionZCurve(double, int)}, and probably faster). If you request too many cells (too high of a
5173     * value for fraction), it will start to overlap, but a fraction value of 0.4 reliably has had no overlap in
5174     * testing. Restricts the total count of "on" cells after this returns to a maximum of {@code limit} (minimum is 0
5175     * if no cells are "on"). If limit is negative, this will not restrict the count.
5176     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5177     * @param limit the maximum count of "on" cells to keep
5178     * @return this for chaining
5179     */
5180    public GreasedRegion mixedRandomRegion(double fraction, int limit) {
5181        return mixedRandomRegion(fraction, limit, 1L);
5182    }
5183    /**
5184     * Modifies this GreasedRegion so it contains a deterministic but random-seeming subset of its previous contents,
5185     * choosing cells so that the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells
5186     * in this. This is pseudo-random with the given seed (which will be made into an odd number if it is not one
5187     * already), and is very good at avoiding overlap (just as good as {@link #separatedZCurve(double, int)}, and
5188     * probably faster). If you request too many cells (too high of a value for fraction), it will start to overlap, but
5189     * a fraction value of 0.4 reliably has had no overlap in testing. Restricts the total count of "on" cells after
5190     * this returns to a maximum of {@code limit} (minimum is 0 if no cells are "on"). If limit is negative, this will
5191     * not restrict the count.
5192     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5193     * @param limit the maximum count of "on" cells to keep
5194     * @return this for chaining
5195     */
5196    public GreasedRegion mixedRandomRegion(double fraction, int limit, long seed) {
5197        GreasedRegion result = this;
5198        int idx, run = 0;
5199        final int total = size();
5200        if (total <= limit) {
5201        } else if (total <= 0) {
5202            result = empty();
5203        } else {
5204            if (limit < 0)
5205                limit = (int) (fraction * ct);
5206            if (limit <= 0) {
5207                result = empty();
5208            } else {
5209                seed |= 1L;
5210                int[] order = new int[limit];
5211                for (int i = 0, m = 1; i < limit; i++, m++) {
5212                    idx = (int) (VanDerCorputQRNG.altDetermine(seed, m) * total);
5213                    BIG:
5214                    while (true) {
5215                        for (int j = 0; j < i; j++) {
5216                            if (order[j] == idx) {
5217                                idx = (int) (VanDerCorputQRNG.altDetermine(seed, ++m) * total);
5218                                continue BIG;
5219                            }
5220                        }
5221                        break;
5222                    }
5223                    order[i] = idx;
5224                }
5225                idx = 0;
5226                Arrays.sort(order);
5227                long t, w;
5228                ALL:
5229                for (int s = 0; s < ySections; s++) {
5230                    for (int x = 0; x < width; x++) {
5231                        if ((t = data[x * ySections + s] | 0L) != 0L) {
5232                            w = NumberTools.lowestOneBit(t);
5233                            while (w != 0) {
5234                                if (run++ == order[idx]) {
5235                                    if (++idx >= limit) {
5236                                        data[x * ySections + s] &= (w << 1) - 1;
5237                                        for (int rx = x + 1; rx < width; rx++) {
5238                                            data[rx * ySections + s] = 0;
5239                                        }
5240                                        for (int rs = s + 1; rs < ySections; rs++) {
5241                                            for (int rx = 0; rx < width; rx++) {
5242                                                data[rx * ySections + rs] = 0;
5243                                            }
5244                                        }
5245                                        break ALL;
5246                                    }
5247                                } else {
5248                                    data[x * ySections + s] ^= w;
5249                                }
5250                                t ^= w;
5251                                w = NumberTools.lowestOneBit(t);
5252                            }
5253                        }
5254                    }
5255                }
5256                tallied = false;
5257            }
5258        }
5259        return result;
5260    }
5261
5262    /**
5263     * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells
5264     * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
5265     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
5266     * request too many cells (too high of a value for fraction), it will start to overlap, however.
5267     * Does not restrict the size of the returned array other than only using up to {@code fraction * size()} cells.
5268     * <br>
5269     * You can choose between {@link #mixedRandomSeparated(double)}, {@link #separatedZCurve(double)}, 
5270     * {@link #separatedBlue(double)}, and this method, where all are quasi-random, mixedRandom and
5271     * separatedBlue are probably fastest, ZCurve and separatedBlue may have better 2-dimensional gaps
5272     * between cells, and this method is somewhere in the middle.
5273     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5274     * @return a freshly-allocated Coord array containing the quasi-random cells
5275     */
5276    public Coord[] quasiRandomSeparated(double fraction)
5277    {
5278        return quasiRandomSeparated(fraction, -1);
5279    }
5280
5281    /**
5282     * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells
5283     * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
5284     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
5285     * request too many cells (too high of a value for fraction), it will start to overlap, however.
5286     * Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells are "on").
5287     * If limit is negative, this will not restrict the size.
5288     * <br>
5289     * You can choose between {@link #mixedRandomSeparated(double)}, {@link #separatedZCurve(double)}, 
5290     * {@link #separatedBlue(double)}, and this method, where all are quasi-random, mixedRandom and
5291     * separatedBlue are probably fastest, ZCurve and separatedBlue may have better 2-dimensional gaps
5292     * between cells, and this method is somewhere in the middle.
5293     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5294     * @param limit the maximum size of the array to return
5295     * @return a freshly-allocated Coord array containing the quasi-random cells
5296     */
5297    public Coord[] quasiRandomSeparated(double fraction, int limit) {
5298        Coord[] result;
5299        if (fraction < 0) {
5300            result = new Coord[0];
5301        } else {
5302            if (fraction > 1)
5303                fraction = 1;
5304            int ct = 0, tmp, total, ic;
5305            long t, w;
5306            int[] counts = new int[width * ySections];
5307            for (int i = 0; i < width * ySections; i++) {
5308                tmp = Long.bitCount(data[i]);
5309                counts[i] = tmp == 0 ? -1 : (ct += tmp);
5310            }
5311            total = ct;
5312            ct *= fraction;// (int)(fraction * ct);
5313            if (limit >= 0 && limit < ct)
5314                ct = limit;
5315            Coord[] vl = new Coord[ct];
5316            EACH_QUASI:
5317            for (int i = 0; i < ct; i++) {
5318                tmp = (int) (VanDerCorputQRNG.determine2(i ^ i >>> 1) * total);
5319                for (int s = 0; s < ySections; s++) {
5320                    for (int x = 0; x < width; x++) {
5321                        if ((ic = counts[x * ySections + s]) > tmp) {
5322                            t = data[x * ySections + s];
5323                            w = NumberTools.lowestOneBit(t);
5324                            for (--ic; w != 0; ic--) {
5325                                if (ic == tmp) {
5326                                    vl[i] = Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w));
5327                                    continue EACH_QUASI;
5328                                }
5329                                t ^= w;
5330                                w = NumberTools.lowestOneBit(t);
5331                            }
5332                        }
5333                    }
5334                }
5335                vl[i] = atFraction(tmp);
5336            }
5337            result = vl;
5338        }
5339        return result;
5340    }
5341
5342
5343
5344    /**
5345     * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that
5346     * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
5347     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
5348     * request too many cells (too high of a value for fraction), it will start to overlap, however.
5349     * Does not restrict the count of "on" cells after this returns other than by only using up to
5350     * {@code fraction * size()} cells.
5351     * <br>
5352     * You can choose between {@link #mixedRandomRegion(double)}, {@link #separatedRegionZCurve(double)}, 
5353     * {@link #separatedRegionBlue(double)}, and this method, where all are quasi-random, mixedRandom and
5354     * separatedRegionBlue are probably fastest, ZCurve and separatedRegionBlue may have better 2-dimensional gaps
5355     * between cells, and this method is somewhere in the middle.
5356     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5357     * @return this for chaining
5358     */
5359    public GreasedRegion quasiRandomRegion(double fraction) {
5360        return quasiRandomRegion(fraction, -1);
5361    }
5362    /**
5363     * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that
5364     * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
5365     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
5366     * request too many cells (too high of a value for fraction), it will start to overlap, however.
5367     * Restricts the total count of "on" cells after this returns to a maximum of {@code limit} (minimum is 0 if no
5368     * cells are "on"). If limit is negative, this will not restrict the count.
5369     * <br>
5370     * You can choose between {@link #mixedRandomRegion(double)}, {@link #separatedRegionZCurve(double)},
5371     * {@link #separatedRegionBlue(double)}, and this method, where all are quasi-random, mixedRandom and
5372     * separatedRegionBlue are probably fastest, ZCurve and separatedRegionBlue may have better 2-dimensional gaps
5373     * between cells, and this method is somewhere in the middle.
5374     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
5375     * @param limit the maximum count of "on" cells to keep
5376     * @return this for chaining
5377     */
5378    public GreasedRegion quasiRandomRegion(double fraction, int limit) {
5379        GreasedRegion result = this;
5380        int ct = size(), idx, run = 0;
5381        if (ct <= limit) {
5382        } else if (ct <= 0) {
5383            result = empty();
5384        } else {
5385            if (limit < 0)
5386                limit = (int) (fraction * ct);
5387            if (limit <= 0) {
5388                result = empty();
5389            } else {
5390                int[] order = new int[limit];
5391                for (int i = 0, m = 1; i < limit; i++, m++) {
5392                    idx = (int) (VanDerCorputQRNG.determine2(m) * ct);
5393                    BIG:
5394                    while (true) {
5395                        for (int j = 0; j < i; j++) {
5396                            if (order[j] == idx) {
5397                                idx = (int) (VanDerCorputQRNG.determine2(++m) * ct);
5398                                continue BIG;
5399                            }
5400                        }
5401                        break;
5402                    }
5403                    order[i] = idx;
5404                }
5405                idx = 0;
5406                Arrays.sort(order);
5407                long t, w;
5408                ALL:
5409                for (int s = 0; s < ySections; s++) {
5410                    for (int x = 0; x < width; x++) {
5411                        if ((t = data[x * ySections + s]) != 0) {
5412                            w = NumberTools.lowestOneBit(t);
5413                            while (w != 0) {
5414                                if (run++ == order[idx]) {
5415                                    if (++idx >= limit) {
5416                                        data[x * ySections + s] &= (w << 1) - 1;
5417                                        for (int rx = x + 1; rx < width; rx++) {
5418                                            data[rx * ySections + s] = 0;
5419                                        }
5420                                        for (int rs = s + 1; rs < ySections; rs++) {
5421                                            for (int rx = 0; rx < width; rx++) {
5422                                                data[rx * ySections + rs] = 0;
5423                                            }
5424                                        }
5425                                        break ALL;
5426                                    }
5427                                } else {
5428                                    data[x * ySections + s] ^= w;
5429                                }
5430                                t ^= w;
5431                                w = NumberTools.lowestOneBit(t);
5432                            }
5433                        }
5434                    }
5435                }
5436                tallied = false;
5437            }
5438        }
5439        return result;
5440    }
5441
5442    /**
5443     * Like {@link #retract()}, this removes the "on" cells that are 4-way-adjacent to any "off" cell, but unlike that
5444     * method it keeps a fraction of those surface cells, quasi-randomly selecting them. This can be thought of as
5445     * running {@link #surface()} on a copy of this GreasedRegion, running {@link #quasiRandomRegion(double)} on that
5446     * surface with the given fractionKept, taking the original GreasedRegion and removing its whole surface with
5447     * {@link #retract()}, then inserting the quasi-randomly-removed surface into this GreasedRegion to replace its
5448     * surface with a randomly "damaged" one.
5449     * @param fractionKept the fraction between 0.0 and 1.0 of how many cells on the outer surface of this to keep "on"
5450     * @return this for chaining
5451     */
5452    public GreasedRegion fray(double fractionKept)
5453    {
5454        GreasedRegion cpy = new GreasedRegion(this).retract();
5455        return xor(cpy).quasiRandomRegion(1.0 - fractionKept).or(cpy);
5456    }
5457
5458    /**
5459     * Modifies this GreasedRegion so it contains a random subset of its previous contents, choosing cells so that the
5460     * distance between any two "on" cells is at least {@code minimumDistance}, with at least one cell as "on" if any
5461     * were "on" in this originally. Does not limit the count of "on" cells in the result.
5462     * @param rng used to generate random positions
5463     * @param minimumDistance the minimum distance between "on" cells in the result
5464     * @return this for chaining
5465     */
5466    public GreasedRegion randomScatter(IRNG rng, int minimumDistance) {
5467        return randomScatter(rng, minimumDistance, -1);
5468    }
5469    /**
5470     * Modifies this GreasedRegion so it contains a random subset of its previous contents, choosing cells so that the
5471     * distance between any two "on" cells is at least {@code minimumDistance}, with at least one cell as "on" if any
5472     * were "on" in this originally.
5473     * Restricts the total count of "on" cells after this returns to a maximum of {@code limit} (minimum is 0 if no
5474     * cells are "on"). If limit is negative, this will not restrict the count.
5475     * @param rng used to generate random positions
5476     * @param minimumDistance the minimum distance between "on" cells in the result
5477     * @param limit the maximum count of "on" cells to keep
5478     * @return this for chaining
5479     */
5480    public GreasedRegion randomScatter(IRNG rng, int minimumDistance, int limit) {
5481        GreasedRegion result = this;
5482        int tmp, total = 0, ct;
5483        tally();
5484        if (this.ct == 0) {
5485        } else if (limit == 0) {
5486            result = empty();
5487        } else {
5488            if (limit < 0)
5489                limit = width * height;
5490            long t, w;
5491            long[] data2 = new long[data.length];
5492            MAIN_LOOP:
5493            while (total < limit) {
5494                if (!tallied)
5495                    tally();
5496                tmp = rng.nextInt(this.ct);
5497
5498                for (int s = 0; s < ySections; s++) {
5499                    for (int x = 0; x < width; x++) {
5500                        if ((ct = counts[x * ySections + s]) > tmp) {
5501                            t = data[x * ySections + s];
5502                            w = NumberTools.lowestOneBit(t);
5503                            for (--ct; w != 0; ct--) {
5504                                if (ct == tmp) {
5505                                    removeRectangle(x - minimumDistance,
5506                                            ((s << 6) | Long.numberOfTrailingZeros(w)) - minimumDistance,
5507                                            minimumDistance << 1 | 1, minimumDistance << 1 | 1);
5508                                    data2[x * ySections + s] |= w;
5509                                    ++total;
5510                                    continue MAIN_LOOP;
5511                                }
5512                                t ^= w;
5513                                w = NumberTools.lowestOneBit(t);
5514                            }
5515                        }
5516                    }
5517                }
5518                break;
5519            }
5520            data = data2;
5521            tallied = false;
5522        }
5523        return result;
5524    }
5525
5526    public double rateDensity() {
5527        double result = 0;
5528        double sz = height * width;
5529        if (sz != 0) {
5530            double onAmount = sz - size(), retractedOn = sz - copy().retract().size();
5531            result = (onAmount + retractedOn) / (sz * 2.0);
5532        }
5533        return result;
5534    }
5535    public double rateRegularity() {
5536        double result = 0;
5537        GreasedRegion me2 = copy().surface8way();
5538        double irregularCount = me2.size();
5539        if (irregularCount != 0) {
5540            result = me2.remake(this).surface().size() / irregularCount;
5541        }
5542        return result;
5543    }
5544
5545    private static int median(int[] working, int start, int amount) {
5546        int result;
5547        Arrays.sort(working, start, start + amount);
5548        if ((amount & 1) == 0) {
5549            result = working[start + (amount >> 1) - 1] + working[start + (amount >> 1)] >>> 1;
5550        } else {
5551            result = working[start + (amount >> 1)];
5552        }
5553        return result;
5554    }
5555
5556    /**
5557     * Calculates a perceptual hash for this GreasedRegion using a method that is only precise for some sizes of
5558     * GreasedRegion; it writes a result to into, and uses working as a temporary buffer. The lengths of into and
5559     * working should be related; if into is length 1, then working should be length 64, and though the hash won't be
5560     * very detailed, it will work well for images with width and height that are multiples of 8; if into is length 4,
5561     * then working should be length 256, and this will work with more detail on images that have width and height that
5562     * are multiples of 16. If working is null or is too small, then this won't reuse it and will allocate an
5563     * appropriately-sized array for internal use.
5564     * <br>
5565     * Ported from https://github.com/commonsmachinery/blockhash/blob/master/blockhash.c , which is MIT-licensed.
5566     * @param into should be a long array of length 1 or 4; the contents don't matter and this will be where output is written to
5567     * @param working should be an int array of length 64 (if into has length 1) or 256 (if into has length 4); may be null if you like garbage collection
5568     */
5569    public void perceptualHashQuick(long[] into, int[] working)
5570    {
5571        final int bits = 8 << (Integer.numberOfTrailingZeros(Integer.highestOneBit(into.length)) >> 1);
5572        if(working == null || working.length < bits * bits)
5573            working = new int[bits * bits];
5574        final int blockWidth = width / bits, blockHeight = height / bits, blockWidthSections = blockWidth * ySections;
5575        if(blockHeight == 1)
5576        {
5577            for (int y = 0; y < bits; y++) {
5578                for (int x = 0; x < bits; x++) {
5579                    int value = 0;
5580                    for (int ix = 0; ix < blockWidthSections; ix += ySections) {
5581                        value += (data[x * blockWidthSections + ix + (y >> 6)] >>> (y & 63) & 1L);
5582                    }
5583                    working[x * bits + y] = value;
5584                }
5585            }
5586        }
5587        else if(blockHeight < 64 && Integer.bitCount(blockHeight) == 1) {
5588            final long yBlockMask = ~(-1L << blockHeight);
5589            final int divisorMask = (64 / blockHeight) - 1;
5590            long currentMask;
5591            int blockY = 0;
5592            for (int y = 0; y < bits; y++, blockY += blockHeight) {
5593                currentMask = yBlockMask << ((y & divisorMask) << blockHeight);
5594                for (int x = 0; x < bits; x++) {
5595                    int value = 0;
5596                    for (int ix = 0; ix < blockWidthSections; ix += ySections) {
5597                        value += Long.bitCount(data[x * blockWidthSections + ix + (blockY >> 6)] & currentMask);
5598                    }
5599                    working[x * bits + y] = value;
5600                }
5601            }
5602        }
5603        final int cellsPerBlock = blockWidth * blockHeight, numBlocks = bits * bits,
5604                halfCellCount = cellsPerBlock >>> 1;
5605        int bandSize = numBlocks >>> 2;
5606        int m, v;
5607        int currentInto = 0;
5608        long currentIntoPos = 1L;
5609        for (int i = 0; i < 4; i++) {
5610            m = median(working, i * bandSize, bandSize);
5611            for (int j = i * bandSize; j < (i + 1) * bandSize; j++) {
5612                v = working[j];
5613                if(v > m || (v - m == 0 && m > halfCellCount)) into[currentInto] |= currentIntoPos;
5614                if((currentIntoPos <<= 1) == 0)
5615                {
5616                    ++currentInto;
5617                    currentIntoPos = 1L;
5618                }
5619            }
5620        }
5621    }
5622
5623    /*
5624    // This showed a strong x-y correlation because it didn't have a way to use a non-base-2 van der Corput sequence.
5625    // It also produced very close-together points, unfortunately.
5626    public static double quasiRandomX(int idx)
5627    {
5628        return atVDCSequence(26 + idx * 5);
5629    }
5630    public static double quasiRandomY(int idx)
5631    {
5632        return atVDCSequence(19 + idx * 3);
5633    }
5634
5635    private static double atVDCSequence(int idx)
5636    {
5637        int leading = Integer.numberOfLeadingZeros(idx);
5638        return (Integer.reverse(idx) >>> leading) / (1.0 * (1 << (32 - leading)));
5639    }
5640    */
5641    public Coord[] asCoords()
5642    {
5643        return asCoords(new Coord[size()]);
5644
5645    }
5646    public Coord[] asCoords(Coord[] points) {
5647        Coord[] result = null;
5648        if (points == null)
5649            points = new Coord[size()];
5650        int idx = 0, len = points.length;
5651        long t, w;
5652        for (int x = 0; x < width; x++) {
5653            for (int s = 0; s < ySections; s++) {
5654                if ((t = data[x * ySections + s]) != 0) {
5655                    w = NumberTools.lowestOneBit(t);
5656                    while (w != 0) {
5657                        if (idx >= len) {
5658                            result = points;
5659                            break;
5660                        }
5661                        points[idx++] = Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w));
5662                        t ^= w;
5663                        w = NumberTools.lowestOneBit(t);
5664                    }
5665                    if (result != null) break;
5666                }
5667            }
5668            if (result != null) break;
5669        }
5670        if (result == null) {
5671            result = points;
5672        }
5673        return result;
5674    }
5675    public int[] asEncoded()
5676    {
5677        int ct = size(), idx = 0;
5678        int[] points = new int[ct];
5679        long t, w;
5680        for (int x = 0; x < width; x++) {
5681            for (int s = 0; s < ySections; s++) {
5682                if((t = data[x * ySections + s]) != 0)
5683                {
5684                    w = NumberTools.lowestOneBit(t);
5685                    while (w != 0) {
5686                        points[idx++] = Coord.pureEncode(x, (s << 6) | Long.numberOfTrailingZeros(w));
5687                        t ^= w;
5688                        w = NumberTools.lowestOneBit(t);
5689                    }
5690                }
5691            }
5692        }
5693        return points;
5694    }
5695    public int[] asTightEncoded()
5696    {
5697        int ct = size(), idx = 0;
5698        int[] points = new int[ct];
5699        long t, w;
5700        for (int x = 0; x < width; x++) {
5701            for (int s = 0; s < ySections; s++) {
5702                if((t = data[x * ySections + s]) != 0)
5703                {
5704                    w = NumberTools.lowestOneBit(t);
5705                    while (w != 0) {
5706                        points[idx++] =  ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x;
5707                        t ^= w;
5708                        w = NumberTools.lowestOneBit(t);
5709                    }
5710                }
5711            }
5712        }
5713        return points;
5714    }
5715
5716    /**
5717     * @return All cells in this zone.
5718     */
5719    @Override
5720    public List<Coord> getAll() {
5721        ArrayList<Coord> points = new ArrayList<>();
5722        long t, w;
5723        for (int x = 0; x < width; x++) {
5724            for (int s = 0; s < ySections; s++) {
5725                if((t = data[x * ySections + s]) != 0)
5726                {
5727                    w = NumberTools.lowestOneBit(t);
5728                    while (w != 0) {
5729                        points.add(Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w)));
5730                        t ^= w;
5731                        w = NumberTools.lowestOneBit(t);
5732                    }
5733                }
5734            }
5735        }
5736        return points;
5737
5738    }
5739
5740    /**
5741     * Gets the first Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty.
5742     * @return the first Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty
5743     */
5744    public Coord first()
5745    {
5746        long w;
5747        for (int x = 0; x < width; x++) {
5748            for (int s = 0; s < ySections; s++) {
5749                if ((w = (data[x * ySections + s])) != 0) {
5750                    return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w));
5751                }
5752            }
5753        }
5754        return Coord.get(-1, -1);
5755    }
5756
5757    public int firstTight()
5758    {
5759        long w;
5760        for (int x = 0; x < width; x++) {
5761            for (int s = 0; s < ySections; s++) {
5762                if ((w = (data[x * ySections + s])) != 0) {
5763                    return ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x;
5764                }
5765            }
5766        }
5767        return -1;
5768    }
5769
5770    /**
5771     * Gets the last Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty.
5772     * @return the last Coord in the iteration order, or (-1,-1) if this GreasedRegion is empty
5773     */
5774    public Coord last()
5775    {
5776        long w;
5777        for (int x = width - 1; x >= 0; x--) {
5778            for (int s = ySections - 1; s >= 0; s--) {
5779                if ((w = (data[x * ySections + s])) != 0) {
5780                    return Coord.get(x, (s << 6) | 63 - Long.numberOfLeadingZeros(w));
5781                }
5782            }
5783        }
5784        return Coord.get(-1, -1);
5785    }
5786
5787    public int lastTight()
5788    {
5789        long w;
5790        for (int x = width - 1; x >= 0; x--) {
5791            for (int s = ySections - 1; s >= 0; s--) {
5792                if ((w = (data[x * ySections + s])) != 0) {
5793                    return ((s << 6) | 63 - Long.numberOfLeadingZeros(w)) * width + x;
5794                }
5795            }
5796        }
5797        return -1;
5798    }
5799
5800    public Coord nth(final int index)
5801    {
5802        if(index < 0)
5803            return Coord.get(-1, -1);
5804        int ct = size(), tmp;
5805        if(index >= ct)
5806            return Coord.get(-1, -1);
5807        long t, w;
5808        for (int s = 0; s < ySections; s++) {
5809            for (int x = 0; x < width; x++) {
5810                if ((ct = counts[x * ySections + s]) > index) {
5811                    t = data[x * ySections + s];
5812                    w = NumberTools.lowestOneBit(t);
5813                    for (--ct; w != 0; ct--) {
5814                        if (ct == index)
5815                            return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w));
5816                        t ^= w;
5817                        w = NumberTools.lowestOneBit(t);
5818                    }
5819                }
5820            }
5821        }
5822        return Coord.get(-1, -1);
5823    }
5824
5825    public Coord atFraction(final double fraction)
5826    {
5827        int ct = size(), tmp;
5828        if(ct <= 0) return Coord.get(-1, -1);
5829        tmp = Math.abs((int)(fraction * ct) % ct);
5830        long t, w;
5831        for (int s = 0; s < ySections; s++) {
5832            for (int x = 0; x < width; x++) {
5833                if ((ct = counts[x * ySections + s]) > tmp) {
5834                    t = data[x * ySections + s];
5835                    w = NumberTools.lowestOneBit(t);
5836                    for (--ct; w != 0; ct--) {
5837                        if (ct == tmp)
5838                            return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w));
5839                        t ^= w;
5840                        w = NumberTools.lowestOneBit(t);
5841                    }
5842                }
5843            }
5844        }
5845        return Coord.get(-1, -1);
5846    }
5847
5848    public int atFractionTight(final double fraction)
5849    {
5850        int ct = size(), tmp;
5851        if(ct <= 0) return -1;
5852        tmp = Math.abs((int)(fraction * ct) % ct);
5853        long t, w;
5854
5855        for (int x = 0; x < width; x++) {
5856            for (int s = 0; s < ySections; s++) {
5857                if ((ct = counts[x * ySections + s]) > tmp) {
5858                    t = data[x * ySections + s];
5859                    w = NumberTools.lowestOneBit(t);
5860                    for (--ct; w != 0; ct--) {
5861                        if (ct == tmp)
5862                            return ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x;
5863                        t ^= w;
5864                        w = NumberTools.lowestOneBit(t);
5865                    }
5866                }
5867            }
5868        }
5869        return -1;
5870    }
5871
5872    /**
5873     * Gets a single random Coord from the "on" positions in this GreasedRegion, or the Coord (-1,-1) if this is empty.
5874     * Uses the given IRNG to generate one random int, which is used as an index. The technique this uses to iterate
5875     * over bits can be credited to Erling Ellingsen and Daniel Lemire, found
5876     * <a href="https://lemire.me/blog/2013/12/23/even-faster-bitmap-decoding/">here</a>, and seems to be a little
5877     * faster than the previous method. The fastest way to get a random Coord from a GreasedRegion is to avoid iterating
5878     * over the bits at all, so if your region data doesn't change you should get it as a Coord array with
5879     * {@link #asCoords()} and call {@link IRNG#getRandomElement(Object[])} with that array as the parameter. If you
5880     * take the asCoords() call out of consideration, getting random elements out of an array (especially a large one)
5881     * can be hundreds of times faster.
5882     * @param rng an IRNG such as an {@link RNG} or {@link GWTRNG}
5883     * @return a single randomly-chosen Coord from the "on" positions in this GreasedRegion, or (-1,-1) if empty
5884     */
5885    public Coord singleRandom(IRNG rng)
5886    {
5887        int ct = size(), tmp = rng.nextInt(ct);
5888        long t, w;
5889        for (int s = 0; s < ySections; s++) {
5890            for (int x = 0; x < width; x++) {
5891                if ((ct = counts[x * ySections + s]) > tmp) {
5892                    t = data[x * ySections + s] | 0L;
5893                    for (--ct; t != 0; ct--) {
5894                        w = NumberTools.lowestOneBit(t);
5895                        if (ct == tmp)
5896                            return Coord.get(x, (s << 6) | Long.bitCount(w-1));
5897                        t ^= w;
5898                    }
5899                }
5900            }
5901        }
5902        return Coord.get(-1, -1);
5903    }
5904
5905    public int singleRandomTight(IRNG rng)
5906    {
5907        int ct = size(), tmp = rng.nextInt(ct);
5908        long t, w;
5909        for (int s = 0; s < ySections; s++) {
5910            for (int x = 0; x < width; x++) {
5911                if ((ct = counts[x * ySections + s]) > tmp) {
5912                    t = data[x * ySections + s];
5913                    w = NumberTools.lowestOneBit(t);
5914                    for (--ct; w != 0; ct--) {
5915                        if (ct == tmp)
5916                            return ((s << 6) | Long.numberOfTrailingZeros(w)) * width + x;
5917                        t ^= w;
5918                        w = NumberTools.lowestOneBit(t);
5919                    }
5920                }
5921            }
5922        }
5923
5924        return -1;
5925    }
5926
5927    /**
5928     * Narrow-purpose; takes an x and a y value, each between 0 and 65535 inclusive, and interleaves their bits so the
5929     * least significant bit and every other bit after it are filled with the bits of x, while the
5930     * second-least-significant bit and every other bit after that are filled with the bits of y. Essentially, this
5931     * takes two numbers with bits labeled like {@code a b c} for x and {@code R S T} for y and makes a number with
5932     * those bits arranged like {@code R a S b T c}.
5933     * @param x an int between 0 and 65535, inclusive
5934     * @param y an int between 0 and 65535, inclusive
5935     * @return an int that interleaves x and y, with x in the least significant bit position
5936     */
5937    public static int interleaveBits(int x, int y)
5938    {
5939        x |= y << 16;
5940        x =    ((x & 0x0000ff00) << 8) | ((x >>> 8) & 0x0000ff00) | (x & 0xff0000ff);
5941        x =    ((x & 0x00f000f0) << 4) | ((x >>> 4) & 0x00f000f0) | (x & 0xf00ff00f);
5942        x =    ((x & 0x0c0c0c0c) << 2) | ((x >>> 2) & 0x0c0c0c0c) | (x & 0xc3c3c3c3);
5943        return ((x & 0x22222222) << 1) | ((x >>> 1) & 0x22222222) | (x & 0x99999999);
5944    }
5945
5946    /**
5947     * Narrow-purpose; takes an int that represents a distance down the Z-order curve and moves its bits around so that
5948     * its x component is stored in the bottom 16 bits (use {@code (n & 0xffff)} to obtain) and its y component is
5949     * stored in the upper 16 bits (use {@code (n >>> 16)} to obtain). This may be useful for ordering traversals of all
5950     * points in a GreasedRegion less predictably.
5951     * @param n an int that has already been interleaved, though this can really be any int
5952     * @return an int with x in its lower bits ({@code x = n & 0xffff;}) and y in its upper bits ({@code y = n >>> 16;})
5953     */
5954    public static int disperseBits(int n)
5955    {
5956        n =    ((n & 0x22222222) << 1) | ((n >>> 1) & 0x22222222) | (n & 0x99999999);
5957        n =    ((n & 0x0c0c0c0c) << 2) | ((n >>> 2) & 0x0c0c0c0c) | (n & 0xc3c3c3c3);
5958        n =    ((n & 0x00f000f0) << 4) | ((n >>> 4) & 0x00f000f0) | (n & 0xf00ff00f);
5959        return ((n & 0x0000ff00) << 8) | ((n >>> 8) & 0x0000ff00) | (n & 0xff0000ff);
5960    }
5961    private static int nextPowerOfTwo(int n)
5962    {
5963        final int highest = Integer.highestOneBit(n);
5964        return  (highest == NumberTools.lowestOneBit(n)) ? highest : highest << 1;
5965    }
5966
5967    /**
5968     * Like {@link #nth(int)}, this gets the Coord at a given index along a path through the GreasedRegion, but unlike
5969     * nth(), this traverses the path in a zig-zag pattern called the Z-Order Curve. This method is often not very fast
5970     * compared to nth(), but this different path can help if iteration needs to seem less regular while still covering
5971     * all "on" cells in the GresedRegion eventually.
5972     * @param index the distance along the Z-order curve to travel, only counting "on" cells in this GreasedRegion.
5973     * @return the Coord at the given distance, or the Coord with x and y both -1 if index is too high or low
5974     */
5975    public Coord nthZCurve(final int index)
5976    {
5977        int ct = -1, x, y, s, d, max = nextPowerOfTwo(width) * nextPowerOfTwo(height);
5978        long t, w;
5979        for (int o = 0; o < max; o++) {
5980            d = disperseBits(o);
5981            x = d & 0xffff;
5982            y = d >>> 16;
5983            if(x >= width && y >= height)
5984                break;
5985            if(x >= width)
5986                continue;
5987            if(y >= height)
5988                continue;
5989            s = d >>> 22;
5990            t = data[x * ySections + s];
5991            if((ct += (t >>> (y & 63) & 1L)) == index)
5992                return Coord.get(x, y);
5993        }
5994        return Coord.get(-1, -1);
5995    }
5996    /**
5997     * Like {@link #nth(int)}, this finds a given index along a path through the GreasedRegion, but unlike nth(), this
5998     * traverses the path in a zig-zag pattern called the Z-Order Curve, and unlike {@link #nthZCurve(int)}, this does
5999     * not return a Coord and instead produces a "tight"-encoded int. This method is often not very fast compared to
6000     * nth(), but this different path can help if iteration needs to seem less regular while still covering all "on"
6001     * cells in the GreasedRegion eventually, and the tight encoding may be handy if you need to use ints.
6002     * @param index the distance along the Z-order curve to travel, only counting "on" cells in this GreasedRegion.
6003     * @return the "tight" encoded point at the given distance, or -1 if index is too high or low
6004     */
6005
6006    public int nthZCurveTight(final int index)
6007    {
6008        int ct = -1, x, y, s, d, max = nextPowerOfTwo(width) * nextPowerOfTwo(height);
6009        long t;
6010        for (int o = 0; o < max; o++) {
6011            d = disperseBits(o);
6012            x = d & 0xffff;
6013            y = d >>> 16;
6014            if(x >= width && y >= height)
6015                break;
6016            if(x >= width)
6017                continue;
6018            if(y >= height)
6019                continue;
6020            s = d >>> 22;
6021            t = data[x * ySections + s];
6022            if((ct += (t >>> (y & 63) & 1L)) == index)
6023                return y * width + x;
6024        }
6025        return -1;
6026    }
6027
6028    /**
6029     * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells
6030     * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6031     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
6032     * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with
6033     * {@link #mixedRandomSeparated(double, int)}, which tends to overlap more frequently. This method seems to work
6034     * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler
6035     * approach mixedRandomSeparated uses to traverse points (which just runs through the "on" cells a column at a time,
6036     * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon
6037     * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomSeparated has
6038     * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by
6039     * {@link #quasiRandomSeparated(double)} has it overlapping at the same rate as this method, though it should be
6040     * much faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that are
6041     * outside the GreasedRegion but are on the Z-Order Curve.
6042     * Does not restrict the size of the returned array other than only using up to {@code fraction * size()} cells.
6043     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
6044     * @return a freshly-allocated Coord array containing the quasi-random cells
6045     */
6046    public Coord[] separatedZCurve(double fraction)
6047    {
6048        return separatedZCurve(fraction, -1);
6049    }
6050
6051    /**
6052     * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells
6053     * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6054     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
6055     * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with
6056     * {@link #mixedRandomSeparated(double, int)}, which tends to overlap more frequently. This method seems to work
6057     * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler
6058     * approach mixedRandomSeparated uses to traverse points (which just runs through the "on" cells a column at a time,
6059     * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon
6060     * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomSeparated has
6061     * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by
6062     * {@link #quasiRandomSeparated(double, int)} has it overlapping at the same rate as this method, though it should
6063     * be much faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that
6064     * are outside the GreasedRegion but are on the Z-Order Curve.
6065     * Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells are "on").
6066     * If limit is negative, this will not restrict the size.
6067     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
6068     * @param limit the maximum size of the array to return
6069     * @return a freshly-allocated Coord array containing the quasi-random cells
6070     */
6071    public Coord[] separatedZCurve(double fraction, int limit)
6072    {
6073        if(fraction < 0)
6074            return new Coord[0];
6075        if(fraction > 1)
6076            fraction = 1;
6077        int ct = size(), total;
6078        total = ct;
6079        ct *= fraction;
6080        if(limit >= 0 && limit < ct)
6081            ct = limit;
6082        Coord[] vl = new Coord[ct];
6083        for (int i = 0; i < ct; i++)
6084        {
6085            vl[i] = nthZCurve((int) (VanDerCorputQRNG.determine2(i ^ i >>> 1) * total));
6086        }
6087        return vl;
6088    }
6089    /**
6090     * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that
6091     * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6092     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
6093     * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with
6094     * {@link #mixedRandomRegion(double)}, which tends to overlap more frequently. This method seems to work
6095     * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler
6096     * approach mixedRandomRegion uses to traverse points (which just runs through the "on" cells a column at a time,
6097     * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon
6098     * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomRegion has
6099     * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by
6100     * {@link #quasiRandomRegion(double)} has it overlapping at the same rate as this method, though it should be much
6101     * faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that are
6102     * outside the GreasedRegion but are on the Z-Order Curve.
6103     * Does not restrict the size of the returned array other than only using up to {@code fraction * size()} cells.
6104     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
6105     * @return this, after modifications, for chaining
6106     */
6107    public GreasedRegion separatedRegionZCurve(double fraction)
6108    {
6109        return separatedRegionZCurve(fraction, -1);
6110    }
6111
6112    /**
6113     * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that
6114     * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6115     * random instead of pseudo-random because it is somewhat less likely to produce nearby cells in the result. If you
6116     * request too many cells (too high of a value for fraction), it will start to overlap, however. Contrast with
6117     * {@link #mixedRandomRegion(double, int)}, which tends to overlap more frequently. This method seems to work
6118     * well because it chooses quasi-random points by their index in the Z-Order Curve as opposed to the simpler
6119     * approach mixedRandomRegion uses to traverse points (which just runs through the "on" cells a column at a time,
6120     * not caring if two points are in adjacent cells as long as they are in different columns). Testing with a dungeon
6121     * layout of mostly-on cells, this method has no overlap with a fraction of 0.4, while mixedRandomRegion has
6122     * overlap as early as 0.15 for fraction, and it only gets worse at higher values. A change to the algorithm used by
6123     * {@link #quasiRandomRegion(double, int)} has it overlapping at the same rate as this method, though it should be
6124     * much faster. This method can be especially slow, since each Z-Order traversal may need to try many cells that
6125     * are outside the GreasedRegion but are on the Z-Order Curve.
6126     * Restricts the total size of the returned array to a maximum of {@code limit} (minimum is 0 if no cells are "on").
6127     * If limit is negative, this will not restrict the size.
6128     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
6129     * @param limit the maximum size of the array to return
6130     * @return this, after modifications, for chaining
6131     */
6132    public GreasedRegion separatedRegionZCurve(double fraction, int limit)
6133    {
6134        if(fraction <= 0)
6135            return empty();
6136        if(fraction >= 1)
6137            return this;
6138        int ct = size(), total;
6139        if(limit >= ct)
6140            return this;
6141        total = ct;
6142        ct *= fraction;
6143        if(limit >= 0 && limit < ct)
6144            ct = limit;
6145        int[] vl = new int[ct];
6146        for (int i = 0; i < ct; i++)
6147        {
6148            vl[i] = nthZCurveTight((int) (VanDerCorputQRNG.determine2(i ^ i >>> 1) * total));
6149        }
6150        return empty().insertSeveral(vl);
6151    }
6152
6153    /**
6154     * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells
6155     * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6156     * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being
6157     * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to
6158     * have nearby cells, however. Does not restrict the size of the returned array other than only using up to
6159     * {@code fraction * size()} cells.
6160     * <br>
6161     * Also take a look at {@link #separatedZCurve(double, int)}, {@link #quasiRandomSeparated(double, int)},
6162     * {@link #mixedRandomSeparated(double, int, long)}, and {@link #separatedRegionBlue(double, int)}. Internally, this
6163     * is a thin wrapper around {@link #separatedRegionBlue(double, int)}, and won't be more efficient than that method.
6164     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
6165     * @return a freshly-allocated Coord array containing the quasi-random cells
6166     */
6167    public Coord[] separatedBlue(double fraction)
6168    {
6169        return separatedBlue(fraction, -1);
6170    }
6171
6172    /**
6173     * Gets a Coord array from the "on" contents of this GreasedRegion, using a quasi-random scattering of chosen cells
6174     * with a count that matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6175     * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being
6176     * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to
6177     * have nearby cells, however. Restricts the total size of the returned array to a maximum of {@code limit} (minimum
6178     * is 0 if no cells are "on"). If limit is negative, this will not restrict the size.
6179     * <br>
6180     * Also take a look at {@link #separatedZCurve(double, int)}, {@link #quasiRandomSeparated(double, int)},
6181     * {@link #mixedRandomSeparated(double, int, long)}, and {@link #separatedRegionBlue(double, int)}. Internally, this
6182     * is a thin wrapper around {@link #separatedRegionBlue(double, int)}, and won't be more efficient than that method.
6183     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
6184     * @param limit the maximum size of the array to return; may return less
6185     * @return a freshly-allocated Coord array containing the quasi-random cells
6186     */
6187    public Coord[] separatedBlue(double fraction, int limit)
6188    {
6189        if(fraction <= 0)
6190            return new Coord[0];
6191        if(fraction > 0xFFp-8)
6192            fraction = 0xFFp-8;
6193        int ct = size();
6194        if(limit >= ct)
6195            return asCoords();
6196        return copy().separatedRegionBlue(fraction, limit).asCoords();
6197    }
6198    /**
6199     * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that
6200     * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6201     * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being
6202     * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to
6203     * have nearby cells, however. Does not restrict the size of the returned GreasedRegion other than only using up to
6204     * {@code fraction * size()} cells.
6205     * <br>
6206     * Also take a look at {@link #separatedRegionZCurve(double, int)}, {@link #quasiRandomRegion(double, int)},
6207     * {@link #mixedRandomRegion(double, int, long)}, and {@link #separatedBlue(double, int)}.
6208     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
6209     * @return this, after modifications, for chaining
6210     */
6211    public GreasedRegion separatedRegionBlue(double fraction)
6212    {
6213        return separatedRegionBlue(fraction, -1);
6214    }
6215
6216    /**
6217     * Modifies this GreasedRegion so it contains a quasi-random subset of its previous contents, choosing cells so that
6218     * the {@link #size()} matches the given {@code fraction} of the total amount of "on" cells in this. This is quasi-
6219     * random instead of pseudo-random because it uses blue noise to sharply limit the likelihood of nearby points being
6220     * chosen when fraction is small. If you request too many cells (too high of a value for fraction), it will start to
6221     * have nearby cells, however. Restricts the total size of the returned GreasedRegion to a maximum of {@code limit}
6222     * (minimum is 0 if no cells are "on"). If limit is negative, this will not restrict the size.
6223     * <br>
6224     * Also take a look at {@link #separatedRegionZCurve(double, int)}, {@link #quasiRandomRegion(double, int)},
6225     * {@link #mixedRandomRegion(double, int, long)}, and {@link #separatedBlue(double, int)}.
6226     * @param fraction the fraction of "on" cells to quasi-randomly select, between 0.0 and 1.0
6227     * @param limit the maximum size of the array to return; may return less
6228     * @return this, after modifications, for chaining
6229     */
6230    public GreasedRegion separatedRegionBlue(double fraction, int limit)
6231    {
6232        if(fraction <= 0)
6233            return empty();
6234        if(fraction > 0xFFp-8)
6235            fraction = 0xFFp-8;
6236        int ct = size();
6237        if(limit >= ct)
6238            return this;
6239        ct *= fraction;
6240        if(limit >= 0 && limit < ct)
6241            ct = limit;
6242        for (int i = 255; i >= 0; i--) {
6243            if(ct >= andWrapping64(BlueNoise.LEVELS[i]).size())
6244                return this;
6245        }
6246        return this;
6247    }
6248
6249    public Coord[] randomPortion(IRNG rng, int size)
6250    {
6251        int ct = size(), idx = 0, run = 0;
6252        if(ct <= 0 || size <= 0)
6253            return new Coord[0];
6254        if(ct <= size)
6255            return asCoords();
6256        Coord[] points = new Coord[size];
6257        int[] order = rng.randomOrdering(ct);
6258        Arrays.sort(order, 0, size);
6259        long t, w;
6260        ALL:
6261        for (int s = 0; s < ySections; s++) {
6262            for (int x = 0; x < width; x++) {
6263                if((t = data[x * ySections + s]) != 0)
6264                {
6265                    w = NumberTools.lowestOneBit(t);
6266                    while (w != 0) {
6267                        if (run++ == order[idx]) {
6268                            points[idx++] = Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w));
6269                            if (idx >= size) break ALL;
6270                        }
6271                        t ^= w;
6272                        w = NumberTools.lowestOneBit(t);
6273                    }
6274                }
6275            }
6276        }
6277        return points;
6278    }
6279
6280    public GreasedRegion randomRegion(IRNG rng, int size)
6281    {
6282        int ct = size(), idx = 0, run = 0;
6283        if(ct <= 0 || size <= 0)
6284            return empty();
6285        if(ct <= size)
6286            return this;
6287        int[] order = rng.randomOrdering(ct);
6288        Arrays.sort(order, 0, size);
6289        long t, w;
6290        ALL:
6291        for (int s = 0; s < ySections; s++) {
6292            for (int x = 0; x < width; x++) {
6293                if((t = data[x * ySections + s]) != 0)
6294                {
6295                    w = NumberTools.lowestOneBit(t);
6296                    while (w != 0) {
6297                        if (run++ == order[idx]) {
6298                            if(++idx >= size) break ALL;
6299                        }
6300                        else
6301                        {
6302                            data[x * ySections + s] &= ~(1L << Long.numberOfTrailingZeros(w));
6303                        }
6304                        t ^= w;
6305                        w = NumberTools.lowestOneBit(t);
6306                    }
6307                }
6308            }
6309        }
6310        tallied = false;
6311        return this;
6312    }
6313
6314    @Override
6315    public boolean contains(int x, int y)
6316    {
6317        return x >= 0 && y >= 0 && x < width && y < height && ySections > 0 &&
6318                ((data[x * ySections + (y >> 6)] & (1L << (y & 63))) != 0);
6319    }
6320
6321
6322    /**
6323     * @return Whether this zone is empty.
6324     */
6325    @Override
6326    public boolean isEmpty() {
6327        if(tallied) return ct > 0;
6328        for (int i = 0; i < data.length; i++) {
6329            if(data[i] != 0L) return false;
6330        }
6331        return true;
6332    }
6333
6334    /**
6335     * Generates a 2D int array from an array or vararg of GreasedRegions, starting at all 0 and adding 1 to the int at
6336     * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to
6337     * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can
6338     * produce any number between 0 and 16 in a cell.
6339     * @param regions an array or vararg of GreasedRegions; must all have the same width and height
6340     * @return a 2D int array with the same width and height as the regions, where an int cell equals the number of given GreasedRegions that had an "on" cell at that position
6341     */
6342    public static int[][] sum(GreasedRegion... regions)
6343    {
6344        if(regions == null || regions.length <= 0)
6345            return new int[0][0];
6346        int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections;
6347        int[][] numbers = new int[w][h];
6348        for (int x = 0; x < w; x++) {
6349            for (int y = 0; y < h; y++) {
6350                for (int i = 0; i < l; i++) {
6351                    numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0;
6352                }
6353            }
6354        }
6355        return numbers;
6356    }
6357
6358    /**
6359     * Generates a 2D int array from a List of GreasedRegions, starting at all 0 and adding 1 to the int at
6360     * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to
6361     * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can
6362     * produce any number between 0 and 16 in a cell.
6363     * @param regions a List of GreasedRegions; must all have the same width and height
6364     * @return a 2D int array with the same width and height as the regions, where an int cell equals the number of given GreasedRegions that had an "on" cell at that position
6365     */
6366    public static int[][] sum(List<GreasedRegion> regions)
6367    {
6368        if(regions == null || regions.isEmpty())
6369            return new int[0][0];
6370        GreasedRegion t = regions.get(0);
6371        int w = t.width, h = t.height, l = regions.size(), ys = t.ySections;
6372        int[][] numbers = new int[w][h];
6373        for (int x = 0; x < w; x++) {
6374            for (int y = 0; y < h; y++) {
6375                for (int i = 0; i < l; i++) {
6376                    numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0;
6377                }
6378            }
6379        }
6380        return numbers;
6381    }
6382
6383    /**
6384     * Generates a 2D double array from an array or vararg of GreasedRegions, starting at all 0 and adding 1 to the double at
6385     * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to
6386     * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can
6387     * produce any number between 0 and 16 in a cell.
6388     * @param regions an array or vararg of GreasedRegions; must all have the same width and height
6389     * @return a 2D double array with the same width and height as the regions, where an double cell equals the number of given GreasedRegions that had an "on" cell at that position
6390     */
6391    public static double[][] sumDouble(GreasedRegion... regions)
6392    {
6393        if(regions == null || regions.length <= 0)
6394            return new double[0][0];
6395        int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections;
6396        double[][] numbers = new double[w][h];
6397        for (int x = 0; x < w; x++) {
6398            for (int y = 0; y < h; y++) {
6399                for (int i = 0; i < l; i++) {
6400                    numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1.0 : 0.0;
6401                }
6402            }
6403        }
6404        return numbers;
6405    }
6406
6407    /**
6408     * Generates a 2D double array from a List of GreasedRegions, starting at all 0 and adding 1 to the double at
6409     * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to
6410     * this method, it can produce any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can
6411     * produce any number between 0 and 16 in a cell.
6412     * @param regions a List of GreasedRegions; must all have the same width and height
6413     * @return a 2D double array with the same width and height as the regions, where an double cell equals the number of given GreasedRegions that had an "on" cell at that position
6414     */
6415    public static double[][] sumDouble(List<GreasedRegion> regions)
6416    {
6417        if(regions == null || regions.isEmpty())
6418            return new double[0][0];
6419        GreasedRegion t = regions.get(0);
6420        int w = t.width, h = t.height, l = regions.size(), ys = t.ySections;
6421        double[][] numbers = new double[w][h];
6422        for (int x = 0; x < w; x++) {
6423            for (int y = 0; y < h; y++) {
6424                for (int i = 0; i < l; i++) {
6425                    numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1.0 : 0.0;
6426                }
6427            }
6428        }
6429        return numbers;
6430    }
6431
6432    /**
6433     * Generates a 2D int array from an array of GreasedRegions and an array of weights, starting the 2D result at all 0
6434     * and, for every GreasedRegion that has that cell as "on," adding the int in the corresponding weights array at
6435     * the position of that cell. This means if you give an array of 4 GreasedRegions to this method along with the
6436     * weights {@code 1, 2, 3, 4}, it can produce a number between 0 and 10 in a cell (where 10 is used when all 4
6437     * GreasedRegions have a cell "on," since {@code 1 + 2 + 3 + 4 == 10}); if the weights are instead
6438     * {@code 1, 10, 100, 1000}, then the results can vary between 0 and 1111, where 1111 is only if all GreasedRegions
6439     * have a cell as "on." The weights array must have a length at least equal to the length of the regions array.
6440     * @param regions an array of GreasedRegions; must all have the same width and height
6441     * @param weights an array of ints; must have length at least equal to regions' length
6442     * @return a 2D int array with the same width and height as the regions, where an int cell equals the sum of the weights corresponding to GreasedRegions that had an "on" cell at that position
6443     */
6444    public static int[][] sumWeighted(GreasedRegion[] regions, int[] weights)
6445    {
6446        if(regions == null || regions.length <= 0 || weights == null || weights.length < regions.length)
6447            return new int[0][0];
6448        int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections;
6449        int[][] numbers = new int[w][h];
6450        for (int x = 0; x < w; x++) {
6451            for (int y = 0; y < h; y++) {
6452                for (int i = 0; i < l; i++) {
6453                    numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? weights[i] : 0;
6454                }
6455            }
6456        }
6457        return numbers;
6458    }
6459
6460    /**
6461     * Generates a 2D double array from an array of GreasedRegions and an array of weights, starting the 2D result at
6462     * all 0 and, for every GreasedRegion that has that cell as "on," adding the double in the corresponding weights
6463     * array at the position of that cell. This means if you give an array of 4 GreasedRegions to this method along with
6464     * the weights {@code 1, 2, 3, 4}, it can produce a number between 0 and 10 in a cell (where 10 is used when all 4
6465     * GreasedRegions have a cell "on," since {@code 1 + 2 + 3 + 4 == 10}); if the weights are instead
6466     * {@code 1, 10, 100, 1000}, then the results can vary between 0 and 1111, where 1111 is only if all GreasedRegions
6467     * have a cell as "on." The weights array must have a length at least equal to the length of the regions array.
6468     * @param regions an array of GreasedRegions; must all have the same width and height
6469     * @param weights an array of doubles; must have length at least equal to regions' length
6470     * @return a 2D double array with the same width and height as the regions, where an double cell equals the sum of the weights corresponding to GreasedRegions that had an "on" cell at that position
6471     */
6472    public static double[][] sumWeightedDouble(GreasedRegion[] regions, double[] weights)
6473    {
6474        if(regions == null || regions.length <= 0 || weights == null || weights.length < regions.length)
6475            return new double[0][0];
6476        int w = regions[0].width, h = regions[0].height, l = regions.length, ys = regions[0].ySections;
6477        double[][] numbers = new double[w][h];
6478        for (int x = 0; x < w; x++) {
6479            for (int y = 0; y < h; y++) {
6480                for (int i = 0; i < l; i++) {
6481                    numbers[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? weights[i] : 0.0;
6482                }
6483            }
6484        }
6485        return numbers;
6486    }
6487
6488    /**
6489     * Adds to an existing 2D int array with an array or vararg of GreasedRegions, adding 1 to the int in existing at
6490     * a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to
6491     * this method, it can increment by any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can
6492     * increase the value in existing by any number between 0 and 16 in a cell.
6493     * @param existing a non-null 2D int array that will have each cell incremented by the sum of the GreasedRegions
6494     * @param regions an array or vararg of GreasedRegions; must all have the same width and height
6495     * @return existing, after modification, where an int cell will be changed by the number of given GreasedRegions that had an "on" cell at that position
6496     */
6497    public static int[][] sumInto(int[][] existing, GreasedRegion... regions)
6498    {
6499        if(regions == null || regions.length <= 0 || existing == null || existing.length == 0 || existing[0].length == 0)
6500            return existing;
6501        int w = existing.length, h = existing[0].length, l = regions.length, ys;
6502        for (int i = 0; i < l; i++) {
6503            GreasedRegion region = regions[i];
6504            ys = region.ySections;
6505            for (int x = 0; x < w && x < region.width; x++) {
6506                for (int y = 0; y < h && y < region.height; y++) {
6507                    existing[x][y] += (region.data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0;
6508                }
6509            }
6510        }
6511        return existing;
6512    }
6513
6514
6515    /**
6516     * Adds to an existing 2D double array with an array or vararg of GreasedRegions, adding 1 to the double in existing
6517     * at a position once for every GreasedRegion that has that cell as "on." This means if you give 8 GreasedRegions to
6518     * this method, it can increment by any number between 0 and 8 in a cell; if you give 16 GreasedRegions, then it can
6519     * increase the value in existing by any number between 0 and 16 in a cell.
6520     * @param existing a non-null 2D double array that will have each cell incremented by the sum of the GreasedRegions
6521     * @param regions an array or vararg of GreasedRegions; must all have the same width and height
6522     * @return existing, after modification, where a double cell will be changed by the number of given GreasedRegions that had an "on" cell at that position
6523     */
6524    public static double[][] sumIntoDouble(double[][] existing, GreasedRegion... regions)
6525    {
6526        if(regions == null || regions.length <= 0 || existing == null || existing.length == 0 || existing[0].length == 0)
6527            return existing;
6528        int w = existing.length, h = existing[0].length, l = regions.length, ys = regions[0].ySections;
6529        for (int i = 0; i < l; i++) {
6530            for (int x = 0; x < w && x < regions[i].width; x++) {
6531                for (int y = 0; y < h && y < regions[i].height; y++) {
6532                    existing[x][y] += (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1.0 : 0.0;
6533                }
6534            }
6535        }
6536        return existing;
6537    }
6538
6539    /**
6540     * Discouraged from active use; slower than {@link squidpony.squidai.DijkstraMap} and has less features.
6541     * @param map a 2D char array where '#' is a wall
6542     * @param goals an array or vararg of Coord to get the distances toward
6543     * @return a 2D double array of distances from a cell to the nearest goal
6544     */
6545    public static double[][] dijkstraScan(char[][] map, Coord... goals)
6546    {
6547        if(map == null || map.length <= 0 || map[0].length <= 0 || goals == null || goals.length <= 0)
6548            return new double[0][0];
6549        int w = map.length, h = map[0].length, ys = (h + 63) >>> 6;
6550        double[][] numbers = new double[w][h];
6551        GreasedRegion walls = new GreasedRegion(map, '#'), floors = new GreasedRegion(walls).not(),
6552                middle = new GreasedRegion(w, h, goals).and(floors);
6553        ArrayList<GreasedRegion> regions = middle.floodSeriesToLimit(floors);
6554        int l = regions.size();
6555        for (int x = 0; x < w; x++) {
6556            for (int y = 0; y < h; y++) {
6557                for (int i = 0; i < l; i++) {
6558                    numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0;
6559                }
6560            }
6561        }
6562        return numbers;
6563    }
6564    /**
6565     * Discouraged from active use; slower than {@link squidpony.squidai.DijkstraMap} and has less features.
6566     * @param map a 2D char array where '#' is a wall
6567     * @param goals an array or vararg of Coord to get the distances toward
6568     * @return a 2D double array of distances from a cell to the nearest goal
6569     */
6570    public static double[][] dijkstraScan8way(char[][] map, Coord... goals)
6571    {
6572        if(map == null || map.length <= 0 || map[0].length <= 0 || goals == null || goals.length <= 0)
6573            return new double[0][0];
6574        int w = map.length, h = map[0].length, ys = (h + 63) >>> 6;
6575        double[][] numbers = new double[w][h];
6576        GreasedRegion walls = new GreasedRegion(map, '#'), floors = new GreasedRegion(walls).not(),
6577                middle = new GreasedRegion(w, h, goals).and(floors);
6578        ArrayList<GreasedRegion> regions = middle.floodSeriesToLimit8way(floors);
6579        int l = regions.size();
6580        for (int x = 0; x < w; x++) {
6581            for (int y = 0; y < h; y++) {
6582                for (int i = 0; i < l; i++) {
6583                    numbers[x][y] += (regions.get(i).data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 : 0;
6584                }
6585            }
6586        }
6587        return numbers;
6588    }
6589
6590    /**
6591     * Generates a 2D int array from an array or vararg of GreasedRegions, treating each cell in the nth region as the
6592     * nth bit of the int at the corresponding x,y cell in the int array. This means if you give 8 GreasedRegions to
6593     * this method, it can produce any 8-bit number in a cell (0-255); if you give 16 GreasedRegions, then it can
6594     * produce any 16-bit number (0-65535).
6595     * @param regions an array or vararg of GreasedRegions; must all have the same width and height
6596     * @return a 2D int array with the same width and height as the regions, with bits per int taken from the regions
6597     */
6598    public static int[][] bitSum(GreasedRegion... regions)
6599    {
6600        if(regions == null || regions.length <= 0)
6601            return new int[0][0];
6602        int w = regions[0].width, h = regions[0].height, l = Math.min(32, regions.length), ys = regions[0].ySections;
6603        int[][] numbers = new int[w][h];
6604        for (int x = 0; x < w; x++) {
6605            for (int y = 0; y < h; y++) {
6606                for (int i = 0; i < l; i++) {
6607                    numbers[x][y] |= (regions[i].data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0 ? 1 << i : 0;
6608                }
6609            }
6610        }
6611        return numbers;
6612    }
6613
6614    /*
6615    public static int[][] selectiveNegate(int[][] numbers, GreasedRegion region, int mask)
6616    {
6617        if(region == null)
6618            return numbers;
6619        int w = region.width, h = region.height, ys = region.ySections;
6620        for (int x = 0; x < w; x++) {
6621            for (int y = 0; y < h; y++) {
6622                if((region.data[x * ys + (y >> 6)] & (1L << (y & 63))) != 0) numbers[x][y] = (~numbers[x][y] & mask);
6623            }
6624        }
6625        return numbers;
6626    }
6627    */
6628
6629    @Override
6630    public boolean equals(Object o) {
6631        if (this == o) return true;
6632        if (o == null || getClass() != o.getClass()) return false;
6633
6634        GreasedRegion that = (GreasedRegion) o;
6635
6636        if (height != that.height) return false;
6637        if (width != that.width) return false;
6638        if (ySections != that.ySections) return false;
6639        if (yEndMask != that.yEndMask) return false;
6640        return Arrays.equals(data, that.data);
6641    }
6642
6643    @Override
6644    public int hashCode() {
6645        return CrossHash.hash(data) ^ GWTRNG.determineInt(GWTRNG.determineInt(height) + width);
6646//        long result = 0x1A976FDF6BF60B8EL, z = 0x60642E2A34326F15L;
6647//        final int len = data.length;
6648//        for (int i = 0; i < len; i++) {
6649//            result ^= (z += (data[i] ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL);
6650//            result = (result << 54 | result >>> 10);
6651//        }
6652//        result ^= (z += (height ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL);
6653//        result = (result << 54 | result >>> 10);
6654//        result ^= (z += (width ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL);
6655//        result = (result << 54 | result >>> 10);
6656//
6657//        result += (z ^ z >>> 26) * 0x632BE59BD9B4E019L;
6658//        result = (result ^ result >>> 33) * 0xFF51AFD7ED558CCDL;
6659//        return (int)((result ^ result >>> 33) * 0xC4CEB9FE1A85EC53L);
6660    }
6661    public long hash64() {
6662        return CrossHash.hash64(data) ^ DiverRNG.randomize(DiverRNG.randomize(height) + width);
6663//
6664//        long result = 0x1A976FDF6BF60B8EL, z = 0x60642E2A34326F15L;
6665//        final int len = data.length;
6666//        for (int i = 0; i < len; i++) {
6667//            result ^= (z += (data[i] ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL);
6668//            result = (result << 54 | result >>> 10);
6669//        }
6670//        result ^= (z += (height ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL);
6671//        result = (result << 54 | result >>> 10);
6672//        result ^= (z += (width ^ 0xC6BC279692B5CC85L) * 0x6C8E9CF570932BABL);
6673//        result = (result << 54 | result >>> 10);
6674//        
6675//        result += (z ^ z >>> 26) * 0x632BE59BD9B4E019L;
6676//        result = (result ^ result >>> 33) * 0xFF51AFD7ED558CCDL;
6677//        return ((result ^ result >>> 33) * 0xC4CEB9FE1A85EC53L);
6678    }
6679
6680    /**
6681     * Computes a 64-bit hash code of this GreasedRegion given a 64-bit seed; even if given two very similar seeds,
6682     * this should produce very different hash codes for the same GreasedRegion.
6683     * <br>
6684     * Meant for potential use in Bloom filters. Uses {@link CrossHash.Yolk}'s algorithm(s).
6685     * @param seed a seed that will determine how the hashing algorithm works; all 64 bits are used.
6686     * @return a 64-bit hash code for this GreasedRegion
6687     */
6688    public long hash64(long seed)
6689    {
6690        seed += b1;
6691        seed ^= seed >>> 23 ^ seed >>> 48 ^ seed << 7 ^ seed << 53;
6692        long a = seed + b4, b = seed + b3, c = seed + b2, d = seed + b1;
6693        final int len = data.length;
6694        for (int i = 3; i < len; i+=4) {
6695            a ^= data[i-3] * b1; a = (a << 23 | a >>> 41) * b3;
6696            b ^= data[i-2] * b2; b = (b << 25 | b >>> 39) * b4;
6697            c ^= data[i-1] * b3; c = (c << 29 | c >>> 35) * b5;
6698            d ^= data[i  ] * b4; d = (d << 31 | d >>> 33) * b1;
6699            seed += a + b + c + d;
6700        }
6701        seed += b5;
6702        switch (len & 3) {
6703            case 1: seed = wow(seed, b1 ^ data[len-1]); break;
6704            case 2: seed = wow(seed + data[len-2], b2 + data[len-1]); break;
6705            case 3: seed = wow(seed + data[len-3], b2 + data[len-2]) ^ wow(seed + data[len-1], seed ^ b3); break;
6706        }
6707        seed = wow(seed + height, b4 + width);
6708        seed = (seed ^ seed << 16) * (len ^ b0 ^ seed >>> 32);
6709        return seed - (seed >>> 31) + (seed << 33);
6710
6711    }
6712
6713    public String serializeToString()
6714    {
6715        return width +
6716                "," + height +
6717                "," + StringKit.joinAlt(",",data);
6718    }
6719    public static GreasedRegion deserializeFromString(String s)
6720    {
6721        if(s == null || s.isEmpty())
6722            return null;
6723        int gap = s.indexOf(','), w = Integer.parseInt(s.substring(0, gap)),
6724                gap2 = s.indexOf(',', gap+1), h = Integer.parseInt(s.substring(gap+1, gap2));
6725        String[] splits = StringKit.split(s.substring(gap2+1), ",");
6726        long[] data = new long[splits.length];
6727        for (int i = 0; i < splits.length; i++) {
6728            data[i] = StringKit.longFromDec(splits[i]);
6729        }
6730        return new GreasedRegion(data, w, h);
6731    }
6732
6733    /**
6734     * Constructs a GreasedRegion using a vararg for data. Primarily meant for generated code, since
6735     * {@link #serializeToString()} produces a String that happens to be a valid parameter list for this method.
6736     * @param width width of the GreasedRegion to produce
6737     * @param height height of the GreasedRegion to produce
6738     * @param data array or vararg of long containing the exact data, probably from an existing GreasedRegion
6739     * @return a new GreasedRegion with the given width, height, and data
6740     */
6741    public static GreasedRegion of(final int width, final int height, final long... data)
6742    {
6743        return new GreasedRegion(data, width, height);
6744    }
6745    
6746    /**
6747     * Compresses this GreasedRegion into a UTF-16 String and returns the String without modifying this GreasedRegion.
6748     * Uses {@link CoordPacker}'s algorithm and data to compress this GreasedRegion in 256x128 blocks, storing the
6749     * CoordPacker-like data as chars with values from 256 to 33023 (a concept also used in {@link LZSEncoding}),
6750     * and using ASCII semicolons to separate them or store other info (just width and height, which are given first as
6751     * 16 hex digits). This finishes by running the result through {@link LZSEncoding}, a combination which typically
6752     * gets very good compression.
6753     * @return a String that could be used to reconstruct this GreasedRegion using {@link #decompress(String)}
6754     */
6755    public String toCompressedString()
6756    {
6757        CoordPacker.init();
6758        StringBuilder packing = new StringBuilder(width * height >> 3);
6759        StringKit.appendHex(packing, width);
6760        StringKit.appendHex(packing, height);
6761        final int chunksX = width + 255 >> 8, chunksY = height + 127 >> 7;
6762        for (int bigX = 0, baseX = 0; bigX < chunksX; bigX++, baseX += 256) {
6763            for (int bigY = 0, baseY = 0; bigY < chunksY; bigY++, baseY += 128) {
6764                packing.append(';');
6765                boolean on = false, current;
6766                short skip = 0, hx, hy;
6767                int xSize = Math.min(256, width - baseX), ySize = Math.min(128, height - baseY),
6768                        limit = 0x8000, mapLimit = xSize * ySize;
6769                if (xSize <= 128) {
6770                    limit >>= 1;
6771                    if (xSize <= 64) {
6772                        limit >>= 1;
6773                        if (ySize <= 64) {
6774                            limit >>= 1;
6775                            if (ySize <= 32) {
6776                                limit >>= 1;
6777                                if (xSize <= 32) {
6778                                    limit >>= 1;
6779                                }
6780                            }
6781                        }
6782                    }
6783                }
6784                for (int i = 0, ml = 0; i < limit && ml < mapLimit; i++, skip++) {
6785                    hx = CoordPacker.hilbertX[i];
6786                    hy = CoordPacker.hilbertY[i];
6787                    if (hx >= xSize || hy >= ySize) {
6788                        if (on) {
6789                            on = false;
6790                            packing.append((char) (skip + 256));
6791                            skip = 0;
6792                        }
6793                        continue;
6794                    }
6795                    ml++;
6796                    current = ((data[(baseX + hx) * ySections + (baseY + hy >> 6)] & (1L << hy)) != 0);
6797                    if (current != on) {
6798                        packing.append((char) (skip + 256));
6799                        skip = 0;
6800                        on = current;
6801                    }
6802                }
6803                if (on)
6804                    packing.append((char) (skip + 256));
6805            }
6806        }
6807        return LZSEncoding.compressToUTF16(packing.toString());
6808    }
6809
6810    /**
6811     * Decompresses a String returned by {@link #toCompressedString()}, returning a new GreasedRegion with identical
6812     * width, height, and contents to the GreasedRegion before compression. This decompresses the {@link LZSEncoding}
6813     * applied to the data, then decompresses the {@link CoordPacker}-type Hilbert Curve RLE data to get the original
6814     * GreasedRegion back.
6815     * @param compressed a String that was compressed by {@link #toCompressedString()}, without changes
6816     * @return a new copy of the GreasedRegion that was previously compressed
6817     */
6818    public static GreasedRegion decompress(String compressed)
6819    {
6820        CoordPacker.init();
6821        GreasedRegion target;
6822        compressed = LZSEncoding.decompressFromUTF16(compressed);
6823        final int width = StringKit.intFromHex(compressed), height = StringKit.intFromHex(compressed, 8, 16);
6824        target = new GreasedRegion(width, height);
6825        final int chunksX = width + 255 >> 8, chunksY = height + 127 >> 7;
6826        int startPack = 16, endPack, idx;//, hy;
6827        boolean on;
6828        for (int bigX = 0, baseX = 0; bigX < chunksX; bigX++, baseX += 256) {
6829            for (int bigY = 0, baseY = 0; bigY < chunksY; bigY++, baseY += 128) {
6830                ++startPack;
6831                endPack = compressed.indexOf(';', startPack);
6832                if(endPack < 0) endPack = compressed.length();
6833                on = false;
6834                idx = 0;
6835                for(int p = startPack; p < endPack; p++, on = !on) {
6836                    if (on) {
6837                        for (int toSkip = idx + (compressed.charAt(p) - 256); idx < toSkip && idx < 0x8000; idx++) {
6838                            target.insert(CoordPacker.hilbertX[idx] + baseX, CoordPacker.hilbertY[idx] + baseY);
6839                            //hy = CoordPacker.hilbertY[idx] + baseY;
6840                            //target.data[(CoordPacker.hilbertX[idx] + baseX) * target.ySections + (hy >> 6)] |= 1L << (hy & 63);
6841                        }
6842                    } else {
6843                        idx += compressed.charAt(p) - 256;
6844                    }
6845                }
6846                startPack = endPack;
6847            }
6848        }
6849//        target.tallied = false;
6850        return target;
6851    }
6852
6853    @Override
6854    public boolean contains(Object o) {
6855        if(o instanceof Coord)
6856            return contains((Coord)o);
6857        return false;
6858    }
6859
6860    @Override
6861    public Iterator<Coord> iterator() {
6862        return new GRIterator();
6863    }
6864
6865    @Override
6866    public Object[] toArray() {
6867        return asCoords();
6868    }
6869
6870    @SuppressWarnings("unchecked")
6871    @Override
6872    public <T> T[] toArray(T[] a) {
6873        if(a instanceof Coord[])
6874            return (T[])asCoords((Coord[])a);
6875        return a;
6876    }
6877
6878    @Override
6879    public boolean add(Coord coord) {
6880        if(contains(coord))
6881            return false;
6882        insert(coord);
6883        return true;
6884    }
6885    @Override
6886    public void clear()
6887    {
6888        Arrays.fill(data, 0L);
6889    }
6890
6891    @Override
6892    public boolean remove(Object o) {
6893        if(o instanceof Coord)
6894        {
6895            if(contains((Coord)o))
6896            {
6897                remove((Coord)o);
6898                return true;
6899            }
6900            return false;
6901        }
6902        return false;
6903    }
6904
6905    @Override
6906    public boolean containsAll(Collection<?> c) {
6907        for(Object o : c)
6908        {
6909            if(!contains(o))
6910                return false;
6911        }
6912        return true;
6913    }
6914
6915    @Override
6916    public boolean addAll(Collection<? extends Coord> c) {
6917        boolean changed = false;
6918        for(Coord co : c)
6919        {
6920            changed |= add(co);
6921        }
6922        return changed;
6923    }
6924
6925    @Override
6926    public boolean removeAll(Collection<?> c) {
6927        boolean changed = false;
6928        for(Object o : c)
6929        {
6930            changed |= remove(o);
6931        }
6932        return changed;
6933    }
6934
6935    @Override
6936    public boolean retainAll(Collection<?> c) {
6937        GreasedRegion g2 = new GreasedRegion(width, height);
6938        for(Object o : c)
6939        {
6940            if(contains(o) && o instanceof Coord)
6941            {
6942                g2.add((Coord)o);
6943            }
6944        }
6945        boolean changed = equals(g2);
6946        remake(g2);
6947        return changed;
6948    }
6949
6950    /**
6951     * Randomly removes points from a GreasedRegion, with larger values for preservation keeping more of the existing
6952     * shape intact. If preservation is 1, roughly 1/2 of all points will be removed; if 2, roughly 1/4, if 3, roughly
6953     * 1/8, and so on, so that preservation can be thought of as a negative exponent of 2.
6954     * @param rng used to determine random factors
6955     * @param preservation roughly what degree of points to remove (higher keeps more); removes about {@code 1/(2^preservation)} points
6956     * @return a randomly modified change to this GreasedRegion
6957     */
6958    public GreasedRegion deteriorate(RandomnessSource rng, int preservation) {
6959        if(rng == null || width <= 2 || ySections <= 0 || preservation <= 0)
6960            return this;
6961        long mash;
6962        for (int i = 0; i < width * ySections; i++) {
6963            mash = rng.nextLong();
6964            for (int j = i; j < preservation; j++) {
6965                mash |= rng.nextLong();
6966            }
6967            data[i] &= mash;
6968        }
6969        tallied = false;
6970        return this;
6971    }
6972
6973    /**
6974     * Randomly removes points from a GreasedRegion, with preservation as a fraction between 1.0 (keep all) and 0.0
6975     * (remove all). If preservation is 0.5, roughly 1/2 of all points will be removed; if 0.25, roughly 3/4 will be
6976     * removed (roughly 0.25 will be _kept_), if 0.8, roughly 1/5 will be removed (and about 0.8 will be kept), and so
6977     * on. Preservation must be between 0.0 and 1.0 for this to have the intended behavior; 1.0 or higher will keep all
6978     * points without change (returning this GreasedRegion), while anything less than 0.015625 (1.0/64) will empty this
6979     * GreasedRegion (using {@link #empty()}) and then return it. The parameter {@code random} can be an object like a
6980     * {@link DiverRNG}, an {@link RNG} backed by a well-distributed RandomnessSource like its default, DiverRNG, a
6981     * {@link GWTRNG} (especially if you target GWT, where it will perform much better than most alternatives), or any
6982     * of various other RandomnessSource implementations that distribute bits well for
6983     * {@link RandomnessSource#nextLong()}, but should not be intentionally-biased RNGs like {@link DharmaRNG} or 
6984     * {@link EditRNG}, nor double-based QRNGs like {@link VanDerCorputQRNG} or {@link SobolQRNG}.
6985     * @param random used to determine random factors; likely to be an {@link RNG}, {@link DiverRNG}, or {@link GWTRNG}
6986     * @param preservation the rough fraction of points to keep, between 0.0 and 1.0
6987     * @return a randomly modified change to this GreasedRegion
6988     */
6989    public GreasedRegion deteriorate(final RandomnessSource random, final double preservation) {
6990        if(random == null || width <= 2 || ySections <= 0 || preservation >= 1)
6991            return this;
6992        if(preservation <= 0)
6993            return empty();
6994        int bitCount = (int) (preservation * 64);
6995        for (int i = 0; i < width * ySections; i++) {
6996            data[i] &= approximateBits(random, bitCount);
6997        }
6998        tallied = false;
6999        return this;
7000    }
7001
7002    /**
7003     * Changes the on/off state of the cell with the given x and y, making an on cell into an off cell, or an off cell
7004     * into an on cell.
7005     * <br>
7006     * This was called flip(), but that name would be confusing since flipping a rectangular area usually means
7007     * reversing an axis.
7008     * @param x the x position of the cell to flip
7009     * @param y the y position of the cell to flip
7010     * @return this for chaining, modified
7011     */
7012    public GreasedRegion toggle(int x, int y) {
7013        if(x >= 0 && y >= 0 && x < width && y < height && ySections > 0)
7014        {
7015            data[x * ySections + (y >> 6)] ^= (1L << (y & 63));
7016            tallied = false;
7017        }
7018        return this;
7019
7020    }
7021
7022    /**
7023     * Returns a new GreasedRegion that has been mirrored along the rightmost edge, parallel to the y-axis. The new
7024     * GreasedRegion will have exactly twice the width, the additional width will have the contents of the original
7025     * GreasesRegion in reversed order. The positions shared by both GreasedRegions will be the same, that is, any area
7026     * not added to the original will be equal to the original.
7027     * @return a new GreasedRegion with twice the width of {@code this}, that is mirrored along the rightmost edge
7028     */
7029    public GreasedRegion mirrorY()
7030    {
7031        GreasedRegion next = new GreasedRegion(data, width, height, width * 2, height);
7032        for (int i = 0, o = width * 2 - 1; i < width; i++, o--) {
7033            System.arraycopy(data, ySections * i, next.data, ySections * o, ySections);
7034        }
7035        return next;
7036    }
7037
7038    @Override
7039    public boolean intersectsWith(Zone other) {
7040        if (other instanceof GreasedRegion)
7041            return intersects((GreasedRegion) other);
7042        long t, w;
7043        for (int x = 0; x < width; x++) {
7044            for (int s = 0; s < ySections; s++) {
7045                if ((t = data[x * ySections + s]) != 0) {
7046                    w = NumberTools.lowestOneBit(t);
7047                    while (w != 0) {
7048                        if (other.contains(x, (s << 6) | Long.numberOfTrailingZeros(w)))
7049                            return true;
7050                        t ^= w;
7051                        w = NumberTools.lowestOneBit(t);
7052                    }
7053                }
7054            }
7055        }
7056        return false;
7057    }
7058    /**
7059     * Translates a copy of {@code this} by the x,y values in {@code c}.
7060     * Implemented with {@code return copy().translate(c.x, c.y);}
7061     * @return {@code this} copied and shifted by {@code (c.x,c.y)}
7062     */
7063    @Override
7064    public GreasedRegion translate(Coord c) {
7065        return copy().translate(c.x, c.y);
7066    }
7067    /**
7068     * Gets a Collection of Coord values that are not in this GreasedRegion, but are
7069     * adjacent to it, either orthogonally or diagonally. Related to the fringe()
7070     * methods in CoordPacker and GreasedRegion, but guaranteed to use 8-way
7071     * adjacency and to return a new Collection of Coord. This implementation returns
7072     * a GreasedRegion produced simply by {@code return copy().fringe8way();} .
7073     * @return Cells adjacent to {@code this} (orthogonally or diagonally) that
7074     * aren't in {@code this}
7075     */
7076    @Override
7077    public GreasedRegion getExternalBorder() {
7078        return copy().fringe8way();
7079    }
7080
7081    /**
7082     * Gets a new Zone that contains all the Coords in {@code this} plus all
7083     * neighboring Coords, which can be orthogonally or diagonally adjacent
7084     * to any Coord this has in it. Related to the expand() methods in
7085     * CoordPacker and GreasedRegion, but guaranteed to use 8-way adjacency
7086     * and to return a new Zone. This implementation returns a GreasedRegion
7087     * produced simply by {@code return copy().expand8way();} .
7088     * @return A new GreasedRegion where "off" cells adjacent to {@code this}
7089     *         (orthogonally or diagonally) have been added to the "on" cells
7090     *         in {@code this}
7091     */
7092    @Override
7093    public GreasedRegion extend() {
7094        return copy().expand8way();
7095    }
7096
7097    /**
7098     * Checks if {@code c} is present in this GreasedRegion. Returns true if and only if c is present in this
7099     * GreasedRegion as an "on" cell. This will never be true if c is null, has negative x or y, has a value for x that
7100     * is equal to or greater than {@link #width}, or has a value for y that is equal to or greater than
7101     * {@link #height}, but none of those conditions will cause Exceptions to be thrown.
7102     * @param c a Coord to try to find in this GreasedRegion; if null this will always return false
7103     * @return true if {@code c} is an "on" cell in this GreasedRegion, or false otherwise, including if c is null
7104     */
7105    @Override
7106    public boolean contains(Coord c) {
7107        return c != null && contains(c.x, c.y);
7108    }
7109
7110    /**
7111     * Checks whether all Coords in {@code other} are also present in {@code this}.
7112     * Requires that {@code other} won't give a null Coord while this method iterates over it.
7113     * @param other another Zone, such as a GreasedRegion or a {@link squidpony.squidgrid.zone.CoordPackerZone}
7114     * @return true if all Coords in other are "on" in this GreasedRegion, or false otherwise
7115     */
7116    @Override
7117    public boolean contains(Zone other) {
7118        if(other instanceof Collection)
7119            return containsAll((Collection) other);
7120        for(Coord c : other)
7121        {
7122            if(!contains(c.x, c.y))
7123                return false;
7124        }
7125        return true;
7126    }
7127
7128    /**
7129     * @param findSmallest if true, finds the smallest x-coordinate value;
7130     *                          if false, finds the biggest.
7131     * @return The x-coordinate of the Coord within {@code this} that has the
7132     * smallest (or biggest) x-coordinate. Or -1 if the zone is empty.
7133     */
7134    @Override
7135    public int xBound(boolean findSmallest) {
7136        if(findSmallest)
7137            return first().x;
7138        else
7139            return last().x;
7140    }
7141
7142    /**
7143     * @param findSmallest if true, finds the smallest y-coordinate value;
7144     *                          if false, finds the biggest.
7145     * @return The y-coordinate of the Coord within {@code this} that has the
7146     * smallest (or biggest) y-coordinate. Or -1 if the zone is empty.
7147     */
7148    @Override
7149    public int yBound(boolean findSmallest) {
7150        long t = 0L;
7151        if(findSmallest) {
7152            for (int s = 0; s < ySections; s++) {
7153                for (int x = 0; x < width; x++) {
7154                    t |= data[x * ySections + s];
7155                }
7156                if(t != 0L)
7157                    return s << 6 | Long.numberOfTrailingZeros(t);
7158            }
7159            return -1;
7160        }
7161        else
7162        {
7163            for (int s = ySections - 1; s >= 0; s--) {
7164                for (int x = 0; x < width; x++) {
7165                    t |= data[x * ySections + s];
7166                }
7167                if(t != 0L)
7168                    return s << 6 | 63 - Long.numberOfLeadingZeros(t);
7169            }
7170            return -1;
7171        }
7172    }
7173
7174    /**
7175     * Gets the distance between the minimum x-value contained in this GreasedRegion and the maximum x-value in it.
7176     * Not the same as accessing the field {@link #width} on a GreasedRegion! The field will get the span of the space
7177     * that the GreasedRegion can use, including "on" and "off" cells. This method will only get the distance between
7178     * the furthest-separated "on" cells on the x-axis, and won't consider "off" cells. This method can return -1 if the
7179     * GreasedRegion is empty, 0 if the "on" cells are all in a vertical line (that is, when the minimum x is equal to
7180     * the maximum x), or a positive int in other cases with multiple x-values.
7181     * @return the distance on the x-axis between the "on" cell with the lowest x-value and the one with the highest
7182     */
7183    @Override
7184    public int getWidth() {
7185        if (super.width == -2)
7186            super.width = isEmpty() ? -1 : xBound(false) - xBound(true);
7187        return super.width;
7188    }
7189    /**
7190     * Gets the distance between the minimum y-value contained in this GreasedRegion and the maximum y-value in it.
7191     * Not the same as accessing the field {@link #height} on a GreasedRegion! The field will get the span of the space
7192     * that the GreasedRegion can use, including "on" and "off" cells. This method will only get the distance between
7193     * the furthest-separated "on" cells on the y-axis, and won't consider "off" cells. This method can return -1 if the
7194     * GreasedRegion is empty, 0 if the "on" cells are all in a horizontal line (that is, when the minimum y is equal to
7195     * the maximum y), or a positive int in other cases with multiple y-values.
7196     * @return the distance on the y-axis between the "on" cell with the lowest y-value and the one with the highest
7197     */
7198    @Override
7199    public int getHeight() {
7200        if (super.height == -2)
7201            super.height = isEmpty() ? -1 : yBound(false) - yBound(true);
7202        return super.height;
7203    }
7204
7205    /**
7206     * Gets the diagonal distance from the point combining the lowest x-value present in this GreasedRegion with the
7207     * lowest y-value in this, to the point combining the highest x-value and the highest y-value. These minimum and
7208     * maximum values don't necessarily match a single "on" cell for each min and max corner, and can take their x and y
7209     * values from two different points. The diagonal distance uses Euclidean measurement (basic Pythagorean Theorem
7210     * math here), and will be a double.
7211     * @return the diagonal distance from (min x, min y) to (max x, max y), as a double
7212     */
7213    @Override
7214    public double getDiagonal() {
7215        final int w = getWidth();
7216        final int h = getHeight();
7217        return Math.sqrt((w * w) + (h * h));
7218    }
7219//////Not duplicated because the superclass does this just fine.
7220//    @Override
7221//    public Coord getCenter() {
7222//        return super.getCenter();
7223//    }
7224
7225    @Override
7226    public GreasedRegion getInternalBorder() {
7227        return copy().surface8way();
7228    }
7229
7230    /**
7231     * Gets a Coord array from the "on" contents of this GreasedRegion, using a deterministic but random-seeming
7232     * scattering of chosen cells with a count that matches the given {@code fraction} of the total amount of "on" cells
7233     * in this. This is pseudo-random with the given seed (which will be made into an odd number if it is not one
7234     * already), and is very good at avoiding overlap (just as good as {@link #separatedZCurve(double, int)}, and
7235     * much faster). If you request too many cells (too high of a value for fraction), it will start to overlap, but
7236     * a fraction value of 0.4 reliably has had no overlap in testing. Restricts the total size of the returned array to
7237     * a maximum of {@code limit} (minimum is 0 if no cells are "on"). If limit is negative, this will not restrict the
7238     * size.
7239     * @param fraction the fraction of "on" cells to randomly select, between 0.0 and 1.0
7240     * @param limit the maximum size of the array to return
7241     * @param seed a long seed to change the points; the most significant 21 bits (except the sign bit) and least significant bit are ignored
7242     * @return a freshly-allocated Coord array containing the pseudo-random cells
7243     */
7244    public Coord[] mixedRandomSeparatedAlt(double fraction, int limit, long seed)
7245    {
7246        if(fraction < 0)
7247            return new Coord[0];
7248        if(fraction > 1)
7249            fraction = 1;
7250        int tmp, ic;
7251        long t, w;
7252        seed |= 1L;
7253        final int total = size();
7254        int ct = (int)(total * fraction);
7255        if(limit >= 0 && limit < ct)
7256            ct = limit;
7257        Coord[] vl = new Coord[ct];
7258        EACH_QUASI:
7259        for (int i = 0; i < ct; i++)
7260        {
7261            tmp = (int)(VanDerCorputQRNG.altDetermine(seed, i+1) * total);
7262
7263            for (int s = 0; s < ySections; s++) {
7264                for (int x = 0; x < width; x++) {
7265                    if ((ic = counts[x * ySections + s]) > tmp) {
7266                        t = data[x * ySections + s];
7267                        for (--ic; t != 0; ic--) {
7268                            w = NumberTools.lowestOneBit(t);
7269                            if (ic == tmp)
7270                            {
7271                                vl[i] = Coord.get(x, (s << 6) | Long.bitCount(w-1));
7272                                continue EACH_QUASI;
7273                            }
7274                            t ^= w;
7275                        }
7276                    }
7277                }
7278            }
7279        }
7280        return vl;
7281    }
7282
7283
7284    public class GRIterator implements Iterator<Coord>
7285    {
7286        public int index;
7287        private long t, w;
7288        public GRIterator()
7289        {
7290            if(!tallied)
7291                tally();
7292        }
7293        @Override
7294        public boolean hasNext() {
7295            return index < ct;
7296        }
7297
7298        @Override
7299        public Coord next() {
7300            int c;
7301            if(index >= ct)
7302                return null;
7303            for (int s = 0; s < ySections; s++) {
7304                for (int x = 0; x < width; x++) {
7305                    if ((c = counts[x * ySections + s]) > index) {
7306                        t = data[x * ySections + s] | 0L;
7307                        for (--c; t != 0; c--) {
7308                            w = NumberTools.lowestOneBit(t);
7309                            if (c == index)
7310                            {
7311                                index++;
7312                                return Coord.get(x, (s << 6) | Long.bitCount(w-1));
7313                            }
7314                            t ^= w;
7315                        }
7316                    }
7317                }
7318            }
7319            return null;
7320
7321            /*
7322            for (int x = 0; x < width; x++) {
7323                for (int s = 0; s < ySections; s++) {
7324                    if ((w = NumberTools.lowestOneBit(data[x * ySections + s])) != 0 && i++ >= index) {
7325                        if(index++ < limit)
7326                            return Coord.get(x, (s << 6) | Long.numberOfTrailingZeros(w));
7327                        else
7328                            return null;
7329                    }
7330                }
7331            }
7332            */
7333        }
7334
7335        @Override
7336        public void remove() {
7337            throw new UnsupportedOperationException("remove() is not supported on this Iterator.");
7338        }
7339    }
7340}