slmake

For scripts to aid with computation or simulation in cellular automata.
User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

slmake

Post by calcyman » July 6th, 2022, 6:23 am

Apparently there wasn't a thread for slmake discussion, so I've created one.

After more than a year of inactivity, there have been several major improvements over the last few days:
  • Firstly, and most importantly, slmake is considerably faster, especially when building large, dense constructions. It has no difficulty building the ECCA (4943 cells), completing both the construction and the defragmentation within 3 hours.
  • A diagonal sweep-line sort order is used, so it prefers to construct objects closest to the source of slow gliders (which will be most accessible), thereby avoiding wasting time attempting to construct inaccessible objects. (The exception to this is that bespoke objects are prioritised above everything else; maybe I should try disabling that?)
  • A recursive approach is used, where slmake dynamically identifies subproblems during construction and restricts attention to those. This both improves performance and means that less defragmentation is required at the end. Subproblems correspond to an obelisk-shaped convex region, with thick walls of empty space and slightly tapered boundaries to help ensure that the construction does not interfere with objects outside the subproblem.
  • slmake now saves a progress file after every step. This will be called currentN.mc where N is the recursion depth (0 if it's handling the main problem, 1 for a subproblem, 2 for a subsubproblem, and so forth).
  • In particular, at the end of construction there will be the raw non-defragmented slow-salvo recipe in current0.mc. (This only includes the construction, not the movement of the initial hand block.)
The instructions in Dave's excellent LifeWiki tutorial still apply.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » November 26th, 2022, 8:34 am

As of the latest commit, some new features have been added to slmake:
  • The slow salvo is saved to defragmented.mc, which is useful if you want the slow salvo but not the single-channel construction.
  • For slow-salvo constructions, the base period has been increased from p2 to p8, with added bespoke syntheses for the Blocker and Figure 8 oscillators. In particular, it can build both the p8 bouncer and p8 bumper in any orientation. [Note; period-8 support currently only applies to slow-salvo syntheses (defragmented.mc) rather than the single-channel construction (outfile.mc).] This means, for example, that Pavgran's DBCA can be built by slmake in a single pass.
  • Bespoke syntheses have been generalised so that they can begin with an arbitrary seed rather than a block. This means that they're more usable in tighter situations, and in particular slmake can now construct a minimal-length non-welded Snark loop.
  • Further speedups have been implemented, largely from parameter tweaks.
  • The heuristic has been changed slightly so that it prefers using the strategy split rather than reduce where possible. This should result in more efficient slow-salvo constructions.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
pipsqueek
Posts: 265
Joined: September 10th, 2022, 4:42 pm

Re: slmake

Post by pipsqueek » January 1st, 2023, 3:46 pm

This thread is very dry, but I'm having trouble compiling slmake. I'm on Mac and if I try to run slsparse.cpp, I get this error:

Code: Select all

Me@My-computer ~ % ./slsparse.cpp
(normal output)
Compiling...
clang: error: the clang compiler does not support '-march=native'
...compiled.
./slsparse.cpp: line 10: : command not found
Me@My-computer slmake % 
I tried use x86_64 emulation, that solved the problem. but it causes another error.

Code: Select all

Me@My-computer slmake % arch -x86_64 ./slsparse.cpp
Ensuring lifelib is up-to-date...
Compiling...
ld: can't open output file for writing: , errno=2 for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
...compiled.
./slsparse.cpp: line 10: : command not found

How can I fix this issue? (I'm on MacOS M1)

Code: Select all

x=17,y=16,rule=B3/S23
3bo3bobo2bob2o$bobo4bo4b4o$bobo5bobo2b3o$b2obob2o3b2o$3o4b2ob2o2b2o$4b
o4bo$4b2obobob2ob3o$3ob3o2b2o$b3o2bobobo5bo$o3b2o3bobo2b2o$4bo3bob2o3b
o$2obo2bobobo2b2o$3b3o5bo2b2o$2obo4bo2bob2o$o3bob2obo3b2o$2bo8bobobo![[ STOP 3 GPS 4 ]]

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » January 1st, 2023, 6:18 pm

pipsqueek wrote:
January 1st, 2023, 3:46 pm
This thread is very dry, but I'm having trouble compiling slmake. I'm on Mac and if I try to run slsparse.cpp, I get this error:

Code: Select all

Me@My-computer ~ % ./slsparse.cpp
(normal output)
Compiling...
clang: error: the clang compiler does not support '-march=native'
...compiled.
./slsparse.cpp: line 10: : command not found
Me@My-computer slmake % 
I tried use x86_64 emulation, that solved the problem. but it causes another error.

Code: Select all

Me@My-computer slmake % arch -x86_64 ./slsparse.cpp
Ensuring lifelib is up-to-date...
Compiling...
ld: can't open output file for writing: , errno=2 for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
...compiled.
./slsparse.cpp: line 10: : command not found

How can I fix this issue? (I'm on MacOS M1)
That looks like it's trying to write to an empty file name, probably because you don't have readlink installed. Just try:

Code: Select all

arch -x86_64 g++ -std=c++11 -O3 -march=native -pedantic -Wall -Wextra "slsparse.cpp" -o "slsparse" -g
and then run ./slsparse instead of ./slsparse.cpp
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
pipsqueek
Posts: 265
Joined: September 10th, 2022, 4:42 pm

Re: slmake

Post by pipsqueek » January 2nd, 2023, 6:32 pm

I think I found a bug. I tried to make a snark with this pattern infile.mc:

Code: Select all

x = 28, y = 22, rule = B3/S23
3b2o$4bo$2bo$2b5o14b2o$7bo13bo$4b3o12bobo$3bo15b2o$3b4o$b2o3bo3b2o$o2b
3o4b2o$2obo$3bo$3b2o3$11b2o$12bo$9b3o13bo$9bo11b2obobo$21b2obo2bo$25bo
bo$26bo!
towards the end of the recipe, it breaks the snark because it tries to push the block back but it forgot about the snark:

Code: Select all

x = 2894, y = 2888, rule = B3/S23
3b2o$4bo$2bo$2b5o14b2o$7bo13bo$4b3o12bobo$3bo15b2o$3b4o$b2o3bo3b2o$o2b
3o4b2o$2obo$3bo$3b2o3$11b2o$12bo$9b3o$9bo73$119b2o$119b2o6$117b2o$115b
2ob2o$114bo3bo$118bo$112bo4bo$111bo6bo7bo$111bobo3b3o6bo$110b3o2b2ob2o
bo$111bo7bo2bo$112bo7b3o4$129b2o$128bo2bo$129b2o$114bo$114bo$114bo14$
136b3o$136bo$137bo21$160b2o$159b2o$161bo21$182b3o$182bo$183bo20$205b2o
$205bobo$205bo22$229b2o$228b2o$230bo21$251b3o$251bo$252bo20$275bo$274b
2o$274bobo33$309b2o$308b2o$310bo37$348b2o$348bobo$348bo22$372b2o$372bo
bo$372bo21$394b3o$394bo$395bo28$424b3o$424bo$425bo20$448bo$447b2o$447b
obo23$471b3o$471bo$472bo24$499bo$498b2o$498bobo21$521b2o$520b2o$522bo
20$544bo$543b2o$543bobo22$566b3o$566bo$567bo45$613b3o$613bo$614bo25$
641b2o$640b2o$642bo20$664bo$663b2o$663bobo22$686b3o$686bo$687bo20$710b
o$709b2o$709bobo21$732b2o$731b2o$733bo22$755b3o$755bo$756bo20$779bo$
778b2o$778bobo21$801b2o$801bobo$801bo33$835b3o$835bo$836bo37$875b2o$
874b2o$876bo22$899b2o$898b2o$900bo20$922bo$921b2o$921bobo28$952bo$951b
2o$951bobo21$974b2o$974bobo$974bo22$999bo$998b2o$998bobo25$1025b2o$
1025bobo$1025bo21$1047b3o$1047bo$1048bo20$1070b2o$1070bobo$1070bo21$
1094bo$1093b2o$1093bobo45$1141bo$1140b2o$1140bobo26$1167b3o$1167bo$
1168bo20$1190b2o$1190bobo$1190bo21$1214bo$1213b2o$1213bobo21$1236b2o$
1236bobo$1236bo21$1258b3o$1258bo$1259bo21$1283bo$1282b2o$1282bobo21$
1305b2o$1305bobo$1305bo21$1328b2o$1327b2o$1329bo32$1363bo$1362b2o$
1362bobo38$1401b3o$1401bo$1402bo22$1425b3o$1425bo$1426bo20$1448b2o$
1448bobo$1448bo28$1478b2o$1478bobo$1478bo21$1501b2o$1500b2o$1502bo22$
1525b2o$1525bobo$1525bo25$1552b2o$1551b2o$1553bo20$1575bo$1574b2o$
1574bobo21$1597b2o$1596b2o$1598bo21$1620b2o$1620bobo$1620bo45$1667b2o$
1667bobo$1667bo25$1695bo$1694b2o$1694bobo21$1717b2o$1716b2o$1718bo21$
1740b2o$1740bobo$1740bo21$1763b2o$1762b2o$1764bo20$1786bo$1785b2o$
1785bobo22$1809b2o$1809bobo$1809bo21$1832b2o$1831b2o$1833bo21$1854b3o$
1854bo$1855bo32$1889b2o$1889bobo$1889bo37$1929bo$1928b2o$1928bobo22$
1953bo$1952b2o$1952bobo21$1975b2o$1974b2o$1976bo28$2005b2o$2004b2o$
2006bo21$2027b3o$2027bo$2028bo22$2052b2o$2051b2o$2053bo25$2078b3o$
2078bo$2079bo20$2101b2o$2101bobo$2101bo21$2123b3o$2123bo$2124bo21$
2147b2o$2146b2o$2148bo45$2194b2o$2193b2o$2195bo25$2221b2o$2221bobo$
2221bo21$2243b3o$2243bo$2244bo21$2267b2o$2266b2o$2268bo21$2289b3o$
2289bo$2290bo20$2313bo$2312b2o$2312bobo21$2335b2o$2334b2o$2336bo20$
2358bo$2357b2o$2357bobo21$2381bo$2380b2o$2380bobo21$2403b2o$2402b2o$
2404bo22$2426b3o$2426bo$2427bo20$2450bo$2449b2o$2449bobo41$2492b2o$
2491b2o$2493bo20$2515bo$2514b2o$2514bobo21$2537b2o$2536b2o$2538bo21$
2559b3o$2559bo$2560bo20$2583bo$2582b2o$2582bobo23$2607b2o$2606b2o$
2608bo21$2629b3o$2629bo$2630bo20$2653bo$2652b2o$2652bobo21$2675b2o$
2674b2o$2676bo25$2702b2o$2702bobo$2702bo21$2725b2o$2724b2o$2726bo21$
2749bo$2748b2o$2748bobo21$2771b2o$2771bobo$2771bo21$2794b2o$2793b2o$
2795bo21$2817b2o$2816b2o$2818bo20$2840bo$2839b2o$2839bobo28$2869b2o$
2869bobo$2869bo21$2891b3o$2891bo$2892bo!
this can be fixed by putting the block farther away, but I don't think its supposed to do that

Code: Select all

x=17,y=16,rule=B3/S23
3bo3bobo2bob2o$bobo4bo4b4o$bobo5bobo2b3o$b2obob2o3b2o$3o4b2ob2o2b2o$4b
o4bo$4b2obobob2ob3o$3ob3o2b2o$b3o2bobobo5bo$o3b2o3bobo2b2o$4bo3bob2o3b
o$2obo2bobobo2b2o$3b3o5bo2b2o$2obo4bo2bob2o$o3bob2obo3b2o$2bo8bobobo![[ STOP 3 GPS 4 ]]

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: slmake

Post by dvgrn » January 2nd, 2023, 6:47 pm

pipsqueek wrote:
January 2nd, 2023, 6:32 pm
I tried to make a snark with this pattern infile.mc:

Code: Select all

x = 28, y = 22, rule = B3/S23
3b2o$4bo$2bo$2b5o14b2o$7bo13bo$4b3o12bobo$3bo15b2o$3b4o$b2o3bo3b2o$o2b
3o4b2o$2obo$3bo$3b2o3$11b2o$12bo$9b3o13bo$9bo11b2obobo$21b2obo2bo$25bo
bo$26bo!
Yeah, slmake is designed to produce slow salvo recipes that can come from infinity, and it's assumed that that's effectively what they do. There's no error-checking code in slsparse that even tries to worry about whether the single-channel recipe will produce explosions that modify the constructed object, at any stage of the construction.

It's not easy to fix this, or even to define clearly what slmake's expected behavior should be in this case. You might say the expected behavior is that the user should move the block-and-mango to a safe distance and run slsparse again. So this is not so much a bug as a "known limitation that most likely will never be removed, unless you fix it yourself and contribute the patch to slmake".

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » January 8th, 2023, 4:18 pm

I've added a new edgy synthesis for the eater-bridge-eater still-life, which works in two orientations:

Code: Select all

x = 105, y = 28, rule = B3/S23
9bo85bo$8bobo83bobo$7bo2bo2b2o75b2o2bo2bo$8b2o3b2o75b2o3b2o2$31b2o39b
2o$31b2o39b2o$2o101b2o$2o101b2o2$20bo59bo$20bo59bo$20bo59bo3$22bo59bo$
22bo59bo$22bo59bo8$41bo59bo$40b2o58b2o$40bobo57bobo!
There's also a one-glider seed for Coe's p8. Together with the new eater-bridge-eater syntheses, we can now construct Kazyan's phase-agnostic p8 glider reflector in all orientations. Here's a demonstration (building a p624 eight-barrelled glider gun), which I would have tightened up were it not for the fact that Dave's recipe minimiser doesn't work with p8 ash intermediates:

Code: Select all

x = 106953, y = 214937, rule = B3/S23
2o$2o26$32b2o$31b2o$33bo125$162b2o$161b2o$163bo126$282b2o$281b2o$283bo
126$416b2o$415b2o$417bo127$547b3o$547bo$548bo126$678b2o$677b2o$679bo
127$813b3o$813bo$814bo125$949b3o$949bo$950bo126$1076b3o$1076bo$1077bo
125$1217bo$1216b2o$1216bobo127$1352b3o$1352bo$1353bo126$1479b3o$1479bo
$1480bo125$1620bo$1619b2o$1619bobo127$1755b3o$1755bo$1756bo126$1882b3o
$1882bo$1883bo125$2023bo$2022b2o$2022bobo127$2158b3o$2158bo$2159bo126$
2275b3o$2275bo$2276bo126$2408b3o$2408bo$2409bo125$2518bo$2517b2o$2517b
obo126$2627bo$2626b2o$2626bobo126$2736bo$2735b2o$2735bobo127$2846b2o$
2846bobo$2846bo126$2974b2o$2974bobo$2974bo126$3090b2o$3089b2o$3091bo
126$3220b2o$3220bobo$3220bo125$3356b2o$3356bobo$3356bo127$3466b2o$
3466bobo$3466bo126$3603b2o$3603bobo$3603bo125$3733b2o$3733bobo$3733bo
126$3861b2o$3861bobo$3861bo126$3999b2o$3998b2o$4000bo126$4117b2o$4117b
obo$4117bo127$4235b2o$4235bobo$4235bo125$4365b2o$4365bobo$4365bo126$
4489b2o$4489bobo$4489bo126$4619b2o$4619bobo$4619bo127$4739b2o$4738b2o$
4740bo125$4861b2o$4861bobo$4861bo127$5013b2o$5013bobo$5013bo125$5124b
2o$5123b2o$5125bo127$5248b2o$5247b2o$5249bo126$5368b2o$5367b2o$5369bo
125$5524b2o$5523b2o$5525bo126$5644b2o$5643b2o$5645bo128$5751b3o$5751bo
$5752bo125$5889b3o$5889bo$5890bo126$6025bo$6024b2o$6024bobo126$6146b2o
$6145b2o$6147bo125$6280b2o$6279b2o$6281bo126$6402b2o$6401b2o$6403bo
126$6518b2o$6517b2o$6519bo126$6656b2o$6655b2o$6657bo128$6777b3o$6777bo
$6778bo125$6911b3o$6911bo$6912bo126$7036b3o$7036bo$7037bo126$7166b3o$
7166bo$7167bo126$7297b3o$7297bo$7298bo127$7403b3o$7403bo$7404bo125$
7531b3o$7531bo$7532bo126$7660b3o$7660bo$7661bo125$7794bo$7793b2o$7793b
obo127$7909b3o$7909bo$7910bo126$8032b3o$8032bo$8033bo126$8157b3o$8157b
o$8158bo125$8290bo$8289b2o$8289bobo127$8412b3o$8412bo$8413bo126$8537b
3o$8537bo$8538bo125$8670bo$8669b2o$8669bobo127$8792b3o$8792bo$8793bo
126$8923b3o$8923bo$8924bo126$9051b3o$9051bo$9052bo125$9185bo$9184b2o$
9184bobo127$9315b3o$9315bo$9316bo126$9430b3o$9430bo$9431bo126$9543b3o$
9543bo$9544bo126$9661b3o$9661bo$9662bo125$9822b2o$9821b2o$9823bo126$
9943b2o$9942b2o$9944bo126$10078b2o$10077b2o$10079bo128$10202b3o$10202b
o$10203bo126$10320b3o$10320bo$10321bo126$10446b3o$10446bo$10447bo126$
10577b3o$10577bo$10578bo126$10712b3o$10712bo$10713bo126$10823b3o$
10823bo$10824bo126$10950b3o$10950bo$10951bo126$11084b3o$11084bo$11085b
o124$11221b2o$11220b2o$11222bo128$11346b3o$11346bo$11347bo126$11474b3o
$11474bo$11475bo124$11622b2o$11621b2o$11623bo128$11757b3o$11757bo$
11758bo126$11888b3o$11888bo$11889bo126$12023b3o$12023bo$12024bo126$
12151b3o$12151bo$12152bo126$12268b3o$12268bo$12269bo126$12404b3o$
12404bo$12405bo126$12523b3o$12523bo$12524bo126$12658b3o$12658bo$12659b
o126$12769b3o$12769bo$12770bo124$12899b2o$12898b2o$12900bo126$13034b2o
$13033b2o$13035bo126$13143b2o$13142b2o$13144bo128$13278b3o$13278bo$
13279bo124$13414b2o$13413b2o$13415bo126$13545b2o$13544b2o$13546bo128$
13661b3o$13661bo$13662bo126$13795b3o$13795bo$13796bo124$13930b2o$
13929b2o$13931bo126$14065b2o$14064b2o$14066bo126$14184b2o$14183b2o$
14185bo126$14329b2o$14328b2o$14330bo126$14456b2o$14455b2o$14457bo126$
14585b2o$14584b2o$14586bo128$14708b3o$14708bo$14709bo126$14835b3o$
14835bo$14836bo126$14961b3o$14961bo$14962bo124$15106b2o$15105b2o$
15107bo126$15226b2o$15225b2o$15227bo128$15353b3o$15353bo$15354bo124$
15469b2o$15468b2o$15470bo126$15608b2o$15607b2o$15609bo128$15734b3o$
15734bo$15735bo126$15854b3o$15854bo$15855bo125$16028b2o$16028bobo$
16028bo126$16152b2o$16152bobo$16152bo125$16270b2o$16270bobo$16270bo
127$16404b2o$16403b2o$16405bo125$16534b2o$16533b2o$16535bo126$16652b2o
$16651b2o$16653bo127$16794b2o$16793b2o$16795bo126$16925b3o$16925bo$
16926bo126$17049b3o$17049bo$17050bo126$17195b3o$17195bo$17196bo125$
17322bo$17321b2o$17321bobo127$17438b3o$17438bo$17439bo126$17563b3o$
17563bo$17564bo127$17691b3o$17691bo$17692bo126$17823b3o$17823bo$17824b
o126$17943b3o$17943bo$17944bo126$18072b3o$18072bo$18073bo125$18202bo$
18201b2o$18201bobo127$18338b3o$18338bo$18339bo125$18477b3o$18477bo$
18478bo127$18600b3o$18600bo$18601bo125$18719bo$18718b2o$18718bobo126$
18848bo$18847b2o$18847bobo126$18961b2o$18961bobo$18961bo126$19090b2o$
19090bobo$19090bo126$19226b2o$19226bobo$19226bo126$19349b2o$19349bobo$
19349bo126$19492bo$19491b2o$19491bobo126$19630b2o$19630bobo$19630bo
127$19749b3o$19749bo$19750bo126$19880b3o$19880bo$19881bo126$20015b3o$
20015bo$20016bo126$20137b3o$20137bo$20138bo126$20270b3o$20270bo$20271b
o126$20398b3o$20398bo$20399bo126$20525b3o$20525bo$20526bo126$20648b3o$
20648bo$20649bo126$20786b3o$20786bo$20787bo126$20905b3o$20905bo$20906b
o126$21030b3o$21030bo$21031bo126$21151b3o$21151bo$21152bo125$21297bo$
21296b2o$21296bobo127$21428b3o$21428bo$21429bo124$21547bo$21546b2o$
21546bobo126$21672bo$21671b2o$21671bobo126$21792bo$21791b2o$21791bobo
126$21922b2o$21922bobo$21922bo126$22067b2o$22067bobo$22067bo126$22183b
o$22182b2o$22182bobo126$22312bo$22311b2o$22311bobo126$22452bo$22451b2o
$22451bobo126$22580bo$22579b2o$22579bobo126$22702b2o$22702bobo$22702bo
126$22841b2o$22841bobo$22841bo127$22979b2o$22979bobo$22979bo126$23107b
2o$23107bobo$23107bo126$23229b2o$23229bobo$23229bo126$23367b2o$23367bo
bo$23367bo125$23497b2o$23497bobo$23497bo126$23615b2o$23615bobo$23615bo
126$23715b2o$23714b2o$23716bo127$23837bo$23836b2o$23836bobo126$23962bo
$23961b2o$23961bobo126$24098bo$24097b2o$24097bobo126$24225bo$24224b2o$
24224bobo126$24355bo$24354b2o$24354bobo126$24478bo$24477b2o$24477bobo
126$24608b2o$24608bobo$24608bo125$24735b2o$24734b2o$24736bo127$24871b
2o$24870b2o$24872bo125$24981b2o$24980b2o$24982bo128$25122b3o$25122bo$
25123bo124$25255b2o$25254b2o$25256bo127$25370b3o$25370bo$25371bo125$
25495b2o$25494b2o$25496bo127$25630b2o$25629b2o$25631bo126$25750bo$
25749b2o$25749bobo126$25886bo$25885b2o$25885bobo126$26016bo$26015b2o$
26015bobo126$26125bo$26124b2o$26124bobo126$26263b2o$26263bobo$26263bo
126$26396b2o$26396bobo$26396bo126$26539b2o$26539bobo$26539bo126$26657b
o$26656b2o$26656bobo126$26782b2o$26782bobo$26782bo125$26920bo$26919b2o
$26919bobo126$27051bo$27050b2o$27050bobo126$27179bo$27178b2o$27178bobo
126$27292b2o$27292bobo$27292bo126$27421b2o$27421bobo$27421bo127$27548b
o$27547b2o$27547bobo126$27673bo$27672b2o$27672bobo126$27809bo$27808b2o
$27808bobo126$27931bo$27930b2o$27930bobo126$28055b2o$28055bobo$28055bo
126$28200bo$28199b2o$28199bobo126$28323b2o$28323bobo$28323bo126$28441b
o$28440b2o$28440bobo126$28563bo$28562b2o$28562bobo127$28736b3o$28736bo
$28737bo126$28867b3o$28867bo$28868bo126$29002b3o$29002bo$29003bo126$
29122b3o$29122bo$29123bo126$29243b3o$29243bo$29244bo126$29373b3o$
29373bo$29374bo125$29529b2o$29529bobo$29529bo126$29653b2o$29653bobo$
29653bo126$29777b2o$29776b2o$29778bo126$29895b2o$29894b2o$29896bo126$
30021b2o$30021bobo$30021bo125$30153b2o$30153bobo$30153bo127$30285b2o$
30285bobo$30285bo126$30411b2o$30411bobo$30411bo126$30524b2o$30523b2o$
30525bo125$30648b2o$30647b2o$30649bo126$30788b2o$30787b2o$30789bo127$
30902b2o$30901b2o$30903bo126$31022b2o$31021b2o$31023bo125$31150b2o$
31150bobo$31150bo127$31276b2o$31276bobo$31276bo126$31402b2o$31402bobo$
31402bo125$31526b2o$31526bobo$31526bo127$31660b2o$31660bobo$31660bo
125$31768b2o$31768bobo$31768bo126$31904b2o$31903b2o$31905bo127$32032b
2o$32032bobo$32032bo126$32152b2o$32152bobo$32152bo125$32282b2o$32282bo
bo$32282bo126$32408b2o$32408bobo$32408bo128$32585b3o$32585bo$32586bo
125$32716b2o$32715b2o$32717bo126$32844b2o$32843b2o$32845bo125$32978b2o
$32977b2o$32979bo126$33108b2o$33107b2o$33109bo127$33226b2o$33225b2o$
33227bo126$33353b3o$33353bo$33354bo126$33471b3o$33471bo$33472bo126$
33603b3o$33603bo$33604bo126$33739b3o$33739bo$33740bo125$33869bo$33868b
2o$33868bobo127$33985b3o$33985bo$33986bo125$34107bo$34106b2o$34106bobo
126$34237bo$34236b2o$34236bobo127$34374b3o$34374bo$34375bo126$34502b3o
$34502bo$34503bo125$34652b2o$34651b2o$34653bo127$34822b3o$34822bo$
34823bo127$34942b3o$34942bo$34943bo126$35073b3o$35073bo$35074bo126$
35196b3o$35196bo$35197bo126$35339b3o$35339bo$35340bo126$35449b3o$
35449bo$35450bo126$35577b3o$35577bo$35578bo125$35725b2o$35724b2o$
35726bo125$35855b2o$35854b2o$35856bo126$35991b2o$35990b2o$35992bo127$
36110b3o$36110bo$36111bo125$36241b2o$36240b2o$36242bo127$36382b2o$
36382bobo$36382bo126$36502b2o$36502bobo$36502bo126$36644b2o$36644bobo$
36644bo125$36770b2o$36770bobo$36770bo126$36916b2o$36916bobo$36916bo
126$37042b2o$37042bobo$37042bo126$37160b2o$37160bobo$37160bo127$37290b
o$37289b2o$37289bobo126$37422bo$37421b2o$37421bobo126$37551b2o$37551bo
bo$37551bo126$37676bo$37675b2o$37675bobo126$37803bo$37802b2o$37802bobo
126$37933bo$37932b2o$37932bobo126$38077b2o$38077bobo$38077bo126$38198b
2o$38198bobo$38198bo126$38333b2o$38333bobo$38333bo125$38426b2o$38426bo
bo$38426bo126$38544b2o$38544bobo$38544bo127$38690b2o$38690bobo$38690bo
126$38808b2o$38808bobo$38808bo126$38946b2o$38946bobo$38946bo125$39074b
2o$39074bobo$39074bo127$39204b2o$39204bobo$39204bo126$39324b2o$39324bo
bo$39324bo126$39450bo$39449b2o$39449bobo126$39571bo$39570b2o$39570bobo
126$39705bo$39704b2o$39704bobo126$39825bo$39824b2o$39824bobo126$39957b
o$39956b2o$39956bobo126$40089bo$40088b2o$40088bobo126$40225b2o$40225bo
bo$40225bo126$40345bo$40344b2o$40344bobo126$40477bo$40476b2o$40476bobo
126$40614b2o$40614bobo$40614bo126$40727b2o$40727bobo$40727bo126$40870b
o$40869b2o$40869bobo126$40990b2o$40990bobo$40990bo126$41122b2o$41122bo
bo$41122bo126$41271b2o$41270b2o$41272bo127$41364b3o$41364bo$41365bo
126$41489b3o$41489bo$41490bo125$41618bo$41617b2o$41617bobo127$41753b3o
$41753bo$41754bo125$41879bo$41878b2o$41878bobo126$42014bo$42013b2o$
42013bobo125$42134b2o$42133b2o$42135bo126$42254b2o$42253b2o$42255bo
127$42400b2o$42399b2o$42401bo126$42528b2o$42527b2o$42529bo125$42654b2o
$42653b2o$42655bo126$42782b2o$42781b2o$42783bo127$42897b3o$42897bo$
42898bo126$43045b3o$43045bo$43046bo125$43139b2o$43138b2o$43140bo126$
43267b2o$43266b2o$43268bo128$43389b3o$43389bo$43390bo126$43520b3o$
43520bo$43521bo126$43648b3o$43648bo$43649bo125$43780bo$43779b2o$43779b
obo127$43893b3o$43893bo$43894bo125$44030bo$44029b2o$44029bobo127$
44148b3o$44148bo$44149bo126$44276b3o$44276bo$44277bo126$44418b3o$
44418bo$44419bo126$44541b3o$44541bo$44542bo125$44670bo$44669b2o$44669b
obo126$44792bo$44791b2o$44791bobo126$44933b2o$44932b2o$44934bo126$
45053b2o$45052b2o$45054bo126$45181b2o$45180b2o$45182bo125$45307b2o$
45306b2o$45308bo126$45451b2o$45450b2o$45452bo127$45579b2o$45578b2o$
45580bo126$45695b2o$45694b2o$45696bo125$45851b2o$45850b2o$45852bo127$
45958bo$45957b2o$45957bobo126$46072b3o$46072bo$46073bo126$46201b2o$
46200b2o$46202bo125$46327b2o$46326b2o$46328bo128$46458b3o$46458bo$
46459bo126$46578b3o$46578bo$46579bo125$46716b3o$46716bo$46717bo126$
46842b2o$46841b2o$46843bo125$46966b2o$46965b2o$46967bo127$47100b2o$
47099b2o$47101bo125$47212b2o$47211b2o$47213bo127$47337b3o$47337bo$
47338bo126$47489b3o$47489bo$47490bo126$47618b2o$47617b2o$47619bo125$
47728b2o$47727b2o$47729bo127$47888b3o$47888bo$47889bo127$48018b3o$
48018bo$48019bo126$48144b3o$48144bo$48145bo125$48267b3o$48267bo$48268b
o126$48399b3o$48399bo$48400bo126$48526b3o$48526bo$48527bo126$48653b3o$
48653bo$48654bo126$48774b2o$48774bobo$48774bo126$48902b2o$48902bobo$
48902bo126$49024b2o$49023b2o$49025bo126$49146b2o$49145b2o$49147bo126$
49284b2o$49284bobo$49284bo125$49404b2o$49403b2o$49405bo126$49540b2o$
49539b2o$49541bo127$49670b2o$49670bobo$49670bo125$49806bo$49805b2o$
49805bobo126$49931bo$49930b2o$49930bobo126$50054b2o$50054bobo$50054bo
126$50192b2o$50192bobo$50192bo126$50320b2o$50320bobo$50320bo126$50453b
o$50452b2o$50452bobo126$50553b2o$50553bobo$50553bo128$50684b3o$50684bo
$50685bo126$50812b3o$50812bo$50813bo126$50933b3o$50933bo$50934bo126$
51056b3o$51056bo$51057bo126$51181b3o$51181bo$51182bo125$51314bo$51313b
2o$51313bobo127$51436b3o$51436bo$51437bo126$51561b3o$51561bo$51562bo
125$51694bo$51693b2o$51693bobo126$51842bo$51841b2o$51841bobo126$51972b
3o$51972bo$51973bo126$52103b3o$52103bo$52104bo126$52231b3o$52231bo$
52232bo126$52370b3o$52370bo$52371bo126$52480b3o$52480bo$52481bo126$
52602b3o$52602bo$52603bo125$52737bo$52736b2o$52736bobo127$52857b3o$
52857bo$52858bo126$52980b3o$52980bo$52981bo126$53071bo$53070b2o$53070b
obo126$53196bo$53195b2o$53195bobo126$53336b2o$53336bobo$53336bo126$
53453b2o$53453bobo$53453bo126$53571b2o$53571bobo$53571bo126$53699bo$
53698b2o$53698bobo126$53847b2o$53847bobo$53847bo126$53964bo$53963b2o$
53963bobo126$54104b2o$54104bobo$54104bo126$54233bo$54232b2o$54232bobo
125$54362bo$54361b2o$54361bobo126$54491bo$54490b2o$54490bobo126$54605b
2o$54605bobo$54605bo126$54701b2o$54701bobo$54701bo126$54816b2o$54816bo
bo$54816bo127$54934b2o$54934bobo$54934bo126$55070b2o$55070bobo$55070bo
126$55173b2o$55173bobo$55173bo125$55303b2o$55303bobo$55303bo126$55423b
2o$55423bobo$55423bo127$55555b2o$55555bobo$55555bo126$55693b2o$55693bo
bo$55693bo126$55825b2o$55825bobo$55825bo125$55955b2o$55954b2o$55956bo
126$56071b2o$56071bobo$56071bo126$56209b2o$56209bobo$56209bo128$56350b
3o$56350bo$56351bo126$56482b3o$56482bo$56483bo126$56608b3o$56608bo$
56609bo126$56742b3o$56742bo$56743bo125$56881bo$56880b2o$56880bobo127$
57007b3o$57007bo$57008bo126$57117b3o$57117bo$57118bo126$57269b3o$
57269bo$57270bo125$57387b3o$57387bo$57388bo126$57519b3o$57519bo$57520b
o126$57643b3o$57643bo$57644bo125$57794bo$57793b2o$57793bobo126$57947b
2o$57947bobo$57947bo126$58077bo$58076b2o$58076bobo126$58205bo$58204b2o
$58204bobo126$58337b2o$58337bobo$58337bo126$58466b2o$58466bobo$58466bo
126$58595b2o$58595bobo$58595bo126$58710b2o$58710bobo$58710bo126$58841b
2o$58841bobo$58841bo126$58971b2o$58971bobo$58971bo128$59084b3o$59084bo
$59085bo126$59209b3o$59209bo$59210bo126$59333b3o$59333bo$59334bo126$
59462b3o$59462bo$59463bo126$59599b3o$59599bo$59600bo125$59721bo$59720b
2o$59720bobo127$59842b3o$59842bo$59843bo126$59977b3o$59977bo$59978bo
126$60113b3o$60113bo$60114bo124$60260b2o$60259b2o$60261bo126$60388b2o$
60387b2o$60389bo128$60515b3o$60515bo$60516bo124$60640b2o$60639b2o$
60641bo128$60793b3o$60793bo$60794bo126$60925b3o$60925bo$60926bo125$
61050b2o$61049b2o$61051bo127$61177b3o$61177bo$61178bo126$61278b3o$
61278bo$61279bo126$61408b3o$61408bo$61409bo126$61531b3o$61531bo$61532b
o126$61648b3o$61648bo$61649bo126$61787b3o$61787bo$61788bo125$61919bo$
61918b2o$61918bobo127$62044b3o$62044bo$62045bo125$62182bo$62181b2o$
62181bobo127$62328b3o$62328bo$62329bo126$62459b3o$62459bo$62460bo126$
62589b3o$62589bo$62590bo126$62712b3o$62712bo$62713bo125$62841bo$62840b
2o$62840bobo126$62980bo$62979b2o$62979bobo127$63106b3o$63106bo$63107bo
126$63222b3o$63222bo$63223bo126$63348b3o$63348bo$63349bo126$63484b3o$
63484bo$63485bo126$63606b3o$63606bo$63607bo126$63741b3o$63741bo$63742b
o126$63870b3o$63870bo$63871bo125$64008bo$64007b2o$64007bobo126$64120bo
$64119b2o$64119bobo126$64250bo$64249b2o$64249bobo125$64376b2o$64376bob
o$64376bo127$64504b2o$64504bobo$64504bo125$64634b2o$64633b2o$64635bo
127$64764b2o$64764bobo$64764bo125$64888b2o$64888bobo$64888bo127$65022b
2o$65022bobo$65022bo125$65152b2o$65152bobo$65152bo127$65270b2o$65269b
2o$65271bo125$65398b2o$65397b2o$65399bo127$65499bo$65498b2o$65498bobo
127$65634b3o$65634bo$65635bo126$65755b3o$65755bo$65756bo126$65886b3o$
65886bo$65887bo126$66014b3o$66014bo$66015bo125$66143bo$66142b2o$66142b
obo127$66257b3o$66257bo$66258bo125$66402bo$66401b2o$66401bobo127$
66512b3o$66512bo$66513bo125$66673b2o$66672b2o$66674bo126$66791b2o$
66790b2o$66792bo125$66923b2o$66922b2o$66924bo127$67057b2o$67056b2o$
67058bo126$67175b2o$67174b2o$67176bo127$67306b3o$67306bo$67307bo125$
67434b3o$67434bo$67435bo127$67554b3o$67554bo$67555bo126$67683b3o$
67683bo$67684bo125$67817bo$67816b2o$67816bobo126$67928bo$67927b2o$
67927bobo125$68055b2o$68054b2o$68056bo127$68187b2o$68186b2o$68188bo
126$68324b3o$68324bo$68325bo126$68450b3o$68450bo$68451bo126$68569b2o$
68568b2o$68570bo125$68685b2o$68684b2o$68686bo127$68812b3o$68812bo$
68813bo126$68964b3o$68964bo$68965bo125$69122bo$69121b2o$69121bobo126$
69240bo$69239b2o$69239bobo126$69371bo$69370b2o$69370bobo126$69503bo$
69502b2o$69502bobo126$69629bo$69628b2o$69628bobo126$69754bo$69753b2o$
69753bobo126$69903b2o$69903bobo$69903bo126$70030b2o$70030bobo$70030bo
126$70120b2o$70120bobo$70120bo126$70240b2o$70240bobo$70240bo126$70368b
2o$70368bobo$70368bo127$70514b2o$70514bobo$70514bo125$70636b2o$70636bo
bo$70636bo126$70758b2o$70758bobo$70758bo127$70886b2o$70886bobo$70886bo
125$71012b2o$71012bobo$71012bo127$71160b2o$71160bobo$71160bo125$71280b
2o$71279b2o$71281bo127$71398b2o$71398bobo$71398bo126$71532b2o$71532bob
o$71532bo126$71650b2o$71650bobo$71650bo125$71776b2o$71776bobo$71776bo
128$71920b3o$71920bo$71921bo126$72049b3o$72049bo$72050bo126$72181b3o$
72181bo$72182bo126$72299b3o$72299bo$72300bo126$72432b3o$72432bo$72433b
o125$72566bo$72565b2o$72565bobo126$72686b3o$72686bo$72687bo126$72817b
3o$72817bo$72818bo125$72975bo$72974b2o$72974bobo126$73106bo$73105b2o$
73105bobo126$73241bo$73240b2o$73240bobo126$73368bo$73367b2o$73367bobo
126$73492bo$73491b2o$73491bobo126$73618b2o$73618bobo$73618bo126$73741b
o$73740b2o$73740bobo127$73858bo$73857b2o$73857bobo126$73977b3o$73977bo
$73978bo125$74107b2o$74107bobo$74107bo127$74217b2o$74217bobo$74217bo
127$74377b3o$74377bo$74378bo126$74506b3o$74506bo$74507bo125$74634b2o$
74634bobo$74634bo126$74762b2o$74762bobo$74762bo125$74894b2o$74893b2o$
74895bo126$75016b2o$75016bobo$75016bo126$75134b2o$75134bobo$75134bo
127$75282b2o$75281b2o$75283bo126$75406b2o$75406bobo$75406bo126$75537b
2o$75537bobo$75537bo126$75661b2o$75661bobo$75661bo126$75781b2o$75781bo
bo$75781bo126$75905b2o$75905bobo$75905bo126$76033b2o$76033bobo$76033bo
126$76160b2o$76159b2o$76161bo125$76290b2o$76289b2o$76291bo126$76410b2o
$76409b2o$76411bo126$76544b2o$76543b2o$76545bo127$76675b3o$76675bo$
76676bo126$76788b2o$76787b2o$76789bo126$76919b3o$76919bo$76920bo125$
77058b2o$77057b2o$77059bo126$77192b2o$77191b2o$77193bo127$77330b3o$
77330bo$77331bo126$77469b3o$77469bo$77470bo126$77587b3o$77587bo$77588b
o125$77691b2o$77690b2o$77692bo127$77817b2o$77816b2o$77818bo126$77937b
2o$77936b2o$77938bo126$78076b3o$78076bo$78077bo126$78180b3o$78180bo$
78181bo126$78331b2o$78330b2o$78332bo126$78458b3o$78458bo$78459bo125$
78573b2o$78572b2o$78574bo127$78704b2o$78703b2o$78705bo126$78832b2o$
78831b2o$78833bo126$78947b3o$78947bo$78948bo126$79088b2o$79087b2o$
79089bo126$79195b3o$79195bo$79196bo126$79323b3o$79323bo$79324bo125$
79450bo$79449b2o$79449bobo127$79580b3o$79580bo$79581bo126$79715b3o$
79715bo$79716bo125$79845b2o$79844b2o$79846bo127$79971b2o$79970b2o$
79972bo126$80099b2o$80098b2o$80100bo126$80234b3o$80234bo$80235bo126$
80357b2o$80356b2o$80358bo126$80488b3o$80488bo$80489bo126$80625b2o$
80624b2o$80626bo126$80755b2o$80754b2o$80756bo125$80879b2o$80878b2o$
80880bo126$81007b2o$81006b2o$81008bo126$81145b2o$81144b2o$81146bo127$
81245bo$81244b2o$81244bobo126$81335b3o$81335bo$81336bo126$81460b3o$
81460bo$81461bo126$81588b3o$81588bo$81589bo126$81706b3o$81706bo$81707b
o125$81817b2o$81816b2o$81818bo127$81949b2o$81948b2o$81950bo126$82086b
3o$82086bo$82087bo126$82212b3o$82212bo$82213bo126$82331b2o$82330b2o$
82332bo125$82447b2o$82446b2o$82448bo127$82574b3o$82574bo$82575bo127$
82691b3o$82691bo$82692bo126$82822b3o$82822bo$82823bo126$82950b3o$
82950bo$82951bo125$83079bo$83078b2o$83078bobo127$83193b3o$83193bo$
83194bo125$83338bo$83337b2o$83337bobo127$83441b3o$83441bo$83442bo126$
83578b3o$83578bo$83579bo126$83704b3o$83704bo$83705bo126$83831b3o$
83831bo$83832bo124$83967b2o$83967bobo$83967bo127$84093b2o$84093bobo$
84093bo126$84213b2o$84213bobo$84213bo126$84277b3o$84277bo$84278bo126$
84395b3o$84395bo$84396bo126$84533b3o$84533bo$84534bo126$84676b3o$
84676bo$84677bo126$84808b3o$84808bo$84809bo125$84947bo$84946b2o$84946b
obo127$85059b3o$85059bo$85060bo125$85203bo$85202b2o$85202bobo126$
85334bo$85333b2o$85333bobo126$85464bo$85463b2o$85463bobo126$85594bo$
85593b2o$85593bobo128$85721b3o$85721bo$85722bo126$85849b3o$85849bo$
85850bo126$85991b3o$85991bo$85992bo126$86105b3o$86105bo$86106bo126$
86230b3o$86230bo$86231bo126$86361b3o$86361bo$86362bo125$86491bo$86490b
2o$86490bobo125$86626b2o$86625b2o$86627bo126$86746b2o$86745b2o$86747bo
126$86872b2o$86871b2o$86873bo127$87018b2o$87017b2o$87019bo125$87132b2o
$87131b2o$87133bo126$87254b2o$87253b2o$87255bo127$87374b2o$87373b2o$
87375bo126$87498b2o$87497b2o$87499bo126$87654b2o$87654bobo$87654bo126$
87778b2o$87778bobo$87778bo125$87918b2o$87917b2o$87919bo127$88028b2o$
88027b2o$88029bo126$88150b2o$88150bobo$88150bo126$88282b2o$88282bobo$
88282bo125$88408b2o$88408bobo$88408bo126$88545b2o$88544b2o$88546bo126$
88677b2o$88676b2o$88678bo126$88813b2o$88812b2o$88814bo127$88913b2o$
88912b2o$88914bo126$89049b2o$89048b2o$89050bo126$89198b3o$89198bo$
89199bo125$89325b2o$89324b2o$89326bo127$89463b2o$89462b2o$89464bo125$
89573b2o$89572b2o$89574bo127$89719b2o$89718b2o$89720bo126$89851b2o$
89850b2o$89852bo126$89970b3o$89970bo$89971bo127$90100b3o$90100bo$
90101bo125$90236b2o$90236bobo$90236bo126$90356b2o$90356bobo$90356bo
126$90482b2o$90482bobo$90482bo125$90620b2o$90620bobo$90620bo126$90748b
2o$90748bobo$90748bo127$90864b2o$90863b2o$90865bo125$90984b2o$90983b2o
$90985bo127$91143b2o$91143bobo$91143bo126$91267b2o$91267bobo$91267bo
126$91391b2o$91390b2o$91392bo126$91509b2o$91508b2o$91510bo126$91635b2o
$91635bobo$91635bo125$91767b2o$91767bobo$91767bo127$91899b2o$91899bobo
$91899bo126$92025b2o$92025bobo$92025bo126$92198b3o$92198bo$92199bo125$
92341bo$92340b2o$92340bobo126$92473bo$92472b2o$92472bobo126$92599bo$
92598b2o$92598bobo126$92733bo$92732b2o$92732bobo126$92851b2o$92851bobo
$92851bo126$92996bo$92995b2o$92995bobo126$93123b2o$93123bobo$93123bo
127$93209b3o$93209bo$93210bo126$93330b3o$93330bo$93331bo126$93475b3o$
93475bo$93476bo126$93604b3o$93604bo$93605bo126$93726b3o$93726bo$93727b
o125$93845bo$93844b2o$93844bobo127$93976b3o$93976bo$93977bo126$94104b
3o$94104bo$94105bo125$94277b2o$94277bobo$94277bo127$94413b2o$94413bobo
$94413bo126$94533b2o$94533bobo$94533bo125$94647b2o$94647bobo$94647bo
127$94783b2o$94783bobo$94783bo126$94901b2o$94901bobo$94901bo125$95033b
2o$95033bobo$95033bo127$95183b2o$95183bobo$95183bo125$95307b2o$95307bo
bo$95307bo127$95429b2o$95429bobo$95429bo125$95567b2o$95567bobo$95567bo
126$95687b2o$95687bobo$95687bo126$95809bo$95808b2o$95808bobo126$95947b
o$95946b2o$95946bobo126$96068bo$96067b2o$96067bobo126$96205b2o$96205bo
bo$96205bo126$96325b2o$96325bobo$96325bo126$96450bo$96449b2o$96449bobo
126$96579bo$96578b2o$96578bobo126$96706b2o$96706bobo$96706bo126$96806b
2o$96806bobo$96806bo127$96932b2o$96932bobo$96932bo126$97060b2o$97060bo
bo$97060bo126$97188b2o$97187b2o$97189bo125$97328b2o$97328bobo$97328bo
126$97440b2o$97439b2o$97441bo127$97580b2o$97580bobo$97580bo125$97706b
2o$97706bobo$97706bo126$97832b2o$97832bobo$97832bo127$97960b2o$97960bo
bo$97960bo125$98096b2o$98095b2o$98097bo126$98220b2o$98219b2o$98221bo
128$98339b3o$98339bo$98340bo126$98493b3o$98493bo$98494bo125$98610b2o$
98609b2o$98611bo126$98727b3o$98727bo$98728bo126$98865b3o$98865bo$
98866bo126$98979b3o$98979bo$98980bo125$99122b2o$99121b2o$99123bo127$
99217b3o$99217bo$99218bo126$99363bo$99362b2o$99362bobo126$99501bo$
99500b2o$99500bobo126$99622bo$99621b2o$99621bobo126$99753bo$99752b2o$
99752bobo126$99888bo$99887b2o$99887bobo126$99991b2o$99991bobo$99991bo
126$100122b2o$100122bobo$100122bo126$100246b2o$100246bobo$100246bo126$
100386b2o$100386bobo$100386bo126$100512b2o$100512bobo$100512bo126$
100641b2o$100640b2o$100642bo125$100777b2o$100776b2o$100778bo127$
100897b2o$100896b2o$100898bo126$101015b2o$101014b2o$101016bo126$
101126b3o$101126bo$101127bo127$101292b3o$101292bo$101293bo124$101417b
2o$101416b2o$101418bo127$101545b2o$101544b2o$101546bo126$101684b3o$
101684bo$101685bo126$101811b2o$101810b2o$101812bo125$101929b2o$101928b
2o$101930bo127$102056b3o$102056bo$102057bo125$102203b2o$102203bobo$
102203bo127$102341b2o$102341bobo$102341bo125$102463b2o$102463bobo$
102463bo127$102601b2o$102601bobo$102601bo125$102727b2o$102727bobo$
102727bo127$102853b2o$102853bobo$102853bo126$102967b2o$102967bobo$
102967bo125$103091b2o$103091bobo$103091bo127$103217b2o$103216b2o$
103218bo126$103361b2o$103361bobo$103361bo126$103462b3o$103462bo$
103463bo126$103594b3o$103594bo$103595bo125$103729bo$103728b2o$103728bo
bo127$103846b3o$103846bo$103847bo126$103978b3o$103978bo$103979bo126$
104101b3o$104101bo$104102bo125$104248b2o$104247b2o$104249bo126$104372b
2o$104371b2o$104373bo128$104499b3o$104499bo$104500bo124$104624b2o$
104623b2o$104625bo128$104765b3o$104765bo$104766bo126$104879b3o$104879b
o$104880bo124$105008b2o$105007b2o$105009bo127$105155b3o$105155bo$
105156bo125$105289bo$105288b2o$105288bobo126$105414bo$105413b2o$
105413bobo126$105550bo$105549b2o$105549bobo126$105665b2o$105665bobo$
105665bo126$105801b2o$105801bobo$105801bo126$105933b2o$105933bobo$
105933bo127$106022b2o$106022bobo$106022bo125$106156b2o$106156bobo$
106156bo127$106268b2o$106268bobo$106268bo125$106394b2o$106394bobo$
106394bo126$106528b2o$106528bobo$106528bo126$106666b2o$106665b2o$
106667bo126$106790b2o$106790bobo$106790bo126$106918b2o$106918bobo$
106918bo127$106951b2o$106950b2o$106952bo107894$76b3o$75bo2bo$78bo$74bo
3bo$78bo$75bobo!
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » February 19th, 2023, 6:34 am

slmake/slsparse is a fantastic tool. I have run it o consruct my DBCA and it went through it without an intervention.
... I have tought a bit about its usage (when minimizing size of the salvo is imporant) and I would like it to be able to work with "parametrized life objects".
When you build a "cuircuit", often most of the constelations have their fixed position you want to maintain, but some still lifes could be put in several places fulfilling the same task. ... Especially eaters required at ends of glider lines.
I am aware this could be difficult to generalize to configurations consisting from more stilllifes as eater5(tub with tail) or 2 blocks LWSS eater.

Currently I would like it especially for for eater1 ... to just specify glider directed line segment where an eater should consume gliders specifying both flips of the eater are fine would be even better.

Similarly for starting block position of the arm just the line is important, but positioning it after the arm is started has some small cost.
Specifying block is required in this diagonal line segment, preferable close to given position would be nice.

I understand the "input language" should be adjusted, and the splitting to subproblems would be more complicated, but even short line segments variability could possibly allow more efficient recepies. I hope the spliting to subproblems could work with "position of the object fixed", but the final part when the recepie for the object is seleced the variability could help to select cheaper recepie.

(Similarly tub/boat in CC semi-snark you do not mind which one to use ... but in this time you could predict tub would be cheaper so no good reason for variability.)

-------------------
Another tought considers starting configuration specification of the search. You have allowed starting block position to be set in life, what had problems for close positions to stilllifes so Lifehistory could be used for block in proximity with them. Even overlapping is solved this way.

I wish I could specify staring configuration by other still lifes as well (boat) ... actually I would like to say boat in this orientation is on this vertical line (may be with other constraints :( ...)

... OK this is probably not that important ... I would probably start with "optimal block start", make table of block position "glider distances" from it and boat to block distances ... so chosing rather good starting boat position is rather cheap.
-------------------
OK ... I can see any generalisation which comes to my mind would not be "closed" and would lead to complications which are avoidable when the glider count in the recepie is not the main issue, but maybe some small push in indicated directions could be helpful in general.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » February 19th, 2023, 8:58 am

Hippo.69 wrote:
February 19th, 2023, 6:34 am
slmake/slsparse is a fantastic tool. I have run it o consruct my DBCA and it went through it without an intervention.
... I have tought a bit about its usage (when minimizing size of the salvo is imporant)
Thanks!

In terms of minimising the salvo size, the most promising approach seems to be to use slmake to find a solution, and then have a separate postprocessing step which looks for optimisation opportunities in the slow salvo and reduces the cost until a fixed point is found. I experimented with this functionality a long time ago, and it did work, but it was very sluggish -- so I decided to disable this because in many cases people would prefer to have fast runtime than (say) a 10% smaller output salvo.
Hippo.69 wrote:
February 19th, 2023, 6:34 am
but some still lifes could be put in several places fulfilling the same task. ... Especially eaters required at ends of glider lines.
This should be doable, but it would involve a huge overhaul to the code: effectively the target would need to be stored as a set of 'components', where each component is represented as a set of alternatives.

Rather interestingly, this will help improve overall cost even if the input pattern has no alternatives: when we reduce it to a simpler pattern (e.g. by constructing an eater from a block), there may be multiple choices for this block. Effectively, we gain beam-search-like capabilities.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » February 20th, 2023, 9:04 am

I have not looked to insides of slmake, and i Hope I would not in near future ... but I have run it several times ... for the same configuration differing in starting block position it went with the starting block to the same position (almost always), but once the slow salvo from the same "restarting" position (defragmented) had population 44869 (8973 gliders), while in remaining reached population 44254. Than I shifted one eater by 1fd and I got population 44189. I have expected 0-5 gliders difference, but 13! ... in the context of blinker construction arm this is huge difference.
And the restarting box position differs ... (it is say 250,250 away from the shifted eater1).

User avatar
dvgrn
Moderator
Posts: 10612
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: slmake

Post by dvgrn » February 20th, 2023, 12:17 pm

calcyman wrote:
February 19th, 2023, 8:58 am
Hippo.69 wrote:
February 19th, 2023, 6:34 am
but some still lifes could be put in several places fulfilling the same task. ... Especially eaters required at ends of glider lines.
This should be doable, but it would involve a huge overhaul to the code...
I've been thinking of this as a "technically solved" problem, given distributed searching resources or enough time: we can always just generate thousands of different infile.mc inputs, compile them, and see which one turns out to be cheapest. Granted it's a blunt implement at best, and enormously time-consuming compared to actually making good choices at each stage, but it would produce an answer eventually.

Something that wouldn't take such a major overhaul of the code would be to add a readout of the number of gliders in the final recipe -- though I guess these days that could be extracted from defragmented.mc fairly easily.

From looking at the recipes that slsparse produces, compared to hand-compiled construction recipes, it seems to me that the largest gains would come from tracking all instances where a glider is used to shoot down a piece of junk. The locations and junk types could all be tracked, and whenever a build stage is complete and a new target object is needed in a different location, that "virtual junk table" could be consulted and the nearest piece of virtual junk could be de-virtualized and moved into position.

Even if no shot-down junk is available, it's often quite cheap to take an existing block and split it into two blocks, with one of them in the original location, and use the other block as a new target.

Quite often this would prevent some very silly-looking decisions that slsparse currently makes, up to and including (in big recipes like the 0E0P metacell) building a whole new Cordership to re-create a faraway target object right next to a previous completed cluster.

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » February 21st, 2023, 5:05 am

dvgrn wrote:
February 20th, 2023, 12:17 pm
calcyman wrote:
February 19th, 2023, 8:58 am
Hippo.69 wrote:
February 19th, 2023, 6:34 am
but some still lifes could be put in several places fulfilling the same task. ... Especially eaters required at ends of glider lines.
This should be doable, but it would involve a huge overhaul to the code...
I've been thinking of this as a "technically solved" problem, given distributed searching resources or enough time: we can always just generate thousands of different infile.mc inputs, compile them, and see which one turns out to be cheapest. Granted it's a blunt implement at best, and enormously time-consuming compared to actually making good choices at each stage, but it would produce an answer eventually.
Sure, this is actually what I am doing, but the complexity of this solution is too many orders of magnitude worse than it could be.

Wow, seems by moving one box by 1fd away from the pattern I have created configuration slmake fails on. It writes "3 objects of < 32 cells." with no progress for several hours. I am going to test the same pattern again to see if it hapens again. Oh I could post "current3.mc" (.rle has 15MB and rle.gz 4MB) ... at least next attempt did the same. It seems to me "precurse" is greedy algorithm when it finds something looking like a progress, it uses it.
Hashing total remaining population and avoiding continuation with the same total remaining population (and the same hash) could detect the problem, but step back would be required if no other continuation is find ...

Code: Select all

[M2] (lifelib ll2.5.5)
#R B3/S23
#G 0
........$........$........$........$........$........$........$.....**.$
4 0 0 0 1
5 0 0 2 0
........$........$........$........$........$.......*$.......*$........$
........$........$........$........$.....**.$.....**.$........$........$
4 0 4 0 5
5 0 6 0 0
........$........$........$........$........$........$**......$..*.....$
.....**.$........$........$........$........$........$........$........$
.*.*....$.*.*....$..*.....$........$........$........$........$........$
4 8 9 10 0
5 11 0 0 0
6 0 3 7 12
7 0 0 13 0
8 0 14 0 0
........$........$........$........$........$........$........$.***....$
4 0 0 16 0
5 0 0 0 17
.*......$..*.....$........$........$........$........$........$........$
4 19 0 0 0
5 0 20 0 0
6 0 18 0 21
7 0 0 22 0
8 0 0 23 0
........$........$........$........$........$........$........$......**$
........$........$........$........$........$........$........$*.......$
4 0 0 25 26
5 0 0 27 0
......*.$.......*$........$........$........$........$........$........$
4 29 0 0 0
5 30 0 0 0
6 0 28 0 31
7 0 0 32 0
8 0 33 0 0
9 15 24 0 34
10 0 0 0 35
11 0 36 0 0
12 0 37 0 0
13 0 38 0 0
........$........$........$........$........$........$........$***.....$
4 0 0 40 0
5 0 0 0 41
*.......$.*......$........$........$........$........$........$........$
4 43 0 0 0
5 0 44 0 0
6 0 42 0 45
7 0 0 46 0
8 0 0 47 0
9 0 0 48 0
10 0 0 49 0
........$........$........$........$........$........$........$**......$
.......*$........$........$........$........$........$........$........$
4 0 51 52 43
5 0 0 0 53
6 0 54 0 0
7 0 0 55 0
8 0 0 56 0
9 0 0 57 0
........$........$.......*$........$........$........$........$........$
........$**......$*.......$.*......$........$........$........$........$
4 0 0 59 60
5 0 0 0 61
6 0 62 0 0
7 0 0 63 0
8 0 64 0 0
........$........$........$........$........$.......*$........$........$
........$........$........$........$**......$*.......$.*......$........$
4 0 0 66 67
5 0 68 0 0
6 0 69 0 0
7 0 0 70 0
8 0 0 71 0
5 0 0 0 68
6 0 73 0 0
7 0 0 74 0
8 0 75 0 0
9 65 72 0 76
10 0 58 0 77
4 66 67 0 0
5 0 79 0 0
6 0 0 0 80
7 0 0 81 0
8 0 0 82 0
9 0 0 83 0
10 0 0 84 0
11 50 0 78 85
........$........$........$........$........$........$..***...$..*.....$
4 0 0 87 0
5 0 0 0 88
...*....$........$........$........$........$........$........$........$
4 90 0 0 0
5 0 91 0 0
6 0 89 0 92
7 0 0 93 0
8 0 0 94 0
9 0 0 95 0
........$........$........$........$........$........$.......*$.......*$
........$........$........$........$........$........$**......$........$
4 0 0 97 98
5 0 0 99 0
*.......$........$........$........$........$........$........$........$
4 0 101 0 0
5 102 0 0 0
6 0 100 0 103
7 0 0 104 0
8 0 105 0 0
........$........$........$........$........$........$.***....$.*......$
4 0 0 107 0
5 0 0 0 108
..*.....$........$........$........$........$........$........$........$
4 110 0 0 0
5 0 111 0 0
6 0 109 0 112
7 0 0 113 0
8 0 0 114 0
9 106 115 0 0
10 0 96 0 116
11 0 117 0 0
........$........$........$........$........$........$.**.....$**......$
4 0 119 0 110
5 0 0 0 120
6 0 121 0 0
7 0 0 122 0
8 0 0 123 0
.**.....$**......$..*.....$........$........$........$........$........$
4 0 0 0 125
5 0 0 0 126
6 0 127 0 0
7 0 0 128 0
8 0 129 0 0
9 0 124 0 130
........$........$........$.**.....$**......$..*.....$........$........$
4 0 0 0 132
5 0 133 0 0
6 0 134 0 0
7 0 0 135 0
8 0 0 136 0
9 0 0 137 0
5 0 0 0 133
6 0 139 0 0
7 0 0 140 0
8 0 141 0 0
4 0 132 0 0
5 0 143 0 0
6 0 0 0 144
7 0 0 145 0
8 0 0 146 0
9 142 147 0 0
10 131 138 0 148
11 0 0 149 0
12 86 0 118 150
........$........$........$........$........$...***..$...*....$....*...$
4 0 0 152 0
5 0 0 0 153
6 0 154 0 0
7 0 0 155 0
8 0 0 156 0
........$........$........$........$........$***.....$*.......$.*......$
4 0 0 0 158
5 0 0 159 0
6 0 160 0 0
7 0 0 161 0
8 0 162 0 0
9 0 157 0 163
........$........$........$........$........$..***...$..*.....$...*....$
4 0 0 165 0
5 0 0 0 166
6 0 167 0 0
7 0 0 168 0
8 0 0 169 0
9 0 0 170 0
10 164 171 0 0
11 0 172 0 0
12 0 173 0 0
........$........$........$........$........$..**....$.**.....$...*....$
4 0 175 0 0
5 0 0 0 176
6 0 177 0 0
7 0 0 178 0
8 0 0 179 0
9 0 0 180 0
10 0 0 181 0
........$........$........$........$........$........$........$..**....$
.**.....$...*....$........$........$........$........$........$........$
4 0 183 0 184
5 0 0 0 185
6 0 186 0 0
7 0 0 187 0
8 0 188 0 0
........$........$..**....$.**.....$...*....$........$........$........$
4 0 0 0 190
5 0 191 0 0
6 0 192 0 0
7 0 0 193 0
8 0 0 194 0
5 0 0 0 191
6 0 196 0 0
7 0 0 197 0
8 0 198 0 0
9 189 195 0 199
4 0 190 0 0
5 0 201 0 0
6 0 0 0 202
7 0 0 203 0
8 0 0 204 0
9 0 0 205 0
10 200 206 0 0
**......$*.*.....$*.......$........$........$........$........$........$
4 208 0 0 0
5 0 209 0 0
6 0 0 0 210
7 0 0 211 0
8 0 0 212 0
9 0 0 213 0
10 0 0 214 0
11 182 0 207 215
........$........$**......$*.*.....$*.......$........$........$........$
4 217 0 0 0
5 0 218 0 0
6 0 0 0 219
7 0 0 220 0
8 0 221 0 0
........$........$........$........$........$**......$*.*.....$*.......$
4 223 0 0 0
5 0 0 0 224
6 0 225 0 0
7 0 0 226 0
8 0 0 227 0
........$**......$*.*.....$*.......$........$........$........$........$
4 0 0 229 0
5 0 230 0 0
6 0 0 0 231
7 0 0 232 0
8 0 233 0 0
9 222 228 0 234
4 0 0 217 0
5 0 236 0 0
6 0 0 0 237
7 0 0 238 0
8 0 0 239 0
9 0 0 240 0
5 0 224 0 0
6 0 0 0 242
7 0 0 243 0
8 0 244 0 0
9 245 0 0 0
10 235 241 0 246
11 0 247 0 0
........$........$........$........$......**$......*.$.......*$........$
4 0 249 0 0
........$........$........$........$*.......$........$........$........$
4 251 0 0 0
5 250 252 0 0
6 0 0 0 253
7 0 0 254 0
8 0 0 255 0
9 0 0 256 0
10 0 0 257 0
........$........$........$........$...***..$...*....$....*...$........$
4 259 0 0 0
5 260 0 0 0
6 0 0 0 261
7 0 0 262 0
8 0 263 0 0
........$........$........$........$.....***$.....*..$......*.$........$
4 0 265 0 0
5 266 0 0 0
6 0 0 0 267
7 0 0 268 0
8 0 0 269 0
9 264 270 0 0
........$........$........$........$.....**.$....**..$......*.$........$
4 0 0 272 0
5 0 0 0 273
6 0 274 0 0
7 0 0 275 0
8 0 0 276 0
........$........$........$........$........$........$.....**.$....**..$
4 0 0 278 0
5 0 0 0 279
......*.$........$........$........$........$........$........$........$
4 281 0 0 0
5 0 282 0 0
6 0 280 0 283
7 0 0 284 0
8 0 285 0 0
9 0 277 0 286
10 271 0 0 287
........$.....**.$....**..$......*.$........$........$........$........$
4 289 0 0 0
5 0 0 0 290
6 0 291 0 0
7 0 0 292 0
8 0 0 293 0
9 0 0 294 0
10 0 0 295 0
11 258 0 288 296
12 216 0 248 297
13 151 0 174 298
5 0 290 0 0
6 0 0 0 300
7 0 0 301 0
8 0 302 0 0
4 0 0 289 0
5 0 304 0 0
6 0 0 0 305
7 0 0 306 0
8 0 0 307 0
9 303 308 0 0
........$........$........$.......*$.......*$........$........$........$
4 0 310 0 0
........$........$........$**......$........$*.......$........$........$
4 312 0 0 0
5 311 313 0 0
6 0 0 0 314
7 0 0 315 0
8 0 0 316 0
........$........$........$....***.$....*...$.....*..$........$........$
4 318 0 0 0
5 319 0 0 0
6 0 0 0 320
7 0 0 321 0
8 0 322 0 0
9 0 317 0 323
10 309 0 0 324
11 0 325 0 0
12 0 326 0 0
13 0 327 0 0
14 39 299 0 328
15 0 329 0 0
16 0 330 0 0
17 0 331 0 0
18 0 332 0 0
19 0 333 0 0
20 0 334 0 0
21 0 335 0 0
22 0 336 0 0
23 0 337 0 0
24 0 338 0 0
25 0 339 0 0
26 0 340 0 0
27 0 341 0 0
........$........$........$......**$......*.$.......*$........$........$
4 0 343 0 0
........$........$........$*.......$........$........$........$........$
4 345 0 0 0
5 344 346 0 0
6 0 0 0 347
7 0 0 348 0
8 0 0 349 0
9 0 0 350 0
10 0 0 351 0
........$........$........$......**$.....**.$.......*$........$........$
4 0 0 353 0
5 0 0 0 354
6 0 355 0 0
7 0 0 356 0
8 0 0 357 0
9 0 0 358 0
........$........$........$........$........$......**$.....**.$.......*$
4 0 0 360 0
5 0 0 0 361
6 0 362 0 0
7 0 0 363 0
8 0 364 0 0
......**$.....**.$.......*$........$........$........$........$........$
4 366 0 0 0
5 0 0 0 367
6 0 368 0 0
7 0 0 369 0
8 0 0 370 0
5 0 367 0 0
6 0 0 0 372
7 0 0 373 0
8 0 374 0 0
9 365 371 0 375
10 0 359 0 376
4 0 0 366 0
5 0 378 0 0
6 0 0 0 379
7 0 0 380 0
8 0 0 381 0
9 0 0 382 0
10 0 0 383 0
11 352 0 377 384
........$........$***.....$*.......$.*......$........$........$........$
4 386 0 0 0
5 0 387 0 0
6 0 0 0 388
7 0 0 389 0
8 0 0 390 0
9 0 0 391 0
........$........$.....***$.....*..$......*.$........$........$........$
4 393 0 0 0
5 394 0 0 0
6 0 0 0 395
7 0 0 396 0
8 0 397 0 0
........$........$.......*$.......*$........$........$........$........$
4 0 399 0 0
........$........$**......$........$*.......$........$........$........$
4 401 0 0 0
5 400 402 0 0
6 0 0 0 403
7 0 0 404 0
8 0 0 405 0
9 398 406 0 0
10 0 392 0 407
11 0 408 0 0
........$........$.......*$......**$........$........$........$........$
........$........$*.......$........$*.......$........$........$........$
4 0 0 410 411
5 0 0 0 412
6 0 413 0 0
7 0 0 414 0
8 0 0 415 0
........$........$........$........$.......*$......**$........$........$
........$........$........$........$*.......$........$*.......$........$
4 0 0 417 418
5 0 0 0 419
6 0 420 0 0
7 0 0 421 0
8 0 422 0 0
9 0 416 0 423
........$........$........$........$........$........$........$.......*$
4 0 0 425 26
......**$........$........$........$........$........$........$........$
........$*.......$........$........$........$........$........$........$
4 427 428 0 0
5 0 426 0 429
6 0 430 0 0
7 0 0 431 0
8 0 0 432 0
9 0 0 433 0
5 0 0 0 426
5 0 429 0 0
6 0 435 0 436
7 0 0 437 0
8 0 438 0 0
4 425 26 427 428
5 0 440 0 0
6 0 0 0 441
7 0 0 442 0
8 0 0 443 0
9 439 444 0 0
10 424 434 0 445
11 0 0 446 0
12 385 0 409 447
........$.***....$.*......$..*.....$........$........$........$........$
4 449 0 0 0
5 0 450 0 0
6 0 0 0 451
7 0 0 452 0
8 0 0 453 0
........$......**$......*.$.......*$........$........$........$........$
4 455 428 0 0
5 456 0 0 0
6 0 0 0 457
7 0 0 458 0
8 0 459 0 0
9 0 454 0 460
........$***.....$*.......$.*......$........$........$........$........$
4 462 0 0 0
5 0 463 0 0
6 0 0 0 464
7 0 0 465 0
8 0 0 466 0
9 0 0 467 0
10 461 468 0 0
11 0 469 0 0
12 0 470 0 0
8 0 0 64 0
9 0 0 472 0
10 0 0 473 0
........$........$........$........$.......*$........$........$........$
........$........$........$**......$*.......$.*......$........$........$
4 0 0 475 476
5 0 0 0 477
6 0 478 0 0
7 0 0 479 0
8 0 480 0 0
........$........$........$........$........$........$**......$*.......$
4 0 0 425 482
.*......$........$........$........$........$........$........$........$
4 0 484 0 0
5 0 483 0 485
6 0 486 0 0
7 0 0 487 0
8 0 0 488 0
5 0 0 0 483
5 0 485 0 0
6 0 490 0 491
7 0 0 492 0
8 0 493 0 0
9 481 489 0 494
4 425 482 0 484
5 0 496 0 0
6 0 0 0 497
7 0 0 498 0
8 0 0 499 0
9 0 0 500 0
10 495 501 0 0
..***...$..*.....$...*....$........$........$........$........$........$
4 503 0 0 0
5 0 504 0 0
6 0 0 0 505
7 0 0 506 0
8 0 0 507 0
9 0 0 508 0
10 0 0 509 0
11 474 0 502 510
.......*$.......*$........$........$........$........$........$........$
**......$........$*.......$........$........$........$........$........$
4 512 513 0 0
5 514 0 0 0
6 0 0 0 515
7 0 0 516 0
8 0 517 0 0
.***....$.*......$..*.....$........$........$........$........$........$
4 519 0 0 0
5 0 520 0 0
6 0 0 0 521
7 0 0 522 0
8 0 0 523 0
9 518 524 0 0
8 0 0 129 0
........$........$.**.....$**......$..*.....$........$........$........$
4 0 0 0 527
5 0 0 0 528
6 0 529 0 0
7 0 0 530 0
8 0 531 0 0
9 0 526 0 532
10 525 0 0 533
11 0 534 0 0
........$........$........$........$........$.**.....$**......$..*.....$
4 0 0 0 536
5 0 537 0 0
6 0 538 0 0
7 0 0 539 0
8 0 0 540 0
9 0 0 541 0
10 0 0 542 0
5 0 0 0 537
6 0 544 0 0
7 0 0 545 0
8 0 546 0 0
4 0 536 0 0
5 0 548 0 0
6 0 0 0 549
7 0 0 550 0
8 0 0 551 0
9 547 552 0 0
........$........$........$........$........$........$........$...***..$
4 0 0 554 0
5 0 0 0 555
...*....$....*...$........$........$........$........$........$........$
4 557 0 0 0
5 0 558 0 0
6 0 556 0 559
7 0 0 560 0
8 0 0 561 0
4 0 0 0 40
5 0 0 563 0
4 0 43 0 0
5 565 0 0 0
6 0 564 0 566
7 0 0 567 0
8 0 568 0 0
9 0 562 0 569
10 553 0 0 570
........$........$........$........$........$........$........$..***...$
4 0 0 572 0
5 0 0 0 573
..*.....$...*....$........$........$........$........$........$........$
4 575 0 0 0
5 0 576 0 0
6 0 574 0 577
7 0 0 578 0
8 0 0 579 0
9 0 0 580 0
10 0 0 581 0
11 543 0 571 582
12 511 0 535 583
13 448 0 471 584
14 0 0 585 0
..*.*...$..*.....$........$........$........$........$........$........$
4 0 183 0 587
5 0 0 0 588
6 0 589 0 0
7 0 0 590 0
8 0 0 591 0
9 0 0 592 0
........$........$..**....$..*.*...$..*.....$........$........$........$
4 0 0 0 594
5 0 0 0 595
6 0 596 0 0
7 0 0 597 0
8 0 598 0 0
4 0 594 0 0
5 0 600 0 0
6 0 0 0 601
7 0 0 602 0
8 0 0 603 0
4 0 0 0 175
5 0 605 0 0
6 0 606 0 0
7 0 0 607 0
8 0 608 0 0
9 599 604 0 609
10 0 593 0 610
11 0 611 0 0
12 0 612 0 0
13 0 613 0 0
........$........$........$........$........$........$..**....$..*.*...$
4 0 615 0 110
5 0 616 0 0
6 0 0 0 617
7 0 0 618 0
8 0 0 619 0
9 0 0 620 0
10 0 0 621 0
8 0 619 0 0
9 623 0 0 0
9 0 0 24 0
9 34 48 0 0
10 624 625 0 626
11 622 0 627 0
9 0 57 0 65
9 0 0 72 0
9 76 83 0 0
10 629 630 0 631
11 0 632 0 0
9 0 95 0 106
9 0 0 115 0
10 634 635 0 0
9 0 0 124 0
10 0 0 637 0
11 0 0 636 638
12 628 0 633 639
9 130 137 0 142
9 0 0 147 0
10 641 642 0 0
11 0 643 0 0
12 0 644 0 0
9 0 0 157 0
10 0 0 646 0
9 163 170 0 0
9 0 180 0 189
10 648 0 0 649
9 0 0 195 0
10 0 0 651 0
11 647 0 650 652
9 199 205 0 0
9 0 213 0 222
10 654 0 0 655
11 0 656 0 0
9 0 0 228 0
10 0 0 658 0
9 234 240 0 245
9 0 256 0 264
10 660 0 0 661
9 0 0 270 0
10 0 0 663 0
11 659 0 662 664
12 653 0 657 665
13 640 0 645 666
9 0 0 277 0
9 286 294 0 303
10 0 668 0 669
11 0 670 0 0
12 0 671 0 0
13 0 672 0 0
14 614 667 0 673
9 0 0 308 0
10 0 0 675 0
9 0 0 317 0
9 323 350 0 0
10 0 677 0 678
11 676 0 679 0
9 0 358 0 365
9 0 0 371 0
9 375 382 0 0
10 681 682 0 683
11 0 684 0 0
9 0 391 0 398
9 0 0 406 0
10 686 687 0 0
9 0 0 416 0
10 0 0 689 0
11 0 0 688 690
12 680 0 685 691
9 423 433 0 439
9 0 0 444 0
10 693 694 0 0
11 0 695 0 0
12 0 696 0 0
9 0 0 454 0
10 0 0 698 0
9 460 467 0 0
9 0 472 0 481
10 700 0 0 701
9 0 0 489 0
10 0 0 703 0
11 699 0 702 704
9 494 500 0 0
9 0 508 0 518
10 706 0 0 707
11 0 708 0 0
9 0 0 524 0
10 0 0 710 0
9 0 0 526 0
9 532 541 0 547
10 0 712 0 713
9 0 0 552 0
10 0 0 715 0
11 711 0 714 716
12 705 0 709 717
13 692 0 697 718
14 0 0 719 0
15 586 0 674 720
9 0 0 562 0
9 569 580 0 0
10 0 722 0 723
11 0 724 0 0
12 0 725 0 0
13 0 726 0 0
9 0 592 0 599
9 0 0 604 0
9 609 620 0 623
10 728 729 0 730
11 0 0 731 0
9 0 24 0 34
10 733 49 0 0
11 0 734 0 0
10 0 0 58 0
10 77 84 0 0
10 0 0 96 0
11 736 0 737 738
12 732 0 735 739
10 116 0 0 131
11 0 741 0 0
12 0 742 0 0
10 0 0 138 0
10 148 0 0 164
10 0 0 171 0
11 744 0 745 746
10 0 181 0 200
11 0 748 0 0
10 0 0 206 0
10 0 214 0 235
10 0 0 241 0
11 750 0 751 752
12 747 0 749 753
13 740 0 743 754
10 246 257 0 271
11 0 756 0 0
12 0 757 0 0
13 0 758 0 0
14 727 755 0 759
15 0 760 0 0
10 287 295 0 309
11 0 0 762 0
10 324 351 0 0
11 0 764 0 0
10 0 0 359 0
10 376 383 0 0
10 0 0 392 0
11 766 0 767 768
12 763 0 765 769
10 407 0 0 424
11 0 771 0 0
12 0 772 0 0
10 0 0 434 0
10 445 0 0 461
10 0 0 468 0
11 774 0 775 776
10 0 473 0 495
11 0 778 0 0
10 0 0 501 0
10 0 509 0 525
11 780 0 781 0
12 777 0 779 782
13 770 0 773 783
14 0 0 784 0
10 533 542 0 553
11 0 786 0 0
12 0 787 0 0
13 0 788 0 0
10 570 581 0 0
10 0 0 593 0
11 0 0 790 791
10 610 621 0 624
11 0 793 0 0
10 0 0 625 0
10 626 0 0 629
10 0 0 630 0
11 795 0 796 797
12 792 0 794 798
10 631 0 0 634
11 0 800 0 0
12 0 801 0 0
10 0 0 635 0
10 0 637 0 641
10 0 0 642 0
11 803 0 804 805
10 0 646 0 648
11 0 807 0 0
10 649 651 0 654
11 0 0 809 0
12 806 0 808 810
13 799 0 802 811
10 655 658 0 660
11 0 813 0 0
12 0 814 0 0
13 0 815 0 0
14 789 812 0 816
10 661 663 0 0
10 0 0 668 0
11 0 0 818 819
10 669 675 0 0
11 0 821 0 0
10 0 0 677 0
10 678 0 0 681
10 0 0 682 0
11 823 0 824 825
12 820 0 822 826
10 683 0 0 686
11 0 828 0 0
12 0 829 0 0
10 0 0 687 0
10 0 689 0 693
10 0 0 694 0
11 831 0 832 833
10 0 698 0 700
11 0 835 0 0
10 701 703 0 706
11 0 0 837 0
12 834 0 836 838
13 827 0 830 839
14 0 0 840 0
15 785 0 817 841
16 721 0 761 842
10 707 710 0 0
11 0 844 0 0
12 0 845 0 0
13 0 846 0 0
10 0 0 712 0
10 713 715 0 0
10 0 0 722 0
11 848 0 849 850
10 723 0 0 728
11 0 852 0 0
10 0 0 729 0
10 730 0 0 733
11 854 0 855 50
12 851 0 853 856
11 0 78 0 0
12 0 858 0 0
11 85 0 117 0
11 0 149 0 0
11 0 0 172 182
12 860 0 861 862
13 857 0 859 863
11 0 207 0 0
12 0 865 0 0
13 0 866 0 0
14 847 864 0 867
15 0 868 0 0
16 0 869 0 0
11 215 0 247 258
11 0 288 0 0
11 296 0 325 352
12 871 0 872 873
11 0 377 0 0
12 0 875 0 0
11 384 0 408 0
11 0 446 0 0
11 0 0 469 474
12 877 0 878 879
13 874 0 876 880
14 0 0 881 0
11 0 502 0 0
12 0 883 0 0
13 0 884 0 0
11 510 0 534 543
11 0 571 0 0
11 582 0 611 622
12 886 0 887 888
11 0 627 0 0
12 0 890 0 0
11 0 0 632 0
11 0 636 0 0
11 638 0 643 647
12 892 0 893 894
13 889 0 891 895
11 0 650 0 0
12 0 897 0 0
13 0 898 0 0
14 885 896 0 899
11 652 0 656 659
11 0 662 0 0
11 664 0 670 676
12 901 0 902 903
11 0 679 0 0
12 0 905 0 0
11 0 0 684 0
11 0 688 0 0
11 690 0 695 699
12 907 0 908 909
13 904 0 906 910
14 0 0 911 0
15 882 0 900 912
11 0 702 0 0
12 0 914 0 0
13 0 915 0 0
11 704 0 708 711
11 0 714 0 0
11 716 0 724 0
12 917 0 918 919
11 0 731 0 0
12 0 921 0 0
11 0 0 734 736
11 0 737 0 0
11 738 0 741 744
12 923 0 924 925
13 920 0 922 926
11 0 745 0 0
12 0 928 0 0
13 0 929 0 0
14 916 927 0 930
15 0 931 0 0
11 746 0 748 750
11 0 751 0 0
11 752 0 756 0
12 933 0 934 935
11 0 762 0 0
12 0 937 0 0
11 0 0 764 766
11 0 767 0 0
11 768 0 771 774
12 939 0 940 941
13 936 0 938 942
14 0 0 943 0
11 0 775 0 0
12 0 945 0 0
13 0 946 0 0
11 776 0 778 780
11 0 781 0 0
11 0 0 786 0
12 948 0 949 950
11 0 790 0 0
12 0 952 0 0
11 791 0 793 795
11 0 796 0 0
11 797 0 800 803
12 954 0 955 956
13 951 0 953 957
11 0 804 0 0
12 0 959 0 0
13 0 960 0 0
14 947 958 0 961
11 805 0 807 0
11 0 809 0 0
11 0 0 813 0
12 963 0 964 965
11 0 818 0 0
12 0 967 0 0
11 819 0 821 823
11 0 824 0 0
11 825 0 828 831
12 969 0 970 971
13 966 0 968 972
14 0 0 973 0
15 944 0 962 974
16 913 0 932 975
17 843 0 870 976
11 0 832 0 0
12 0 978 0 0
13 0 979 0 0
11 833 0 835 0
11 0 837 0 0
11 0 0 844 848
12 981 0 982 983
11 0 849 0 0
12 0 985 0 0
11 850 0 852 854
11 0 855 0 0
12 987 0 988 86
13 984 0 986 989
12 0 118 0 0
13 0 991 0 0
14 980 990 0 992
15 0 993 0 0
16 0 994 0 0
17 0 995 0 0
12 150 0 173 216
12 0 248 0 0
12 297 0 326 385
13 997 0 998 999
14 0 0 1000 0
12 0 409 0 0
13 0 1002 0 0
12 447 0 470 511
12 0 535 0 0
12 583 0 612 628
13 1004 0 1005 1006
12 0 633 0 0
13 0 1008 0 0
14 1003 1007 0 1009
12 639 0 644 653
12 0 657 0 0
12 665 0 671 680
13 1011 0 1012 1013
14 0 0 1014 0
15 1001 0 1010 1015
12 0 685 0 0
13 0 1017 0 0
12 691 0 696 705
12 0 709 0 0
12 717 0 725 732
13 1019 0 1020 1021
12 0 735 0 0
13 0 1023 0 0
14 1018 1022 0 1024
15 0 1025 0 0
12 739 0 742 747
12 0 749 0 0
12 753 0 757 763
13 1027 0 1028 1029
14 0 0 1030 0
12 0 765 0 0
13 0 1032 0 0
12 769 0 772 777
12 0 779 0 0
12 782 0 787 792
13 1034 0 1035 1036
12 0 794 0 0
13 0 1038 0 0
14 1033 1037 0 1039
12 798 0 801 806
12 0 808 0 0
12 810 0 814 820
13 1041 0 1042 1043
14 0 0 1044 0
15 1031 0 1040 1045
16 1016 0 1026 1046
12 0 822 0 0
13 0 1048 0 0
12 826 0 829 834
12 0 836 0 0
12 838 0 845 851
13 1050 0 1051 1052
12 0 853 0 0
13 0 1054 0 0
14 1049 1053 0 1055
15 0 1056 0 0
16 0 1057 0 0
12 856 0 858 860
12 0 861 0 0
12 862 0 865 871
13 1059 0 1060 1061
14 0 0 1062 0
12 0 872 0 0
13 0 1064 0 0
12 873 0 875 877
12 0 878 0 0
12 879 0 883 886
13 1066 0 1067 1068
12 0 887 0 0
13 0 1070 0 0
14 1065 1069 0 1071
12 888 0 890 892
12 0 893 0 0
12 894 0 897 901
13 1073 0 1074 1075
14 0 0 1076 0
15 1063 0 1072 1077
12 0 902 0 0
13 0 1079 0 0
12 903 0 905 907
12 0 908 0 0
12 909 0 914 917
13 1081 0 1082 1083
12 0 918 0 0
13 0 1085 0 0
14 1080 1084 0 1086
15 0 1087 0 0
12 919 0 921 923
12 0 924 0 0
12 925 0 928 933
13 1089 0 1090 1091
14 0 0 1092 0
12 0 934 0 0
13 0 1094 0 0
12 935 0 937 939
12 0 940 0 0
12 941 0 945 948
13 1096 0 1097 1098
12 0 949 0 0
13 0 1100 0 0
14 1095 1099 0 1101
12 950 0 952 954
12 0 955 0 0
12 956 0 959 963
13 1103 0 1104 1105
14 0 0 1106 0
15 1093 0 1102 1107
16 1078 0 1088 1108
17 1047 0 1058 1109
18 977 0 996 1110
12 0 964 0 0
13 0 1112 0 0
12 965 0 967 969
12 0 970 0 0
12 971 0 978 981
13 1114 0 1115 1116
12 0 982 0 0
13 0 1118 0 0
14 1113 1117 0 1119
15 0 1120 0 0
16 0 1121 0 0
17 0 1122 0 0
18 0 1123 0 0
12 983 0 985 987
12 0 988 0 0
13 1125 0 1126 151
14 0 0 1127 0
13 0 174 0 0
13 298 0 327 448
13 0 471 0 0
14 1129 1130 0 1131
13 584 0 613 640
14 0 0 1133 0
15 1128 0 1132 1134
13 0 645 0 0
13 666 0 672 692
13 0 697 0 0
14 1136 1137 0 1138
15 0 1139 0 0
13 718 0 726 740
14 0 0 1141 0
13 0 743 0 0
13 754 0 758 770
13 0 773 0 0
14 1143 1144 0 1145
13 783 0 788 799
14 0 0 1147 0
15 1142 0 1146 1148
16 1135 0 1140 1149
13 0 802 0 0
13 811 0 815 827
13 0 830 0 0
14 1151 1152 0 1153
15 0 1154 0 0
16 0 1155 0 0
13 839 0 846 857
14 0 0 1157 0
13 0 859 0 0
13 863 0 866 874
13 0 876 0 0
14 1159 1160 0 1161
13 880 0 884 889
14 0 0 1163 0
15 1158 0 1162 1164
13 0 891 0 0
13 895 0 898 904
13 0 906 0 0
14 1166 1167 0 1168
15 0 1169 0 0
13 910 0 915 920
14 0 0 1171 0
13 0 922 0 0
13 926 0 929 936
13 0 938 0 0
14 1173 1174 0 1175
13 942 0 946 951
14 0 0 1177 0
15 1172 0 1176 1178
16 1165 0 1170 1179
17 1150 0 1156 1180
13 0 953 0 0
13 957 0 960 966
13 0 968 0 0
14 1182 1183 0 1184
15 0 1185 0 0
16 0 1186 0 0
17 0 1187 0 0
13 972 0 979 984
14 0 0 1189 0
13 0 986 0 0
13 989 0 991 997
13 0 998 0 0
14 1191 1192 0 1193
13 999 0 1002 1004
14 0 0 1195 0
15 1190 0 1194 1196
13 0 1005 0 0
13 1006 0 1008 1011
13 0 1012 0 0
14 1198 1199 0 1200
15 0 1201 0 0
13 1013 0 1017 1019
14 0 0 1203 0
13 0 1020 0 0
13 1021 0 1023 1027
13 0 1028 0 0
14 1205 1206 0 1207
13 1029 0 1032 1034
14 0 0 1209 0
15 1204 0 1208 1210
16 1197 0 1202 1211
13 0 1035 0 0
13 1036 0 1038 1041
13 0 1042 0 0
14 1213 1214 0 1215
15 0 1216 0 0
16 0 1217 0 0
13 1043 0 1048 1050
14 0 0 1219 0
13 0 1051 0 0
13 1052 0 1054 1059
13 0 1060 0 0
14 1221 1222 0 1223
13 1061 0 1064 1066
14 0 0 1225 0
15 1220 0 1224 1226
13 0 1067 0 0
13 1068 0 1070 1073
13 0 1074 0 0
14 1228 1229 0 1230
15 0 1231 0 0
13 1075 0 1079 1081
14 0 0 1233 0
13 0 1082 0 0
13 1083 0 1085 1089
13 0 1090 0 0
14 1235 1236 0 1237
13 1091 0 1094 1096
14 0 0 1239 0
15 1234 0 1238 1240
16 1227 0 1232 1241
17 1212 0 1218 1242
18 1181 0 1188 1243
19 1111 0 1124 1244
13 0 1097 0 0
13 1098 0 1100 1103
13 0 1104 0 0
14 1246 1247 0 1248
15 0 1249 0 0
16 0 1250 0 0
17 0 1251 0 0
18 0 1252 0 0
19 0 1253 0 0
13 1105 0 1112 1114
14 0 0 1255 0
13 0 1115 0 0
13 1116 0 1118 1125
13 0 1126 0 0
14 1257 1258 0 1259
14 0 0 299 0
15 1256 0 1260 1261
14 328 585 0 614
15 0 1263 0 0
14 0 0 667 0
14 673 719 0 727
14 0 0 755 0
15 1265 0 1266 1267
16 1262 0 1264 1268
14 759 784 0 789
15 0 1270 0 0
16 0 1271 0 0
14 0 0 812 0
14 816 840 0 847
14 0 0 864 0
15 1273 0 1274 1275
14 867 881 0 885
15 0 1277 0 0
14 0 0 896 0
14 899 911 0 916
14 0 0 927 0
15 1279 0 1280 1281
16 1276 0 1278 1282
17 1269 0 1272 1283
14 930 943 0 947
15 0 1285 0 0
16 0 1286 0 0
17 0 1287 0 0
14 0 0 958 0
14 961 973 0 980
14 0 0 990 0
15 1289 0 1290 1291
14 992 1000 0 1003
15 0 1293 0 0
14 0 0 1007 0
14 1009 1014 0 1018
14 0 0 1022 0
15 1295 0 1296 1297
16 1292 0 1294 1298
14 1024 1030 0 1033
15 0 1300 0 0
16 0 1301 0 0
14 0 0 1037 0
14 1039 1044 0 1049
14 0 0 1053 0
15 1303 0 1304 1305
14 1055 1062 0 1065
15 0 1307 0 0
14 0 0 1069 0
14 1071 1076 0 1080
14 0 0 1084 0
15 1309 0 1310 1311
16 1306 0 1308 1312
17 1299 0 1302 1313
18 1284 0 1288 1314
14 1086 1092 0 1095
15 0 1316 0 0
16 0 1317 0 0
17 0 1318 0 0
18 0 1319 0 0
14 0 0 1099 0
14 1101 1106 0 1113
14 0 0 1117 0
15 1321 0 1322 1323
14 1119 1127 0 1129
15 0 1325 0 0
14 0 0 1130 0
14 1131 1133 0 1136
14 0 0 1137 0
15 1327 0 1328 1329
16 1324 0 1326 1330
14 1138 1141 0 1143
15 0 1332 0 0
16 0 1333 0 0
14 0 0 1144 0
14 1145 1147 0 1151
14 0 0 1152 0
15 1335 0 1336 1337
14 1153 1157 0 1159
15 0 1339 0 0
14 0 0 1160 0
14 1161 1163 0 1166
14 0 0 1167 0
15 1341 0 1342 1343
16 1338 0 1340 1344
17 1331 0 1334 1345
14 1168 1171 0 1173
15 0 1347 0 0
16 0 1348 0 0
17 0 1349 0 0
14 0 0 1174 0
14 1175 1177 0 1182
14 0 0 1183 0
15 1351 0 1352 1353
14 1184 1189 0 1191
15 0 1355 0 0
14 0 0 1192 0
14 1193 1195 0 1198
14 0 0 1199 0
15 1357 0 1358 1359
16 1354 0 1356 1360
14 1200 1203 0 1205
15 0 1362 0 0
16 0 1363 0 0
14 0 0 1206 0
14 1207 1209 0 1213
14 0 0 1214 0
15 1365 0 1366 1367
14 1215 1219 0 1221
15 0 1369 0 0
14 0 0 1222 0
14 1223 1225 0 1228
14 0 0 1229 0
15 1371 0 1372 1373
16 1368 0 1370 1374
17 1361 0 1364 1375
18 1346 0 1350 1376
19 1315 0 1320 1377
20 1245 0 1254 1378
14 1230 1233 0 1235
15 0 1380 0 0
16 0 1381 0 0
17 0 1382 0 0
18 0 1383 0 0
19 0 1384 0 0
20 0 1385 0 0
14 0 0 1236 0
14 1237 1239 0 1246
14 0 0 1247 0
15 1387 0 1388 1389
14 1248 1255 0 1257
15 0 1391 0 0
14 0 0 1258 0
14 1259 299 0 328
15 1393 0 1394 586
16 1390 0 1392 1395
15 0 674 0 0
16 0 1397 0 0
15 720 0 760 785
15 0 817 0 0
15 841 0 868 882
16 1399 0 1400 1401
17 1396 0 1398 1402
15 0 900 0 0
16 0 1404 0 0
17 0 1405 0 0
15 912 0 931 944
15 0 962 0 0
15 974 0 993 1001
16 1407 0 1408 1409
15 0 1010 0 0
16 0 1411 0 0
15 1015 0 1025 1031
15 0 1040 0 0
15 1045 0 1056 1063
16 1413 0 1414 1415
17 1410 0 1412 1416
18 1403 0 1406 1417
15 0 1072 0 0
16 0 1419 0 0
17 0 1420 0 0
18 0 1421 0 0
15 1077 0 1087 1093
15 0 1102 0 0
15 1107 0 1120 1128
16 1423 0 1424 1425
15 0 1132 0 0
16 0 1427 0 0
15 1134 0 1139 1142
15 0 1146 0 0
15 1148 0 1154 1158
16 1429 0 1430 1431
17 1426 0 1428 1432
15 0 1162 0 0
16 0 1434 0 0
17 0 1435 0 0
15 1164 0 1169 1172
15 0 1176 0 0
15 1178 0 1185 1190
16 1437 0 1438 1439
15 0 1194 0 0
16 0 1441 0 0
15 1196 0 1201 1204
15 0 1208 0 0
15 1210 0 1216 1220
16 1443 0 1444 1445
17 1440 0 1442 1446
18 1433 0 1436 1447
19 1418 0 1422 1448
15 0 1224 0 0
16 0 1450 0 0
17 0 1451 0 0
18 0 1452 0 0
19 0 1453 0 0
15 1226 0 1231 1234
15 0 1238 0 0
15 1240 0 1249 1256
16 1455 0 1456 1457
15 0 1260 0 0
16 0 1459 0 0
15 1261 0 1263 1265
15 0 1266 0 0
15 1267 0 1270 1273
16 1461 0 1462 1463
17 1458 0 1460 1464
15 0 1274 0 0
16 0 1466 0 0
17 0 1467 0 0
15 1275 0 1277 1279
15 0 1280 0 0
15 1281 0 1285 1289
16 1469 0 1470 1471
15 0 1290 0 0
16 0 1473 0 0
15 1291 0 1293 1295
15 0 1296 0 0
15 1297 0 1300 1303
16 1475 0 1476 1477
17 1472 0 1474 1478
18 1465 0 1468 1479
15 0 1304 0 0
16 0 1481 0 0
17 0 1482 0 0
18 0 1483 0 0
15 1305 0 1307 1309
15 0 1310 0 0
15 1311 0 1316 1321
16 1485 0 1486 1487
15 0 1322 0 0
16 0 1489 0 0
15 1323 0 1325 1327
15 0 1328 0 0
15 1329 0 1332 1335
16 1491 0 1492 1493
17 1488 0 1490 1494
15 0 1336 0 0
16 0 1496 0 0
17 0 1497 0 0
15 1337 0 1339 1341
15 0 1342 0 0
15 1343 0 1347 1351
16 1499 0 1500 1501
15 0 1352 0 0
16 0 1503 0 0
15 1353 0 1355 1357
15 0 1358 0 0
15 1359 0 1362 1365
16 1505 0 1506 1507
17 1502 0 1504 1508
18 1495 0 1498 1509
19 1480 0 1484 1510
20 1449 0 1454 1511
21 1379 0 1386 1512
15 0 1366 0 0
16 0 1514 0 0
17 0 1515 0 0
18 0 1516 0 0
19 0 1517 0 0
20 0 1518 0 0
21 0 1519 0 0
15 1367 0 1369 1371
15 0 1372 0 0
15 1373 0 1380 1387
16 1521 0 1522 1523
15 0 1388 0 0
16 0 1525 0 0
15 1389 0 1391 1393
15 0 1394 0 0
16 1527 0 1528 721
17 1524 0 1526 1529
16 0 761 0 0
17 0 1531 0 0
16 842 0 869 913
16 0 932 0 0
16 975 0 994 1016
17 1533 0 1534 1535
18 1530 0 1532 1536
16 0 1026 0 0
17 0 1538 0 0
18 0 1539 0 0
16 1046 0 1057 1078
16 0 1088 0 0
16 1108 0 1121 1135
17 1541 0 1542 1543
16 0 1140 0 0
17 0 1545 0 0
16 1149 0 1155 1165
16 0 1170 0 0
16 1179 0 1186 1197
17 1547 0 1548 1549
18 1544 0 1546 1550
19 1537 0 1540 1551
16 0 1202 0 0
17 0 1553 0 0
18 0 1554 0 0
19 0 1555 0 0
16 1211 0 1217 1227
16 0 1232 0 0
16 1241 0 1250 1262
17 1557 0 1558 1559
16 0 1264 0 0
17 0 1561 0 0
16 1268 0 1271 1276
16 0 1278 0 0
16 1282 0 1286 1292
17 1563 0 1564 1565
18 1560 0 1562 1566
16 0 1294 0 0
17 0 1568 0 0
18 0 1569 0 0
16 1298 0 1301 1306
16 0 1308 0 0
16 1312 0 1317 1324
17 1571 0 1572 1573
16 0 1326 0 0
17 0 1575 0 0
16 1330 0 1333 1338
16 0 1340 0 0
16 1344 0 1348 1354
17 1577 0 1578 1579
18 1574 0 1576 1580
19 1567 0 1570 1581
20 1552 0 1556 1582
16 0 1356 0 0
17 0 1584 0 0
18 0 1585 0 0
19 0 1586 0 0
20 0 1587 0 0
16 1360 0 1363 1368
16 0 1370 0 0
16 1374 0 1381 1390
17 1589 0 1590 1591
16 0 1392 0 0
17 0 1593 0 0
16 1395 0 1397 1399
16 0 1400 0 0
16 1401 0 1404 1407
17 1595 0 1596 1597
18 1592 0 1594 1598
16 0 1408 0 0
17 0 1600 0 0
18 0 1601 0 0
16 1409 0 1411 1413
16 0 1414 0 0
16 1415 0 1419 1423
17 1603 0 1604 1605
16 0 1424 0 0
17 0 1607 0 0
16 1425 0 1427 1429
16 0 1430 0 0
16 1431 0 1434 1437
17 1609 0 1610 1611
18 1606 0 1608 1612
19 1599 0 1602 1613
16 0 1438 0 0
17 0 1615 0 0
18 0 1616 0 0
19 0 1617 0 0
16 1439 0 1441 1443
16 0 1444 0 0
16 1445 0 1450 1455
17 1619 0 1620 1621
16 0 1456 0 0
17 0 1623 0 0
16 1457 0 1459 1461
16 0 1462 0 0
16 1463 0 1466 1469
17 1625 0 1626 1627
18 1622 0 1624 1628
16 0 1470 0 0
17 0 1630 0 0
18 0 1631 0 0
16 1471 0 1473 1475
16 0 1476 0 0
16 1477 0 1481 1485
17 1633 0 1634 1635
16 0 1486 0 0
17 0 1637 0 0
16 1487 0 1489 1491
16 0 1492 0 0
16 1493 0 1496 1499
17 1639 0 1640 1641
18 1636 0 1638 1642
19 1629 0 1632 1643
20 1614 0 1618 1644
21 1583 0 1588 1645
22 1513 0 1520 1646
16 0 1500 0 0
17 0 1648 0 0
18 0 1649 0 0
19 0 1650 0 0
20 0 1651 0 0
21 0 1652 0 0
22 0 1653 0 0
16 1501 0 1503 1505
16 0 1506 0 0
16 1507 0 1514 1521
17 1655 0 1656 1657
16 0 1522 0 0
17 0 1659 0 0
16 1523 0 1525 1527
16 0 1528 0 0
17 1661 0 1662 843
18 1658 0 1660 1663
17 0 870 0 0
18 0 1665 0 0
17 976 0 995 1047
17 0 1058 0 0
17 1109 0 1122 1150
18 1667 0 1668 1669
19 1664 0 1666 1670
17 0 1156 0 0
18 0 1672 0 0
19 0 1673 0 0
17 1180 0 1187 1212
17 0 1218 0 0
17 1242 0 1251 1269
18 1675 0 1676 1677
17 0 1272 0 0
18 0 1679 0 0
17 1283 0 1287 1299
17 0 1302 0 0
17 1313 0 1318 1331
18 1681 0 1682 1683
19 1678 0 1680 1684
20 1671 0 1674 1685
17 0 1334 0 0
18 0 1687 0 0
19 0 1688 0 0
20 0 1689 0 0
17 1345 0 1349 1361
17 0 1364 0 0
17 1375 0 1382 1396
18 1691 0 1692 1693
17 0 1398 0 0
18 0 1695 0 0
17 1402 0 1405 1410
17 0 1412 0 0
17 1416 0 1420 1426
18 1697 0 1698 1699
19 1694 0 1696 1700
17 0 1428 0 0
18 0 1702 0 0
19 0 1703 0 0
17 1432 0 1435 1440
17 0 1442 0 0
17 1446 0 1451 1458
18 1705 0 1706 1707
17 0 1460 0 0
18 0 1709 0 0
17 1464 0 1467 1472
17 0 1474 0 0
17 1478 0 1482 1488
18 1711 0 1712 1713
19 1708 0 1710 1714
20 1701 0 1704 1715
21 1686 0 1690 1716
17 0 1490 0 0
18 0 1718 0 0
19 0 1719 0 0
20 0 1720 0 0
21 0 1721 0 0
17 1494 0 1497 1502
17 0 1504 0 0
17 1508 0 1515 1524
18 1723 0 1724 1725
17 0 1526 0 0
18 0 1727 0 0
17 1529 0 1531 1533
17 0 1534 0 0
17 1535 0 1538 1541
18 1729 0 1730 1731
19 1726 0 1728 1732
17 0 1542 0 0
18 0 1734 0 0
19 0 1735 0 0
17 1543 0 1545 1547
17 0 1548 0 0
17 1549 0 1553 1557
18 1737 0 1738 1739
17 0 1558 0 0
18 0 1741 0 0
17 1559 0 1561 1563
17 0 1564 0 0
17 1565 0 1568 1571
18 1743 0 1744 1745
19 1740 0 1742 1746
20 1733 0 1736 1747
17 0 1572 0 0
18 0 1749 0 0
19 0 1750 0 0
20 0 1751 0 0
17 1573 0 1575 1577
17 0 1578 0 0
17 1579 0 1584 1589
18 1753 0 1754 1755
17 0 1590 0 0
18 0 1757 0 0
17 1591 0 1593 1595
17 0 1596 0 0
17 1597 0 1600 1603
18 1759 0 1760 1761
19 1756 0 1758 1762
17 0 1604 0 0
18 0 1764 0 0
19 0 1765 0 0
17 1605 0 1607 1609
17 0 1610 0 0
17 1611 0 1615 1619
18 1767 0 1768 1769
17 0 1620 0 0
18 0 1771 0 0
17 1621 0 1623 1625
17 0 1626 0 0
17 1627 0 1630 1633
18 1773 0 1774 1775
19 1770 0 1772 1776
20 1763 0 1766 1777
21 1748 0 1752 1778
22 1717 0 1722 1779
23 1647 0 1654 1780
17 0 1634 0 0
18 0 1782 0 0
19 0 1783 0 0
20 0 1784 0 0
21 0 1785 0 0
22 0 1786 0 0
23 0 1787 0 0
17 1635 0 1637 1639
17 0 1640 0 0
17 1641 0 1648 1655
18 1789 0 1790 1791
17 0 1656 0 0
18 0 1793 0 0
17 1657 0 1659 1661
17 0 1662 0 0
18 1795 0 1796 977
19 1792 0 1794 1797
18 0 996 0 0
19 0 1799 0 0
18 1110 0 1123 1181
18 0 1188 0 0
18 1243 0 1252 1284
19 1801 0 1802 1803
20 1798 0 1800 1804
18 0 1288 0 0
19 0 1806 0 0
20 0 1807 0 0
18 1314 0 1319 1346
18 0 1350 0 0
18 1376 0 1383 1403
19 1809 0 1810 1811
18 0 1406 0 0
19 0 1813 0 0
18 1417 0 1421 1433
18 0 1436 0 0
18 1447 0 1452 1465
19 1815 0 1816 1817
20 1812 0 1814 1818
21 1805 0 1808 1819
18 0 1468 0 0
19 0 1821 0 0
20 0 1822 0 0
21 0 1823 0 0
18 1479 0 1483 1495
18 0 1498 0 0
18 1509 0 1516 1530
19 1825 0 1826 1827
18 0 1532 0 0
19 0 1829 0 0
18 1536 0 1539 1544
18 0 1546 0 0
18 1550 0 1554 1560
19 1831 0 1832 1833
20 1828 0 1830 1834
18 0 1562 0 0
19 0 1836 0 0
20 0 1837 0 0
18 1566 0 1569 1574
18 0 1576 0 0
18 1580 0 1585 1592
19 1839 0 1840 1841
18 0 1594 0 0
19 0 1843 0 0
18 1598 0 1601 1606
18 0 1608 0 0
18 1612 0 1616 1622
19 1845 0 1846 1847
20 1842 0 1844 1848
21 1835 0 1838 1849
22 1820 0 1824 1850
18 0 1624 0 0
19 0 1852 0 0
20 0 1853 0 0
21 0 1854 0 0
22 0 1855 0 0
18 1628 0 1631 1636
18 0 1638 0 0
18 1642 0 1649 1658
19 1857 0 1858 1859
18 0 1660 0 0
19 0 1861 0 0
18 1663 0 1665 1667
18 0 1668 0 0
18 1669 0 1672 1675
19 1863 0 1864 1865
20 1860 0 1862 1866
18 0 1676 0 0
19 0 1868 0 0
20 0 1869 0 0
18 1677 0 1679 1681
18 0 1682 0 0
18 1683 0 1687 1691
19 1871 0 1872 1873
18 0 1692 0 0
19 0 1875 0 0
18 1693 0 1695 1697
18 0 1698 0 0
18 1699 0 1702 1705
19 1877 0 1878 1879
20 1874 0 1876 1880
21 1867 0 1870 1881
18 0 1706 0 0
19 0 1883 0 0
20 0 1884 0 0
21 0 1885 0 0
18 1707 0 1709 1711
18 0 1712 0 0
18 1713 0 1718 1723
19 1887 0 1888 1889
18 0 1724 0 0
19 0 1891 0 0
18 1725 0 1727 1729
18 0 1730 0 0
18 1731 0 1734 1737
19 1893 0 1894 1895
20 1890 0 1892 1896
18 0 1738 0 0
19 0 1898 0 0
20 0 1899 0 0
18 1739 0 1741 1743
18 0 1744 0 0
18 1745 0 1749 1753
19 1901 0 1902 1903
18 0 1754 0 0
19 0 1905 0 0
18 1755 0 1757 1759
18 0 1760 0 0
18 1761 0 1764 1767
19 1907 0 1908 1909
20 1904 0 1906 1910
21 1897 0 1900 1911
22 1882 0 1886 1912
23 1851 0 1856 1913
24 1781 0 1788 1914
18 0 1768 0 0
19 0 1916 0 0
20 0 1917 0 0
21 0 1918 0 0
22 0 1919 0 0
23 0 1920 0 0
24 0 1921 0 0
18 1769 0 1771 1773
18 0 1774 0 0
18 1775 0 1782 1789
19 1923 0 1924 1925
18 0 1790 0 0
19 0 1927 0 0
18 1791 0 1793 1795
18 0 1796 0 0
19 1929 0 1930 1111
20 1926 0 1928 1931
19 0 1124 0 0
20 0 1933 0 0
19 1244 0 1253 1315
19 0 1320 0 0
19 1377 0 1384 1418
20 1935 0 1936 1937
21 1932 0 1934 1938
19 0 1422 0 0
20 0 1940 0 0
21 0 1941 0 0
19 1448 0 1453 1480
19 0 1484 0 0
19 1510 0 1517 1537
20 1943 0 1944 1945
19 0 1540 0 0
20 0 1947 0 0
19 1551 0 1555 1567
19 0 1570 0 0
19 1581 0 1586 1599
20 1949 0 1950 1951
21 1946 0 1948 1952
22 1939 0 1942 1953
19 0 1602 0 0
20 0 1955 0 0
21 0 1956 0 0
22 0 1957 0 0
19 1613 0 1617 1629
19 0 1632 0 0
19 1643 0 1650 1664
20 1959 0 1960 1961
19 0 1666 0 0
20 0 1963 0 0
19 1670 0 1673 1678
19 0 1680 0 0
19 1684 0 1688 1694
20 1965 0 1966 1967
21 1962 0 1964 1968
19 0 1696 0 0
20 0 1970 0 0
21 0 1971 0 0
19 1700 0 1703 1708
19 0 1710 0 0
19 1714 0 1719 1726
20 1973 0 1974 1975
19 0 1728 0 0
20 0 1977 0 0
19 1732 0 1735 1740
19 0 1742 0 0
19 1746 0 1750 1756
20 1979 0 1980 1981
21 1976 0 1978 1982
22 1969 0 1972 1983
23 1954 0 1958 1984
19 0 1758 0 0
20 0 1986 0 0
21 0 1987 0 0
22 0 1988 0 0
23 0 1989 0 0
19 1762 0 1765 1770
19 0 1772 0 0
19 1776 0 1783 1792
20 1991 0 1992 1993
19 0 1794 0 0
20 0 1995 0 0
19 1797 0 1799 1801
19 0 1802 0 0
19 1803 0 1806 1809
20 1997 0 1998 1999
21 1994 0 1996 2000
19 0 1810 0 0
20 0 2002 0 0
21 0 2003 0 0
19 1811 0 1813 1815
19 0 1816 0 0
19 1817 0 1821 1825
20 2005 0 2006 2007
19 0 1826 0 0
20 0 2009 0 0
19 1827 0 1829 1831
19 0 1832 0 0
19 1833 0 1836 1839
20 2011 0 2012 2013
21 2008 0 2010 2014
22 2001 0 2004 2015
19 0 1840 0 0
20 0 2017 0 0
21 0 2018 0 0
22 0 2019 0 0
19 1841 0 1843 1845
19 0 1846 0 0
19 1847 0 1852 1857
20 2021 0 2022 2023
19 0 1858 0 0
20 0 2025 0 0
19 1859 0 1861 1863
19 0 1864 0 0
19 1865 0 1868 1871
20 2027 0 2028 2029
21 2024 0 2026 2030
19 0 1872 0 0
20 0 2032 0 0
21 0 2033 0 0
19 1873 0 1875 1877
19 0 1878 0 0
19 1879 0 1883 1887
20 2035 0 2036 2037
19 0 1888 0 0
20 0 2039 0 0
19 1889 0 1891 1893
19 0 1894 0 0
19 1895 0 1898 1901
20 2041 0 2042 2043
21 2038 0 2040 2044
22 2031 0 2034 2045
23 2016 0 2020 2046
24 1985 0 1990 2047
25 1915 0 1922 2048
19 0 1902 0 0
20 0 2050 0 0
21 0 2051 0 0
22 0 2052 0 0
23 0 2053 0 0
24 0 2054 0 0
25 0 2055 0 0
19 1903 0 1905 1907
19 0 1908 0 0
19 1909 0 1916 1923
20 2057 0 2058 2059
19 0 1924 0 0
20 0 2061 0 0
19 1925 0 1927 1929
19 0 1930 0 0
20 2063 0 2064 1245
21 2060 0 2062 2065
20 0 1254 0 0
21 0 2067 0 0
20 1378 0 1385 1449
20 0 1454 0 0
20 1511 0 1518 1552
21 2069 0 2070 2071
22 2066 0 2068 2072
20 0 1556 0 0
21 0 2074 0 0
22 0 2075 0 0
20 1582 0 1587 1614
20 0 1618 0 0
20 1644 0 1651 1671
21 2077 0 2078 2079
20 0 1674 0 0
21 0 2081 0 0
20 1685 0 1689 1701
20 0 1704 0 0
20 1715 0 1720 1733
21 2083 0 2084 2085
22 2080 0 2082 2086
23 2073 0 2076 2087
20 0 1736 0 0
21 0 2089 0 0
22 0 2090 0 0
23 0 2091 0 0
20 1747 0 1751 1763
20 0 1766 0 0
20 1777 0 1784 1798
21 2093 0 2094 2095
20 0 1800 0 0
21 0 2097 0 0
20 1804 0 1807 1812
20 0 1814 0 0
20 1818 0 1822 1828
21 2099 0 2100 2101
22 2096 0 2098 2102
20 0 1830 0 0
21 0 2104 0 0
22 0 2105 0 0
20 1834 0 1837 1842
20 0 1844 0 0
20 1848 0 1853 1860
21 2107 0 2108 2109
20 0 1862 0 0
21 0 2111 0 0
20 1866 0 1869 1874
20 0 1876 0 0
20 1880 0 1884 1890
21 2113 0 2114 2115
22 2110 0 2112 2116
23 2103 0 2106 2117
24 2088 0 2092 2118
20 0 1892 0 0
21 0 2120 0 0
22 0 2121 0 0
23 0 2122 0 0
24 0 2123 0 0
20 1896 0 1899 1904
20 0 1906 0 0
20 1910 0 1917 1926
21 2125 0 2126 2127
20 0 1928 0 0
21 0 2129 0 0
20 1931 0 1933 1935
20 0 1936 0 0
20 1937 0 1940 1943
21 2131 0 2132 2133
22 2128 0 2130 2134
20 0 1944 0 0
21 0 2136 0 0
22 0 2137 0 0
20 1945 0 1947 1949
20 0 1950 0 0
20 1951 0 1955 1959
21 2139 0 2140 2141
20 0 1960 0 0
21 0 2143 0 0
20 1961 0 1963 1965
20 0 1966 0 0
20 1967 0 1970 1973
21 2145 0 2146 2147
22 2142 0 2144 2148
23 2135 0 2138 2149
20 0 1974 0 0
21 0 2151 0 0
22 0 2152 0 0
23 0 2153 0 0
20 1975 0 1977 1979
20 0 1980 0 0
20 1981 0 1986 1991
21 2155 0 2156 2157
20 0 1992 0 0
21 0 2159 0 0
20 1993 0 1995 1997
20 0 1998 0 0
20 1999 0 2002 2005
21 2161 0 2162 2163
22 2158 0 2160 2164
20 0 2006 0 0
21 0 2166 0 0
22 0 2167 0 0
20 2007 0 2009 2011
20 0 2012 0 0
20 2013 0 2017 2021
21 2169 0 2170 2171
20 0 2022 0 0
21 0 2173 0 0
20 2023 0 2025 2027
20 0 2028 0 0
20 2029 0 2032 2035
21 2175 0 2176 2177
22 2172 0 2174 2178
23 2165 0 2168 2179
24 2150 0 2154 2180
25 2119 0 2124 2181
26 2049 0 2056 2182
20 0 2036 0 0
21 0 2184 0 0
22 0 2185 0 0
23 0 2186 0 0
24 0 2187 0 0
25 0 2188 0 0
26 0 2189 0 0
20 2037 0 2039 2041
20 0 2042 0 0
20 2043 0 2050 2057
21 2191 0 2192 2193
20 0 2058 0 0
21 0 2195 0 0
20 2059 0 2061 2063
20 0 2064 0 0
21 2197 0 2198 1379
22 2194 0 2196 2199
21 0 1386 0 0
22 0 2201 0 0
21 1512 0 1519 1583
21 0 1588 0 0
21 1645 0 1652 1686
22 2203 0 2204 2205
23 2200 0 2202 2206
21 0 1690 0 0
22 0 2208 0 0
23 0 2209 0 0
21 1716 0 1721 1748
21 0 1752 0 0
21 1778 0 1785 1805
22 2211 0 2212 2213
21 0 1808 0 0
22 0 2215 0 0
21 1819 0 1823 1835
21 0 1838 0 0
21 1849 0 1854 1867
22 2217 0 2218 2219
23 2214 0 2216 2220
24 2207 0 2210 2221
21 0 1870 0 0
22 0 2223 0 0
23 0 2224 0 0
24 0 2225 0 0
.......*$......**$........$........$........$........$........$........$
4 0 0 0 2227
*.......$........$*.......$........$........$........$........$........$
4 0 0 2229 0
5 0 0 2228 2230
6 0 2231 0 0
7 0 0 2232 0
8 0 0 2233 0
........$........$........$.......*$......**$........$........$........$
4 0 0 0 2235
........$........$........$*.......$........$*.......$........$........$
4 0 0 2237 0
5 0 0 2236 2238
6 0 2239 0 0
7 0 0 2240 0
8 0 2241 0 0
9 0 2234 0 2242
........$........$........$........$........$........$.......*$......**$
4 0 0 0 2244
........$........$........$........$........$........$*.......$........$
4 0 0 2246 0
4 101 0 0 0
5 2245 2247 0 2248
6 0 2249 0 0
7 0 0 2250 0
8 0 0 2251 0
9 0 0 2252 0
5 0 0 2245 2247
5 0 2248 0 0
6 0 2254 0 2255
7 0 0 2256 0
8 0 2257 0 0
4 0 2244 0 0
4 2246 0 101 0
5 2259 2260 0 0
6 0 0 0 2261
7 0 0 2262 0
8 0 0 2263 0
9 2258 2264 0 0
10 2243 2253 0 2265
11 0 2266 0 0
12 0 2267 0 0
........$........$........$........$..***...$..*.....$...*....$........$
4 2269 0 0 0
5 2270 0 0 0
6 0 0 0 2271
7 0 0 2272 0
8 0 2273 0 0
9 0 270 0 2274
........$........$........$........$....***.$....*...$.....*..$........$
4 0 2276 0 0
5 2277 0 0 0
6 0 0 0 2278
7 0 0 2279 0
8 0 0 2280 0
9 0 0 2281 0
10 2275 2282 0 0
........$........$........$........$....**..$....*.*.$....*...$........$
4 0 0 2284 0
5 0 0 0 2285
6 0 2286 0 0
7 0 0 2287 0
8 0 0 2288 0
9 0 0 2289 0
10 0 0 2290 0
11 0 0 2283 2291
........$........$........$........$........$........$....**..$....*.*.$
4 0 0 2293 0
5 0 0 0 2294
....*...$........$........$........$........$........$........$........$
4 2296 0 0 0
5 0 2297 0 0
6 0 2295 0 2298
7 0 0 2299 0
8 0 2300 0 0
........$....**..$....*.*.$....*...$........$........$........$........$
4 2302 0 0 0
5 0 0 0 2303
6 0 2304 0 0
7 0 0 2305 0
8 0 0 2306 0
5 0 2303 0 0
6 0 0 0 2308
7 0 0 2309 0
8 0 2310 0 0
9 2301 2307 0 2311
........$........$........$........$....**..$...**...$.....*..$........$
4 0 0 2313 0
5 0 2314 0 0
6 0 0 0 2315
7 0 0 2316 0
8 0 0 2317 0
9 0 0 2318 0
........$........$........$....**..$...**...$.....*..$........$........$
4 0 0 2320 0
5 0 2321 0 0
6 0 0 0 2322
7 0 0 2323 0
8 0 2324 0 0
9 2325 0 0 0
10 2312 2319 0 2326
11 0 2327 0 0
........$........$........$........$***.....$*.......$.*......$........$
4 0 0 0 2329
5 0 2330 0 0
6 0 0 2331 0
7 0 0 2332 0
8 0 0 2333 0
9 0 0 2334 0
10 0 0 2335 0
........$........$........$........$.***....$.*......$..*.....$........$
4 0 0 0 2337
5 0 2338 0 0
6 0 0 2339 0
7 0 0 2340 0
8 0 2341 0 0
........$........$........$......*.$.....**.$.....*.*$........$........$
4 0 0 0 2343
5 2344 0 0 0
6 0 0 2345 0
7 0 0 2346 0
8 0 0 2347 0
........$........$........$........$.......*$.......*$........$........$
4 0 0 0 2349
........$........$........$........$**......$........$*.......$........$
4 0 0 2351 0
5 2350 2352 0 0
6 0 0 2353 0
7 0 0 2354 0
8 0 2355 0 0
9 2342 2348 0 2356
4 0 0 259 0
5 0 2358 0 0
6 0 0 2359 0
7 0 0 2360 0
8 0 0 2361 0
9 0 0 2362 0
10 2357 2363 0 0
5 0 2270 0 0
6 0 0 0 2365
7 0 0 2366 0
8 0 0 2367 0
9 0 0 2368 0
10 0 0 2369 0
11 2336 0 2364 2370
12 2292 0 2328 2371
13 151 0 2268 2372
14 0 0 2373 0
4 2337 0 0 0
5 0 2375 0 0
6 0 0 0 2376
7 0 0 2377 0
8 0 2378 0 0
........$........$........$.....*..$....**..$....*.*.$........$........$
4 0 2380 0 0
5 2381 0 0 0
6 0 0 0 2382
7 0 0 2383 0
8 0 0 2384 0
4 0 2337 0 0
5 0 2386 0 0
6 0 0 0 2387
7 0 0 2388 0
8 0 2389 0 0
9 2379 2385 0 2390
........$........$........$........$........$........$........$...**...$
..**....$....*...$........$........$........$........$........$........$
4 0 2392 0 2393
5 0 0 0 2394
6 0 0 2395 0
7 0 0 2396 0
8 0 0 2397 0
........$........$........$........$...**...$..**....$....*...$........$
4 0 2399 0 0
5 0 0 0 2400
6 0 0 2401 0
7 0 0 2402 0
8 0 2403 0 0
9 0 2398 0 2404
10 2391 0 0 2405
11 0 2406 0 0
12 0 2407 0 0
13 0 2408 0 0
........$........$........$...**...$..**....$....*...$........$........$
4 0 0 0 2410
5 0 2411 0 0
6 0 0 2412 0
7 0 0 2413 0
8 0 0 2414 0
9 0 0 2415 0
10 0 0 2416 0
...**...$..**....$....*...$........$........$........$........$........$
4 0 2418 0 0
5 0 0 0 2419
6 0 0 2420 0
7 0 0 2421 0
8 0 2422 0 0
8 0 0 2403 0
4 0 0 0 165
5 0 0 0 2425
6 2426 0 0 0
7 0 0 2427 0
8 0 2428 0 0
9 2423 2424 0 2429
5 0 2419 0 0
6 2431 0 0 0
7 2432 0 0 0
........$........$........$........$........$...**...$..**....$....*...$
4 0 0 0 2434
5 0 0 0 2435
6 0 0 2436 0
7 0 0 2437 0
8 2433 2438 0 0
4 0 0 0 87
5 0 0 0 2440
6 0 0 2441 0
7 0 0 2442 0
8 0 0 2443 0
4 0 90 0 0
5 0 2445 0 0
6 2446 0 0 0
7 2447 0 0 0
8 2448 2443 0 2448
9 2439 2444 0 2449
10 2430 0 0 2450
11 2417 0 2451 0
........$........$........$...***..$...*....$....*...$........$........$
4 0 0 0 2453
5 0 2454 0 0
6 0 0 0 2455
7 0 0 2456 0
8 0 0 2457 0
........$........$........$.....***$.....*..$......*.$........$........$
4 0 0 2459 0
5 2460 0 0 0
6 0 0 2461 0
7 0 0 0 2462
8 0 2463 0 0
9 0 2458 0 2464
8 0 0 2463 0
9 0 0 2466 0
........$........$........$.***....$.*......$..*.....$........$........$
4 0 0 2468 0
5 0 2469 0 0
6 0 0 0 2470
7 0 0 2471 0
8 0 2472 0 0
........$........$........$..***...$..*.....$...*....$........$........$
4 0 0 0 2474
5 2475 0 0 0
6 0 0 0 2476
7 0 0 2477 0
8 0 0 2478 0
4 0 0 343 345
5 2480 0 0 0
6 0 0 0 2481
7 0 0 2482 0
8 0 2483 0 0
9 2473 2479 0 2484
10 2465 2467 0 2485
11 0 2486 0 0
5 0 2475 0 0
6 0 0 2488 0
7 0 0 2489 0
8 0 0 2490 0
9 0 0 2491 0
10 0 0 2492 0
4 0 0 0 2468
5 2494 0 0 0
6 0 0 2495 0
7 0 0 0 2496
8 0 2497 0 0
4 0 0 2453 0
5 0 2499 0 0
6 0 0 2500 0
7 0 0 2501 0
8 0 0 2502 0
9 2498 2503 0 0
..**....$.**.....$...*....$........$........$........$........$........$
4 0 0 0 2505
5 0 0 2506 0
6 0 0 0 2507
7 0 0 2508 0
8 0 0 2509 0
9 0 0 2510 0
........$......**$.....**.$.......*$........$........$........$........$
4 0 0 0 2512
5 0 0 2513 0
6 0 0 0 2514
7 0 0 2515 0
8 0 2516 0 0
4 0 0 2512 0
5 0 0 2518 0
6 0 0 0 2519
7 0 0 2520 0
8 0 0 2521 0
........$......**$......*.$......*.$........$........$........$........$
........$........$*.......$........$........$........$........$........$
4 0 0 2523 2524
5 0 0 0 2525
6 0 0 0 2526
7 0 0 2527 0
8 0 2528 0 0
9 2517 2522 0 2529
10 2504 2511 0 2530
9 0 0 2522 0
10 0 0 2532 0
11 2493 0 2531 2533
12 2452 0 2487 2534
........$.......*$........$........$........$........$........$........$
**......$*.......$.*......$........$........$........$........$........$
4 0 0 2536 2537
5 0 0 2538 0
6 0 0 0 2539
7 0 0 2540 0
8 0 2541 0 0
6 0 0 0 62
7 0 0 2543 0
8 0 0 2544 0
.*......$**......$*.*.....$........$........$........$........$........$
4 0 0 2546 0
5 0 0 0 2547
6 0 0 0 2548
7 0 0 2549 0
8 0 2550 0 0
9 2542 2545 0 2551
.....*..$....**..$....*.*.$........$........$........$........$........$
4 0 0 2553 0
5 0 0 0 2554
6 0 0 0 2555
7 0 0 2556 0
8 0 0 2557 0
9 0 0 2558 0
4 0 0 0 455
5 0 0 0 2560
6 0 0 0 2561
4 0 0 428 0
5 0 0 2563 0
6 0 0 2564 0
7 0 0 2562 2565
8 0 2566 0 0
4 0 0 455 428
5 0 0 0 2568
6 0 0 0 2569
7 0 0 2570 0
8 0 0 2571 0
........$...***..$...*....$....*...$........$........$........$........$
4 0 0 0 2573
5 0 0 0 2574
6 0 0 0 2575
7 0 0 2576 0
8 0 2577 0 0
9 2567 2572 0 2578
10 2552 2559 0 2579
11 0 2580 0 0
12 0 2581 0 0
........$.......*$.......*$........$........$........$........$........$
4 0 0 0 2583
*.......$*.......$.*......$........$........$........$........$........$
4 0 0 2585 0
5 0 0 2584 2586
6 0 0 0 2587
7 0 0 2588 0
8 0 0 2589 0
9 0 0 2590 0
10 0 0 2591 0
5 0 0 2560 2563
6 0 0 0 2593
7 0 0 2594 0
8 0 2595 0 0
6 0 0 2593 0
7 0 0 2597 0
8 0 0 2598 0
........$**......$........$*.......$........$........$........$........$
4 0 0 2583 2600
5 0 0 2601 0
6 0 0 2602 0
7 0 0 2603 0
8 0 2604 0 0
9 2596 2599 0 2605
4 0 0 2573 0
5 0 0 0 2607
6 0 0 2608 0
7 0 0 2609 0
8 0 0 2610 0
9 0 0 2611 0
........$...**...$..**....$....*...$........$........$........$........$
4 0 0 0 2613
5 0 0 0 2614
6 0 0 2615 0
7 0 0 2616 0
8 0 2617 0 0
........$....***.$....*...$.....*..$........$........$........$........$
4 0 0 2619 0
5 0 0 0 2620
6 0 0 2621 0
7 0 0 2622 0
8 0 0 2623 0
5 0 0 61 0
6 0 0 2625 0
7 0 0 2626 0
8 0 2627 0 0
9 2618 2624 0 2628
10 2606 2612 0 2629
........$.......*$......**$........$........$........$........$........$
........$*.......$........$*.......$........$........$........$........$
4 0 0 2631 2632
5 0 0 0 2633
6 0 0 2634 0
7 0 0 2635 0
8 0 0 2636 0
9 0 0 2637 0
10 0 0 2638 0
11 2592 0 2630 2639
4 0 0 449 0
5 0 0 0 2641
6 0 0 2642 0
7 0 0 2643 0
8 0 2644 0 0
5 0 0 2641 0
6 0 0 2646 0
7 0 0 2647 0
8 0 0 2648 0
........$..**....$..*.*...$..*.....$........$........$........$........$
4 0 0 2650 0
5 0 0 2651 0
6 0 0 2652 0
7 0 0 2653 0
8 0 2654 0 0
9 2645 2649 0 2655
4 0 0 208 0
5 0 0 2657 0
6 0 0 2658 0
7 0 0 2659 0
8 0 0 2660 0
9 0 0 2661 0
........$.*......$**......$*.*.....$........$........$........$........$
4 0 0 2663 0
5 0 0 2664 0
6 0 0 2665 0
7 0 0 2666 0
8 0 2667 0 0
4 0 0 0 2553
5 0 0 2669 0
6 0 0 2670 0
7 0 0 2671 0
8 0 0 2672 0
5 0 0 0 2657
6 0 0 2674 0
7 0 0 2675 0
8 0 2676 0 0
9 2668 2673 0 2677
10 2656 2662 0 2678
11 0 2679 0 0
...*....$..**....$..*.*...$........$........$........$........$........$
4 0 0 0 2681
5 0 0 0 2682
6 0 0 2683 0
7 0 0 2684 0
8 0 0 2685 0
9 0 0 2686 0
10 0 0 2687 0
8 0 2685 0 0
4 0 0 2681 0
5 0 0 0 2690
6 0 0 2691 0
7 0 0 2692 0
8 0 0 2693 0
........$.......*$......**$......*.$........$........$........$........$
4 0 0 2695 345
5 0 0 2696 0
6 0 0 0 2697
7 0 0 2698 0
8 0 2699 0 0
9 2689 2694 0 2700
........$..*.....$.**.....$.*.*....$........$........$........$........$
4 0 0 0 2702
5 0 0 2703 0
6 0 0 0 2704
7 0 0 2705 0
8 0 0 2706 0
9 0 0 2707 0
........$.....*..$....**..$....*.*.$........$........$........$........$
4 0 0 2709 0
5 0 0 2710 0
6 0 0 0 2711
7 0 0 2712 0
8 0 2713 0 0
........$........$....***.$....*...$.....*..$........$........$........$
4 0 0 2715 0
5 0 0 2716 0
6 0 0 0 2717
7 0 0 2718 0
8 0 0 2719 0
........$......*.$.....**.$.....*.*$........$........$........$........$
4 0 0 0 2721
5 0 0 0 2722
6 0 0 2723 0
7 0 0 2724 0
8 0 2725 0 0
9 2714 2720 0 2726
10 2701 2708 0 2727
........$........$...***..$...*....$....*...$........$........$........$
4 0 0 2729 0
5 0 0 0 2730
6 0 0 0 2731
7 0 0 2732 0
8 0 0 2733 0
9 0 0 2734 0
10 0 0 2735 0
11 2688 0 2728 2736
12 2640 0 2680 2737
13 2535 0 2582 2738
........$........$......**$......*.$.......*$........$........$........$
4 0 0 2740 2524
5 0 0 2741 0
6 0 0 0 2742
7 0 0 2743 0
8 0 2744 0 0
........$........$.***....$.*......$..*.....$........$........$........$
4 0 0 0 2746
5 0 0 0 2747
6 0 0 0 2748
7 0 0 2749 0
8 0 0 2750 0
4 0 0 0 2729
5 0 0 2752 0
6 0 0 0 2753
7 0 0 2754 0
8 0 2755 0 0
9 2745 2751 0 2756
4 0 0 0 386
5 0 0 0 2758
6 0 0 2759 0
7 0 0 2760 0
8 0 0 2761 0
9 0 0 2762 0
6 0 0 0 2646
7 0 0 2764 0
8 0 2765 0 0
5 0 0 0 2601
6 0 0 2767 0
7 0 0 2768 0
8 0 0 2769 0
9 2766 2770 0 0
10 2757 2763 0 2771
11 0 2772 0 0
12 0 2773 0 0
13 0 2774 0 0
14 2409 2739 0 2775
4 0 2349 0 0
5 0 2777 0 0
4 2351 0 0 0
5 2779 0 0 0
6 2778 2780 0 0
7 2781 0 0 0
5 2386 0 0 0
6 0 2783 0 0
7 2784 0 0 0
8 2782 0 0 2785
5 0 2277 0 0
6 2787 0 0 0
7 2788 0 0 0
6 2783 0 0 0
7 2790 0 0 0
8 2789 0 0 2791
9 2786 0 0 2792
4 0 0 0 512
5 0 2794 0 0
6 0 0 0 2795
4 26 0 43 0
5 2797 0 0 0
6 0 0 2798 0
7 0 0 2796 2799
8 0 0 2800 0
........$........$........$........$........$........$........$..*.....$
.**.....$.*.*....$........$........$........$........$........$........$
4 0 2802 0 2803
5 2804 0 0 0
6 0 0 2805 0
7 0 0 0 2806
8 0 2807 0 0
9 0 2801 0 2808
10 2793 0 0 2809
........$........$........$........$........$........$........$.....*..$
....**..$....*.*.$........$........$........$........$........$........$
4 2811 0 2812 0
5 2813 0 0 0
6 0 0 2814 0
7 0 0 0 2815
8 0 0 2816 0
9 0 0 2817 0
10 0 0 2818 0
11 0 0 2810 2819
8 0 2800 0 0
......*.$......*.$........$........$........$........$........$........$
4 25 0 2822 101
5 2823 0 0 0
6 0 0 2824 0
7 0 0 0 2825
8 0 0 2826 0
.....*.*$.....*..$........$........$........$........$........$........$
4 0 1 0 2828
5 2829 0 0 0
6 0 0 2830 0
7 0 0 0 2831
8 0 2832 0 0
9 2821 2827 0 2833
........$........$........$.**.....$.*.*....$.*......$........$........$
4 0 2835 0 0
5 2836 0 0 0
6 0 0 2837 0
7 0 0 0 2838
8 0 0 2839 0
.**.....$.*.*....$.*......$........$........$........$........$........$
4 0 0 0 2841
5 2842 0 0 0
6 0 0 2843 0
7 0 0 0 2844
8 0 2845 0 0
9 0 2840 0 2846
10 2834 0 0 2847
11 0 2848 0 0
........$........$........$........$.**.....$.*.*....$.*......$........$
4 0 2850 0 0
5 0 0 2851 0
6 2852 0 0 0
7 0 0 0 2853
8 0 0 2854 0
9 0 0 2855 0
10 0 0 2856 0
........$.**.....$**......$..*.....$........$........$........$........$
4 0 0 0 2858
5 0 0 2859 0
6 2860 0 0 0
7 0 0 0 2861
8 0 2862 0 0
4 0 0 0 119
4 0 110 0 0
5 2864 0 2865 0
6 0 0 2866 0
7 0 0 0 2867
8 0 0 2868 0
8 0 2854 0 0
9 2863 2869 0 2870
........$.**.....$.*.*....$.*......$........$........$........$........$
4 0 2872 0 0
5 0 0 2873 0
6 0 0 2874 0
7 0 0 0 2875
8 0 0 2876 0
9 0 0 2877 0
10 2871 2878 0 0
4 425 51 52 428
5 0 0 2880 0
6 0 0 2881 0
7 0 0 0 2882
8 0 0 2883 0
9 0 0 2884 0
10 0 0 2885 0
11 2857 0 2879 2886
12 2820 0 2849 2887
........$........$........$........$........$........$........$.....***$
.....*..$......*.$........$........$........$........$........$........$
4 0 2889 0 2890
5 0 0 0 2891
6 0 0 0 2892
7 0 0 2893 0
8 0 2894 0 0
6 0 0 2892 0
7 0 0 0 2896
8 0 0 2897 0
9 2895 2898 0 0
4 0 318 0 0
5 2900 0 0 0
6 2901 0 0 0
7 0 2902 0 0
4 0 2468 0 0
5 2904 0 0 0
6 2905 0 0 0
7 0 2906 0 0
8 2903 0 0 2907
9 0 0 0 2908
10 2899 0 0 2909
11 0 2910 0 0
12 0 2911 0 0
........$........$......*.$.....**.$.....*.*$........$........$........$
4 0 2913 0 0
5 2914 0 0 0
6 2915 0 0 0
7 0 2916 0 0
8 2917 0 0 0
9 2918 0 0 0
5 0 0 2386 0
6 2920 0 0 0
7 0 0 0 2921
8 0 0 2922 0
9 0 0 2923 0
4 0 2269 0 0
5 0 0 2925 0
6 2926 0 0 0
7 0 0 0 2927
8 0 2928 0 0
8 0 0 2928 0
........$........$........$...*....$..**....$..*.*...$........$........$
4 0 2931 0 0
5 0 0 2932 0
6 2933 0 0 0
7 0 0 0 2934
8 0 2935 0 0
9 2929 2930 0 2936
10 2919 2924 0 2937
5 0 0 2777 2779
6 2939 0 0 0
7 0 0 0 2940
8 0 0 2941 0
9 0 0 2942 0
10 0 0 2943 0
11 0 0 2938 2944
5 0 0 2277 0
6 2946 0 0 0
7 0 0 0 2947
8 0 2948 0 0
4 2349 2351 0 0
5 0 0 0 2950
6 2951 0 0 0
7 0 0 0 2952
8 0 0 2953 0
9 2949 2954 0 0
4 0 2721 0 0
5 0 2956 0 0
6 0 0 0 2957
7 0 0 2958 0
8 0 0 2959 0
4 2702 0 0 0
5 2961 0 0 0
6 0 0 2962 0
7 0 0 0 2963
8 0 2964 0 0
9 0 2960 0 2965
10 2955 0 0 2966
11 0 2967 0 0
4 2721 0 0 0
5 2969 0 0 0
6 0 0 2970 0
7 0 0 0 2971
8 0 0 2972 0
9 0 0 2973 0
10 0 0 2974 0
4 2709 0 0 0
5 2976 0 0 0
6 0 0 2977 0
7 0 0 0 2978
8 0 2979 0 0
........$...*....$..**....$..*.*...$........$........$........$........$
4 2981 0 0 0
5 2982 0 0 0
6 0 0 2983 0
7 0 0 0 2984
8 0 0 2985 0
4 0 2650 0 0
5 0 2987 0 0
6 0 0 0 2988
7 0 0 2989 0
8 0 2990 0 0
9 2980 2986 0 2991
........$.......*$.......*$.......*$........$........$........$........$
4 0 2993 0 0
5 0 2994 0 0
6 0 0 0 2995
........$*.......$.*......$........$........$........$........$........$
4 2997 0 0 0
5 2998 0 0 0
6 0 0 2999 0
7 0 0 2996 3000
8 0 0 3001 0
9 0 0 3002 0
........$*.......$*.......$.*......$........$........$........$........$
4 399 3004 0 0
5 3005 0 0 0
6 0 0 3006 0
7 0 0 0 3007
8 0 3008 0 0
4 2650 0 0 0
5 0 3010 0 0
6 0 0 0 3011
7 0 0 3012 0
8 0 0 3013 0
9 3009 3014 0 2311
10 2992 3003 0 3015
11 2975 0 3016 0
12 2945 0 2968 3017
13 2888 0 2912 3018
14 0 0 3019 0
15 2374 0 2776 3020
5 0 0 0 456
6 3022 0 0 0
7 0 3023 0 0
4 0 449 0 0
5 0 0 0 3025
6 3026 0 0 0
7 0 3027 0 0
8 3024 0 0 3028
9 0 0 0 3029
4 2619 0 0 0
5 0 0 0 3031
6 3032 0 0 0
7 0 3033 0 0
8 3034 0 0 3028
5 0 0 450 0
6 0 3036 0 0
7 0 3037 0 0
4 2681 0 0 0
5 0 0 0 3039
6 3040 0 0 0
7 0 3041 0 0
8 3038 0 0 3042
9 3035 0 0 3043
10 3030 0 0 3044
11 0 3045 0 0
12 0 3046 0 0
13 0 3047 0 0
6 0 3032 0 0
7 0 3049 0 0
8 3050 0 0 0
9 3051 0 0 0
........$........$......**$......*.$......*.$........$........$........$
4 0 0 0 3053
5 0 0 0 3054
4 0 0 345 0
5 0 0 3056 0
6 0 0 3055 3057
7 0 0 0 3058
8 0 0 3059 0
9 0 0 3060 0
8 0 3059 0 0
........$........$........$........$........$........$......**$.....**.$
4 0 3063 0 52
5 0 3064 0 0
6 3065 0 0 0
7 0 3066 0 0
8 3067 3059 0 0
9 3062 0 0 3068
10 3052 3061 0 3069
........$........$........$........$........$........$......**$......*.$
4 0 3071 0 281
5 0 0 0 3072
4 26 0 0 0
5 0 0 3074 0
6 0 0 3073 3075
7 0 0 0 3076
8 0 0 3077 0
9 0 0 3078 0
10 0 0 3079 0
11 0 0 3070 3080
........$........$........$......**$......*.$......*.$........$........$
4 0 0 0 3082
5 0 0 0 3083
4 0 0 251 0
5 0 0 3085 0
6 0 0 3084 3086
7 0 0 0 3087
8 0 3088 0 0
4 0 0 0 3071
5 0 0 0 3090
4 0 0 26 0
5 0 0 3092 0
6 0 0 3091 3093
7 0 0 0 3094
8 0 0 3095 0
4 0 281 0 0
5 0 3097 0 0
6 3098 0 0 0
7 0 3099 0 0
8 3100 3059 0 0
9 3089 3096 0 3101
5 0 2747 0 0
6 3103 0 0 0
7 0 3104 0 0
4 0 0 0 2715
5 0 3106 0 0
6 3107 0 0 0
7 0 3108 0 0
8 3105 0 0 3109
9 0 0 0 3110
10 3102 0 0 3111
11 0 3112 0 0
4 0 0 399 401
5 0 3114 0 0
6 3115 0 0 0
7 0 3116 0 0
8 3109 0 0 3117
4 0 0 0 2663
5 0 3119 0 0
6 3120 0 0 0
7 0 3121 0 0
4 0 0 0 2709
5 0 3123 0 0
6 3124 0 0 0
7 0 3125 0 0
8 3122 0 0 3126
9 3118 0 0 3127
8 3109 0 0 0
9 3129 0 0 0
10 3128 0 0 3130
11 0 0 3131 0
12 3081 0 3113 3132
....*...$...**...$...*.*..$........$........$........$........$........$
4 0 0 0 3134
5 0 3135 0 0
6 3136 0 0 0
7 0 3137 0 0
5 0 2584 0 0
5 2586 0 0 0
6 3139 3140 0 0
7 0 3141 0 0
8 3138 0 0 3142
4 0 0 0 2546
5 3144 0 0 0
6 0 3145 0 0
7 0 3146 0 0
4 0 0 2841 0
5 3148 0 0 0
6 0 3149 0 0
7 0 3150 0 0
8 3147 0 0 3151
9 3143 0 0 3152
...**...$...*.*..$...*....$........$........$........$........$........$
4 0 0 0 3154
5 0 3155 0 0
6 3156 0 0 0
7 0 3157 0 0
5 0 2657 0 0
6 3159 0 0 0
7 0 3160 0 0
8 3158 0 0 3161
9 3162 0 0 0
10 3153 0 0 3163
11 0 3164 0 0
12 0 3165 0 0
........$........$........$........$........$......**$......*.$.......*$
4 0 3167 0 0
5 0 0 0 3168
........$........$........$........$........$*.......$........$........$
4 3170 0 0 0
5 0 0 3171 0
6 3169 3172 0 0
7 0 3173 0 0
4 0 152 0 0
5 0 0 0 3175
6 3176 0 0 0
7 0 3177 0 0
8 3174 0 0 3178
........$........$........$........$.*......$**......$*.*.....$........$
4 0 3180 0 0
5 0 0 3181 0
6 0 3182 0 0
7 0 3183 0 0
........$........$........$........$......*.$.....**.$.....*.*$........$
4 3185 0 0 0
5 0 0 0 3186
6 3187 0 0 0
7 0 3188 0 0
8 3184 0 0 3189
9 3179 0 0 3190
4 158 0 0 0
5 0 0 3192 0
6 0 3193 0 0
7 0 3194 0 0
4 0 4 0 0
........$........$........$........$........$**......$........$*.......$
4 3197 0 0 0
5 0 0 3196 3198
6 0 3199 0 0
7 0 3200 0 0
8 3195 0 0 3201
9 3202 0 0 0
10 3191 0 0 3203
11 0 0 3204 0
4 0 0 3071 2246
5 0 0 0 3206
4 52 0 0 0
5 0 3208 0 0
6 3207 0 3209 0
7 0 3210 0 0
........$........$........$........$........$........$...***..$...*....$
4 0 0 3212 0
5 0 0 0 3213
6 3214 0 2298 0
7 0 3215 0 0
8 3211 0 0 3216
........$........$........$........$........$.*......$**......$*.*.....$
4 0 0 3218 0
5 0 0 3219 0
6 0 3220 0 0
7 0 3221 0 0
4 0 0 0 107
5 0 0 0 3223
5 0 2865 0 0
6 3224 0 3225 0
7 0 3226 0 0
8 3222 0 0 3227
9 3217 0 0 3228
5 0 0 3223 0
5 2865 0 0 0
6 3230 0 3231 0
7 0 3232 0 0
........$........$........$........$........$........$***.....$*.......$
4 0 0 0 3234
5 0 0 3235 0
5 485 0 0 0
6 3236 0 3237 0
7 0 3238 0 0
8 3233 0 0 3239
8 3227 0 0 0
9 3240 0 0 3241
10 3229 0 0 3242
11 0 3243 0 0
4 0 455 0 0
5 0 3245 0 0
4 428 0 0 0
5 3247 0 0 0
6 3246 3248 0 0
7 0 3249 0 0
8 3250 0 0 0
9 0 0 0 3251
4 0 0 4 3197
5 3253 0 0 0
6 0 0 0 3254
7 0 3255 0 0
5 153 0 0 0
6 0 0 0 3257
7 0 3258 0 0
8 3256 0 0 3259
9 0 0 0 3260
10 3252 0 0 3261
11 0 0 3262 0
12 3205 0 3244 3263
13 3133 0 3166 3264
........$........$........$........$........$.....***$.....*..$......*.$
4 0 0 3266 0
5 3267 0 0 0
6 0 0 0 3268
7 0 3269 0 0
5 159 0 0 0
6 0 0 0 3271
7 0 3272 0 0
8 3270 0 0 3273
9 3274 0 0 0
5 496 0 0 0
6 0 3276 0 0
7 3277 0 0 0
........$........$........$.......*$........$........$........$........$
........$........$**......$*.......$.*......$........$........$........$
4 0 0 3279 3280
5 3281 0 0 0
6 0 3282 0 0
7 3283 0 0 0
8 3278 0 0 3284
4 399 401 0 0
5 0 0 3286 0
6 0 3287 0 0
7 3288 0 0 0
5 68 0 0 0
6 0 3290 0 0
7 3291 0 0 0
8 3289 0 0 3292
9 3285 0 0 3293
10 3275 0 0 3294
11 0 3295 0 0
12 0 3296 0 0
13 0 3297 0 0
14 3048 3265 0 3298
15 0 3299 0 0
5 2601 0 0 0
6 0 3301 0 0
7 3302 0 0 0
4 0 0 310 312
5 3304 0 0 0
6 0 3305 0 0
7 3306 0 0 0
8 3303 0 0 3307
5 3114 0 0 0
6 0 3309 0 0
7 3310 0 0 0
8 3311 0 0 0
9 3308 0 0 3312
4 0 2320 0 0
5 0 3314 0 0
6 0 3315 0 0
7 3316 0 0 0
4 0 0 0 2320
5 0 3318 0 0
6 0 3319 0 0
7 3320 0 0 0
8 3317 0 0 3321
9 0 0 0 3322
10 3313 0 0 3323
........$........$........$........$........$........$........$....**..$
4 0 0 0 3325
5 0 0 0 3326
6 0 0 0 3327
7 0 0 3328 0
8 0 0 3329 0
9 0 0 3330 0
10 0 0 3331 0
11 0 0 3324 3332
...**...$.....*..$........$........$........$........$........$........$
4 0 3334 0 0
5 0 3335 0 0
6 0 3336 0 0
7 3337 0 0 0
4 0 2453 0 0
5 0 3339 0 0
6 0 3340 0 0
7 3341 0 0 0
8 3338 0 0 3342
...***..$...*....$....*...$........$........$........$........$........$
4 0 3344 0 0
5 0 3345 0 0
6 0 3346 0 0
7 3347 0 0 0
5 0 0 0 3318
6 0 0 0 3349
7 0 0 3350 0
8 3348 3351 0 0
9 3343 0 0 3352
4 0 3212 0 2296
5 0 0 0 3354
6 0 0 0 3355
7 0 0 3356 0
8 0 0 3357 0
9 0 0 3358 0
5 0 3175 0 0
6 0 3360 0 0
7 3361 0 0 0
8 0 0 0 3362
4 0 0 0 2313
5 0 0 0 3364
6 0 0 0 3365
7 0 0 3366 0
8 0 0 3367 0
5 0 0 0 3314
6 0 0 0 3369
7 0 0 3370 0
8 0 3371 0 0
9 3363 3368 0 3372
10 3353 3359 0 3373
11 0 3374 0 0
6 0 2742 0 0
7 0 3376 0 0
5 0 0 2730 0
6 0 3378 0 0
7 0 3379 0 0
8 3377 0 0 3380
9 0 0 0 3381
4 0 0 399 3004
5 0 0 3383 0
6 0 3384 0 0
7 0 3385 0 0
8 3386 0 0 0
9 3387 0 0 0
10 3382 0 0 3388
11 0 0 3389 0
12 3333 0 3375 3390
4 0 0 0 2459
5 0 3392 0 0
6 3393 0 0 0
7 3394 0 0 0
4 0 0 318 0
5 0 3396 0 0
6 3397 0 0 0
7 3398 0 0 0
8 3395 0 0 3399
6 2455 0 0 0
7 3401 0 0 0
6 0 2476 0 0
7 3403 0 0 0
8 3402 0 0 3404
9 3400 0 0 3405
5 0 2480 0 0
6 3407 0 0 0
7 3408 0 0 0
5 2499 0 0 0
6 0 3410 0 0
7 3411 0 0 0
8 3409 0 0 3412
........$........$..*.....$.**.....$.*.*....$........$........$........$
4 0 0 0 3414
5 3415 0 0 0
6 3416 0 0 0
7 3417 0 0 0
8 3418 0 0 0
9 3413 0 0 3419
10 3406 0 0 3420
11 0 3421 0 0
12 0 3422 0 0
........$........$........$........$........$........$......*.$.....**.$
.....*.*$........$........$........$........$........$........$........$
4 0 3424 0 3425
5 0 0 3426 0
6 3427 0 0 0
7 3428 0 0 0
........$........$........$........$........$........$...*....$..**....$
..*.*...$........$........$........$........$........$........$........$
4 0 3430 0 3431
5 0 0 3432 0
6 3433 0 0 0
7 3434 0 0 0
8 3429 0 0 3435
........$........$........$........$........$........$*.......$*.......$
4 425 3437 52 484
5 0 0 3438 0
6 3439 0 0 0
7 3440 0 0 0
........$........$........$........$........$........$.....*..$....**..$
....*.*.$........$........$........$........$........$........$........$
4 0 3442 0 3443
5 0 0 3444 0
6 3445 0 0 0
7 3446 0 0 0
8 3441 0 0 3447
9 3436 0 0 3448
........$........$........$........$........$........$.*......$**......$
*.*.....$........$........$........$........$........$........$........$
4 0 3450 0 3451
5 0 0 3452 0
6 3453 0 0 0
7 3454 0 0 0
........$........$........$........$........$........$.**.....$.*.*....$
4 0 3456 0 484
5 0 0 0 3457
6 3458 0 0 0
7 3459 0 0 0
8 3455 0 0 3460
........$........$........$........$........$........$.....**.$.....*.*$
.....*..$........$........$........$........$........$........$........$
4 3462 0 3463 0
5 0 0 0 3464
6 3465 0 0 0
7 3466 0 0 0
4 0 97 0 52
........$........$........$........$........$........$*.......$.*......$
4 3469 0 0 0
5 0 0 3468 3470
6 3471 0 0 0
7 3472 0 0 0
8 3467 0 0 3473
9 3461 0 0 3474
10 3449 0 0 3475
11 0 0 3476 0
5 0 0 0 616
6 3478 0 0 0
7 3479 0 0 0
8 3480 0 0 0
9 3481 0 0 0
........$........$........$........$........$.....**.$....**..$......*.$
4 0 3483 0 0
5 0 0 3484 0
6 3485 0 0 0
7 3486 0 0 0
4 0 278 0 281
5 0 0 3488 0
6 3489 0 0 0
7 3490 0 0 0
8 3487 0 0 3491
7 2947 0 0 0
4 0 0 0 2276
5 0 0 3494 0
6 3495 0 0 0
7 3496 0 0 0
8 3493 0 0 3497
9 3492 0 0 3498
10 3482 0 0 3499
11 0 3500 0 0
4 0 2619 0 0
5 0 0 3502 0
6 3503 0 0 0
7 3504 0 0 0
........$........$........$........$........$........$........$....***.$
....*...$.....*..$........$........$........$........$........$........$
4 0 3506 0 3507
5 3508 0 0 0
6 3509 0 0 0
7 3510 0 0 0
8 3505 0 0 3511
9 3512 0 0 0
4 0 0 512 513
5 0 0 3514 0
6 3515 0 0 0
7 3516 0 0 0
8 3517 0 0 3517
5 0 0 2804 0
6 3519 0 0 0
7 3520 0 0 0
4 0 0 513 0
5 0 0 2794 3522
6 3523 0 0 0
7 3524 0 0 0
8 3521 0 0 3525
9 3518 0 0 3526
10 3513 0 0 3527
11 0 0 3528 0
12 3477 0 3501 3529
13 3391 0 3423 3530
14 0 0 3531 0
........$........$........$........$........$........$........$......*.$
.....**.$.....*.*$........$........$........$........$........$........$
4 3533 0 3534 0
5 0 0 0 3535
6 3536 0 0 0
7 3537 0 0 0
4 0 26 512 43
5 0 0 0 3539
6 3540 0 0 0
7 3541 0 0 0
8 3538 0 0 3542
....***.$....*...$.....*..$........$........$........$........$........$
4 0 0 0 3544
5 0 0 3545 0
6 3546 0 0 0
7 3547 0 0 0
5 0 0 0 3514
6 0 3549 0 0
7 0 3550 0 0
8 3548 0 3551 0
9 3543 0 0 3552
10 3553 0 0 0
11 0 3554 0 0
12 0 3555 0 0
13 0 3556 0 0
14 3557 0 0 0
15 3532 0 3558 0
16 3021 0 3300 3559
17 3560 0 0 0
18 1707 0 1709 3561
19 3562 0 0 0
20 3563 0 0 0
21 1881 0 1885 3564
22 3565 0 0 0
23 3566 0 0 0
24 3567 0 0 0
25 2222 0 2226 3568
26 3569 0 0 0
27 2183 0 2190 3570
28 0 0 342 3571
Among 10 starting patterns with one block moved 1fd each time the salvo sizes differ by upto 128 gliders ... not counting the infinite soluion mentioned above. I bet it triggers totally different decompositions rather to slightly different recepies for the block.

Hmm, I cannot pretend I am not thinking about the process speedout. So far, I have defragmentation be a dummy process and and I have removed creating "perp" outputs. It seems perp "inputs" are either p1 or p2, but it does not work with p8 well anyways (linespec need 0..7 rather to EO, cocncat specification requires options 3-10) it seems to me concat has wrong order of streams to work well with desirate, but I can be wrong.
It seemed to me exportgouts could be helpfull, but p2/p8 issue prevented me to work on it (use it). The output.mc actually does not construct the pattern... I am not sure if it is just the p2/p8 issue or I am right with the concat.

I probably understand better and better the whole slsparse so maybe I will try my five cents in using the recepies and their conditioning, but traversing the implicit graph another way. Of course having alternatives in mind. I hope some information could be maintained among preiterate calls to precurse and neednot be exracted from the pattern each time. I hope the alternatives would be helpfull in guiding the "tree" strategy.
Too many alteratives would slow the search down, but shortening the tree branches could compensate it (a bit). I hope it will not be just a waste of time to work on it.

After positioning block on DBCA arm I got to 8800 gliders working recipe. Than I start experimented with south zigzag of reflectors and first position lead to 8686 gliders working recipe. I am still experimenting with its placement, but best of next 44 atempts given just 8694 gliders.
(I have just automated the slsparse invocation and result processing ... I have 16 places where parametrization could be changed so these experiments would take about a month ... so I will probably get plenty of time to experiment with slsparse).

I had added some debug outputs to slmake, and it seems to me for wide patterns it spends a lot of time finding first dimmer a rule can be applied to.
It is very efficient in finding split recepies, what is what should be invited. First "search change" I am going to make is to add a parameter ... where the last reduction appeared ... index of the dimmer. And I would "start search" from this position. I probably would add first round (for the case last reduction was split or bespoke) where only bespoke and split recepies would apply to hope to make efficient recepies. (First change could lead to speedup while the other probably to slow down).

Hmm, the 8686 is the best of all 80 attempts in given direction. And my version with sharing information among different runs of precurse still does not work. Hmm ... the updated program does something ... it is very good in finding splits fast, but the longmoves in the tree strategy ... I know at least about one bug. It could improve things.

OK, finally I got why saving dimers from previous precurse call is nonsense ... I have not understand that all nearby pairs are used, I have tought each monomer is presented just once (I like monomers penalisation ...). Maybe one could maintain clusters and recompute dimers based on the cluster change ... removing corresponding edges and adding edges to the new cluster that would require maintaining set of current cluster edges.
I bet it is not that much important as this time is majorised by tries of a lot of recipes. It could be worth trying to know what is first position on list worth to try again, but further clusters could prevent nearer clusters due to reaction envelope so this probably could not help.

It is hard to find a place for improvement ... maybe the spanning tree computaion? Would not Delanau triangulation be better starting graph? ... Oh isn't that what the sweeping does? It could be important as block repositioning importance is computed often. Maybe tree strategy should consider ("front line of" convex hull of remainder) as well for small number of blocks to prefere moving blocks forward, but ordering of dimmers probably cures such problems.

... and adding variant clusters would cause problems to dimerise or at least really complicate selection of recipes ... probably.

OK, I have swapped order of loops to prefere shorter salvos processing the clusters (as the cluster is among several dimers ... chosing first dimer neednot be the best choice). Let us see wha happens. ... hmm I have to penalize monomers, especially those built through 252 or 696.

Do I understand it well the increased bailout only changes block monomers processing? So trying other dimers is waste of time in such phases?
The budget used in state 0, rest in state 1 with small bailout, but processing the rest again with biger bailout is unnecessary. ...
adding if (initbail > 1) {continue;} above the budget cut test.

Hmm, again not only that the computation was much slower (not fully optimized yet), but the recipe was longer. The parameters are really well tuned... I am ready to resign in trying to find an improvement to slmake ... but I still have ideas to try ... the chunking strategy ... I would probably try to work with a bit biger chunks, but I would not finish precurse with a single block except the depth 0 (I would prefere sending cut edges on chunk/reminder boundary of spanning tree as parameters to recursion and consider them as "sources" for chunk MST used in tree srategy evaluation ... ending with number of blocks less or equal the number of these external edges). I would also limit the dimmers tested to those with x+y (+penalty) (sorting criteria) close to the same of the first dimmer. I would probably encouraged bespoke objects by negative penalty rather to putting them all to front.

I am thinking about replacing variants in the pattern at preiterate (with x+y in "active range") it could result in creating dimmers which would be easier to construct ... it requires the quick monomers processing is prevented out (by the penalties?) (may be for variant monomers the penalty should be increased (except at the end of variants cycle) ... it still requires dimerise should be able to detect a variant:().

Hmm ... I have implemented some form of variant search (cycling among variants in preiterate) ... and I got slightly worse result comparing to the record one (without the variant coosing) ... the tree strategy does not look optimal ...

... and this variant changing is rather risky ... to build a part of recepie in one variant and continue in another ... already built parts of pattern could prevent/force some variants ... so probably another death end ...

I am still using essencially the original slsparse, current record (tested) is 8568 gliders recipe.

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » March 17th, 2023, 1:36 pm

OK, finally I made a working slmake.h incorporating "modified tree strategy". First run took about 2/3 time of the previous version and it got 8348 gliders. (Previous record from some hundreds of tried configuraions was 8568).
(I took another path then variant search ...) ... I am used to prevent recursion whenever possible so I made some stack of variables ... and I let the code in recursive form, but using these ... it would definitely look differently if codded from scratch.

I definitely do not say it is well tested, but it definitely has potential:).

2nd. run took just 35 minues (and got 8442), (previous version times were 60, 54, 74, 89, 222, 61, 69, 66, 81, 225, 63, 63, 62, 64, ... minutes)

The parameters still require a bit of tuning. I have interrupted 3.rd run as "maxDepth should not increase so fast with bailout" ... problematic situation lead to finding a step further forward not solving the issue ... I have changed it to (150 + deep_bailout * 10). And the problematic situation was solved without so long "procrastination". ... Let us see what happens ... 8442. ... I am not sure the maxDepth should be increased at all. If we let it fixed, dimerise is not needed for initbail>1 and more importantly nonblock processing could be skipped for initbail>1. (Oh, there is wrong condition to skip processing dimers in state 1 ... initbail starts on 1 not 0...)

4th 8359 in 27 minutes.
5th 8288 in 44 minutes.

Seems after (150 + deep_bailout * 10) change the running times are in 25-45 minutes range so it looks like this version managed to "double the speed" while making smaller salvos.

Times in following tables are slightly affected by me watching you tube/other videos on the comp during the computation ... and I sometimes run two versions of slmake at the same time ... but one version consumes about 20% of CPU resources, so I hope it is fine :).

Size/time ... orig-new ... maxDepth fixed150 ... max(7*k/2,12) rather 15
8686/61 ... 8346/25 ...
8718/54 ... 8346/26 ...
8568/74 ... 8312/<31 ...
8738/89 ... 8315/38 ...
8867/222 ... 8347/27 ...
8674/61 ... 8372/39 ...
8691/69 ... 8310/38 ... 8363/36
8589/66 ... 8344/33 ...
8692/81 ... 8311/43
8789/225 ... 8312/35
8616/63 ... 8395/66 ???
8616/63 ... ... 8326/51
8654/64 ... ... 8386/27
8593/64... ... 8436/34 ... 8276/<37?
8753/176 ... ... ... 8433/?
8737/104 ... ... ... /
8747/74 ... ... ... /

OK I am going to experiment with tree strategy more ... rather to wanting 15 improvement I want 4*k improvement. I hope to get even smaller recepies. (I have added "utilhash" to compute "util" easier way) ... and 4*k seems to be too restricted, I have reduced it to 7k/2. But it does too small moves ... so I have changed it to max(7k/2,12).

Situation is not that easy :(.
It could happen the greedy algorithm reaches position which it cannot solve. There were 3 bespoke objects among active dimers/monomers and the algorithm went to an infinite loop. This is why I have returned to version which increases maxdepth (slowly). This version went through the infile in about 2 hours, but it could be it was just lucky to avoid the position. (Increasing minchunkpop was not helpfull at all (I would probably change preiterate to stick in minpop 8).)

I will return to experiments with parametrisation ... may be the depth 150 does not allow to remove dimers which block bespoke processing from behind and increasing the depth to slightly bigger constant would solve the problem while allowing not to repeat already done tests (as it would work with the fixed active set during all calls).

The algorithm results rather stable results (for slowly changed infile) ...you can see the number of gliders produced by the original algorihm varied a lot (with few "lucky minimas"), while this version produced a lot of results near the minima. (It is still far from 8276 which one experimental version gave).

last results:
The problematic one as first:
8312/<125, 8386/30, 8328/30,8410/27, 8366/32, 8349/20, 8327/21, 8400/33, 8313/30, 8334/30, 8345/30, 8302/30, 8302/30, 8341/31, 8340/31,
8333/34, 8302/32.
---- changed to chunkminpop=8 ...


Code updated

Code: Select all

#pragma once

#include "../lifelib/pattern2.h"
#include "../lifelib/spantree.h"
#include "../lifelib/classifier.h"
#include "../lifelib/ssplit.h"
#include <set>
#include <cstdlib>
#include <sstream>
#include <algorithm>

#define MIN_EXCLUSION_SIZE 32

/*
* Functionality for constructing constellations of still-lifes.
*/

namespace apg {

    /**
     * Computes the period-8 envelope of a pattern
     */
    pattern to_env(pattern x) {

        pattern y = x;
        pattern z = x;

        for (int i = 0; i < 7; i++) {
            z = z[1];
            y += z;
        }

        return y;
    }

    std::vector<coords64> getccenters(std::vector<bitworld> clusters) {
        std::vector<coords64> ccenters;
            for (uint64_t i = 0; i < clusters.size(); i++) {
                // Get bounding box:
                int64_t bbox[4] = {0};
                clusters[i].getbbox(bbox);
                int64_t mx2 = bbox[0] * 2 + bbox[2] - 1;
                int64_t my2 = bbox[1] * 2 + bbox[3] - 1;
                coords64 m2(mx2, my2);
                ccenters.push_back(m2);
            }
        return ccenters;
    }


    double stlength(pattern stell, std::vector<coords64> eccenters, classifier &cfier) {

        bitworld env = to_env(stell).flatlayer(0);
        bitworld live = stell.flatlayer(0);

        std::vector<bitworld> clusters = cfier.getclusters(live, env, false);
        std::vector<coords64> ccenters = getccenters(clusters);
        const uint64_t ccenters_size = ccenters.size();

        for(std::vector<coords64>::iterator it = eccenters.begin(); it != eccenters.end(); ++it) {
            ccenters.push_back(*it);
        }

        dsds colours(ccenters_size+1);
        std::vector<edge64> sgraph = spanning_graph(ccenters);

        // Compute squared length of edges with an endpoint in stell:
        std::vector<std::pair<int64_t, edge64> > sedges;
        for (std::vector<edge64>::iterator it = sgraph.begin(); it != sgraph.end(); ++it) {
            if ((it->first < ccenters_size)||(it->second < ccenters_size)) {
                coords64 a = ccenters[it->first];
                coords64 b = ccenters[it->second];
                int64_t xdiff = a.first - b.first;
                int64_t ydiff = a.second - b.second;
                int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
                sedges.push_back(std::make_pair(sqlength, *it));
            }
        }

        // Sort edges into ascending order:
        std::sort(sedges.begin(), sedges.end());

        double length=0;
        // Apply Kruskal's algorithm:
        for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
            uint64_t a = it->second.first;
            uint64_t b = it->second.second;
            bool internal=true;
            if (a >= ccenters_size) { a = ccenters_size; internal=false;}
            if (b >= ccenters_size) { b = ccenters_size; internal=false;} // vertices of extra has the same color
            if (!colours.connected(a, b)) {
                colours.merge(a, b);
                if (internal) { length+= std::sqrt(it->first); }
            }
        }
        return 0.5 * length;
    }

    pattern diagonalise(pattern inp) {
        /*
        * Create a diagonal line longer than the diameter of a pattern.
        */

        int64_t bbox[4] = {0};
        inp.getrect(bbox);

        pattern diagonal(inp.getlab(), "o$bo$2bo$3bo$4bo$5bo$6bo$7bo$8bo$9bo$10bo$11bo$12bo$13bo$14bo$15bo!", inp.getrule());
        for (uint64_t i = 4; i < 64; i++) {
            if (((1 << i) >= bbox[2]) && ((1 << i) >= bbox[3])) { break; }
            diagonal += diagonal(1 << i, 1 << i);
        }

        return diagonal;
    }

    pattern get_exclusions(pattern inpat, classifier &cfier) {

        auto lab = inpat.getlab();

        pattern excl(lab, "", "b3s23");

        bitworld live = inpat.flatlayer(0);
        std::vector<bitworld> clusters = cfier.getclusters(live, live, true);
        for (uint64_t i = 0; i < clusters.size(); i++) {
            if (clusters[i].population() >= MIN_EXCLUSION_SIZE) {
                excl += pattern(lab, lab->demorton(clusters[i], 1), "b3s23");
            }
        }

        return excl;
    }

    uint64_t excluded_popcount(pattern inpat, classifier &cfier) {
        pattern excl = get_exclusions(inpat, cfier);
        return (inpat - excl).popcount((1 << 30) + 3);
    }

    pattern cell_lowerright(pattern inpat) {
        // Get a reasonably lower-right cell of an input pattern.
        bitworld bw = inpat.flatlayer(0);
        bw = bw.br1cell();
        auto lab = inpat.getlab();
        pattern brcell(lab, lab->demorton(bw, 1), inpat.getrule());
        return brcell;
    }

    pattern bounding_hull(pattern x) {

        int64_t bbox[4] = {0};
        x.getrect(bbox);

        pattern y(x.getlab(), "bbo$bo$o!", "b3s23");
        y = y.shift(-1, -1);

        uint64_t p = bbox[2] + bbox[3];
        while (p > 0) {
            p = p >> 1;
            y = y.convolve(y);
        }

        return x.convolve(y).subrect(bbox);
    }

    pattern get_isolated_block(pattern inpat) {

        auto lab = inpat.getlab();
        pattern convrect(lab, lab->rectangle(-20, -20, 41, 41), "b3s23");
        return inpat & cell_lowerright(inpat).convolve(convrect);

    }

    bool has_isolated_block(pattern inpat) {

        pattern x = get_isolated_block(inpat);
        int64_t bbox[4] = {0};
        x.getrect(bbox);
        return (bbox[2] == 2) && (bbox[3] == 2);

    }

    pattern get_lowerright(pattern inpat, pattern inpat2, uint64_t minpop, int radius, classifier &cfier, pattern extra) {
        // Get a lower-right chunk of an input pattern.
        auto lab = inpat.getlab();
        pattern convrect(lab, lab->rectangle(-radius, -radius, 2 * radius + 1, 2 * radius + 1), "b3s23");
        pattern icell = cell_lowerright(inpat2);
        pattern diag = diagonalise(inpat);
        pattern sword(lab, "o$bo$2bo$3b2o$3b2o!", "b3s23");

        for (int i = 0; i < 6; i++) {
            sword = sword.convolve(sword);
        }

        sword = sword.convolve(diag).convolve(convrect);
        pattern chunk(lab, "", "b3s23");

        for (;;) {
            pattern newchunk = chunk + icell;

            while (chunk != newchunk) {
                chunk = newchunk & inpat;
                newchunk = bounding_hull(chunk).convolve(sword) & inpat;
            }

            pattern rempat = inpat - chunk;

            if (rempat.empty()) { break; }

            uint64_t epc = excluded_popcount(chunk, cfier);
            if (epc < minpop) {
                icell = icell.convolve(convrect);
            } else {
                std::cout << "\033[36;1mFound " << epc;
                std::cout << "-cell chunk; " << excluded_popcount(rempat, cfier);
                std::cout << " + " << extra.popcount((1 << 30) + 3);
                std::cout << " cells remain.\033[0m" << std::endl;
                break;
            }
        }
        return chunk;
    }

    std::vector<std::pair<int64_t,pattern>> dimerise(pattern stell, classifier &cfier, int64_t maxDepth) {
        /*
        * Find promising still-life pairs and individual still-lifes
        * which the compiler has a good chance of being able to build.
        */

        /*
          dimers have monomer center distances at most 25\sqrt{2} so Manhattan distance at most 50
          for a monomer A center of the most distance dimmer containing A is from center of A in Manhattan distance at most 25
          We want A to be processed after last dimmer containing A so we should penalise A by Manhattan distance > 25
          We use 2 points coordinates so the penalty should be > 50
          (After that we try to reduce a tub or beehive to block, and other monomer than tub, beehive or block to tub, beehive or block)

          Monomer blocks are processed differently, we transform them to blocks, but we should try to improve a
          chance it will create dimmer to be processed.
          The heuristic trying to shorten (global) minimum spanning forest looks well as the most distant blocks tend to move
          towards the rest.

          moving block to Manhattan distance 36 is rather cheap, it seems to me they should be
          "invited to follow monomers" from around such a distance.

        */

        const int64_t diPenalty = 0; // bespokePenalty=-80

        const int64_t monoPenalty = 51;

        const int64_t blockPenalty = 120;

        std::vector<std::pair<int64_t,pattern>> sorted_dimers;
        //std::vector<std::vector<pattern> > sdimers;

        bitworld env = to_env(stell).flatlayer(0);
        bitworld live = stell.flatlayer(0);

        std::vector<bitworld> clusters = cfier.getclusters(live, env, true);
        std::vector<coords64> ccenters;

        std::set<std::pair<int64_t, edge64>> edgelist_sorted;

        for (uint64_t i = 0; i < clusters.size(); i++) {
            // Get bounding box:
            int64_t bbox[4] = {0};
            clusters[i].getbbox(bbox);
            int64_t mx2 = bbox[0] * 2 + bbox[2] - 1;
            int64_t my2 = bbox[1] * 2 + bbox[3] - 1;
            coords64 m2(mx2, my2);
            ccenters.push_back(m2);

            // penalty to ensure that singleton blocks appear after everything else:
            int64_t pen = ((bbox[2] == 2) && (bbox[3] == 2)) ? blockPenalty : monoPenalty;

            edgelist_sorted.emplace(pen - 2*(mx2 + my2), edge64(i, i));
        }

        std::vector<edge64> edgelist_unsorted = spanning_graph(ccenters);

        for (uint64_t i = 0; i < edgelist_unsorted.size(); i++) {
            edge64 edge = edgelist_unsorted[i];

            int64_t x1 = ccenters[edge.first].first;
            int64_t y1 = ccenters[edge.first].second;
            int64_t x2 = ccenters[edge.second].first;
            int64_t y2 = ccenters[edge.second].second;

            if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > 5000) { continue; }
            edgelist_sorted.emplace(diPenalty - (x1 + y1 + x2 + y2), edge);
        }

        lifetree_abstract<uint32_t>* lab = stell.getlab();
        std::set<edge64> edgedump;

        int64_t priorityLimit = edgelist_sorted.begin()->first + maxDepth;
        uint64_t ActiveMonomers=0;

        for (auto it = edgelist_sorted.begin(); it != edgelist_sorted.end(); ++it) {
            if (it->first >= priorityLimit) {break;}
            edge64 edge = it->second;
            if (edge.first == edge.second) {
                // Include monomers:
                ActiveMonomers++;
                uint64_t i = edge.first;
                pattern monomer(lab, lab->demorton(clusters[i], 1), stell.getrule());
                sorted_dimers.push_back(std::make_pair(it->first,monomer));
            } else {
                // Include dimers:
                uint64_t idLo = (edge.first < edge.second) ? edge.first : edge.second;
                uint64_t idHi = (edge.first > edge.second) ? edge.first : edge.second;
                bitworld bw = clusters[edge.first];
                bw += clusters[edge.second];
                pattern dimer(lab, lab->demorton(bw, 1), stell.getrule());
                edge64 newedge(idLo, idHi);
                if (edgedump.count(newedge) == 0) {
                    sorted_dimers.push_back(std::make_pair(it->first,dimer));
                    edgedump.insert(newedge);
                }
            }
        }
        std::cout << "active dimers: " << sorted_dimers.size()-ActiveMonomers << " active monomers: " << ActiveMonomers << " maxDepth: " << maxDepth << std::endl;
        return sorted_dimers;
    }

    template <typename T> struct cgsalvo {

        std::vector<std::pair<T, char> > gliders;
        T dx;
        T dy;
        bool age;
        bool transpose;

        void glidermatch(pattern pat) {

            std::map<std::pair<int64_t, int64_t>, uint8_t> gmap;
            std::vector<pattern> matches;

            pattern a_glider(pat.getlab(), "3o$o$bo!", pat.getrule());

            int64_t bbox[4] = {0};

            for (uint64_t i = 0; i < 4; i++) {
                bitworld bw = pat.match(a_glider).flatlayer(0);
                a_glider = a_glider[1];

                while (bw.population()) {
                    bitworld onecell = bw.get1cell();
                    bw -= onecell;
                    onecell.getbbox(bbox);
                    int64_t lane = (bbox[0] - bbox[1]);
                    uint8_t phase = (uint8_t) (((bbox[1] & 1) << 2) + i);
                    gmap[std::pair<int64_t, int64_t>(bbox[0] + bbox[1], lane)] = phase;
                }
            }

            for (auto it = gmap.begin(); it != gmap.end(); ++it) {
                T lane = it->first.second;
                uint8_t phase = it->second;
                gliders.emplace_back(lane, phase);
            }
        }

        std::pair<pattern, pattern> frompattern(pattern pat) {

            int64_t bbox[4] = {0};
            pattern fin = pat[1 << 20];
            
            to_env(fin).getrect(bbox);
            pattern centred = pat(-bbox[0], -bbox[1]);
            fin = fin(-bbox[0], -bbox[1]);

            pattern target = centred & centred[8];
            pattern gliders = centred - target;

            glidermatch(gliders);

            dx = 0;
            dy = 0;
            age = 0;
            transpose = 0;

            return std::pair<pattern, pattern>(target, fin);
        }

        void fromline(std::string line) {
            std::vector<std::string> colons = string_split(line, ':');
            if (colons.size() >= 3) {

                std::string t = colons[colons.size() - 2];
                std::vector<std::string> gstrings = string_split(t, ' ');

                for (uint64_t i = 0; i < gstrings.size(); i++) {
                    std::string g = gstrings[i];
                    if (g != "") {
                        char lastchar = g[g.length() - 1];
                        if ((lastchar == 'E') || (lastchar == 'O')) {
                            T j = (std::stoll(g.substr(0, g.length() - 1)) - 1) / 2;
                            gliders.emplace_back(j, ((lastchar == 'E') ? 0 : 1));
                        }
                    }
                }

                std::string s = colons[colons.size() - 3];
                std::replace(s.begin(), s.end(), '(', ' ');
                std::replace(s.begin(), s.end(), ')', ' ');
                std::replace(s.begin(), s.end(), ',', ' ');
                std::vector<std::string> hstrings = string_split(s, ' ');
                std::vector<std::string> hstrings2;

                for (uint64_t i = 0; i < hstrings.size(); i++) {
                    std::string h = hstrings[i];
                    if (h != "") { hstrings2.push_back(h); }
                }
                if (hstrings2.size() == 3) {
                    dx = std::stoll(hstrings2[0]);
                    dy = std::stoll(hstrings2[1]);
                    transpose = (hstrings2[2] == "T");
                }

                std::string r = colons[colons.size() - 1];
                if (r.find("o") != std::string::npos) { age = 1; }
                if (r.find("e") != std::string::npos) { age = 0; }

            }
        }

    };

    struct cgfile {
        std::map<std::string, std::vector<std::vector<std::pair<std::string, cgsalvo<int16_t>>>>> sdata;
        std::set<std::string> btargets;

        void digestmc(std::string filename, lifetree_abstract<uint32_t> *lab) {

            pattern x(lab, filename);
            uint64_t period = x[1 << 20].ascertain_period();
            std::cout << " -- " << filename << " has period " << period << "." << std::endl;

            for (uint64_t i = 0; i < period; i++) {
                for (uint64_t j = 0; j < 2; j++) {
                    cgsalvo<int16_t> cg;
                    std::pair<pattern, pattern> respair = cg.frompattern(x);
                    std::string source = respair.first._string32();
                    std::string target = respair.second._string32();
                    btargets.insert(target);

                    if (sdata[target].size() <= cg.gliders.size()) {
                        sdata[target].resize(cg.gliders.size() + 1);
                    }
                    sdata[target][cg.gliders.size()].emplace_back(source, cg);

                    x = x.transpose();
                }
                x = x[1];
            }
        }

        void readfile(std::string filename, lifetree_abstract<uint32_t> *lab, std::string rule) {
            std::ifstream f(filename);
            std::string line;
            std::string rlesofar;
            std::string target;
            std::string source;
            bool readingsrc = false;

            if (!f.good()) { return; }

            std::cout << "Reading file " << filename << "..." << std::endl;
            while (std::getline(f, line)) {
                if (line.empty()) { continue; }
                char c = line[0];

                if ((c == ' ') || (c == '*')) {
                    if (rlesofar != "") { rlesofar += "$"; }
                    rlesofar += line;
                } else if (rlesofar != "") {
                    std::replace( rlesofar.begin(), rlesofar.end(), ' ', 'b');
                    std::replace( rlesofar.begin(), rlesofar.end(), '*', 'o');
                    rlesofar += "!";
                    // std::cout << rlesofar << std::endl;
                    pattern p(lab, rlesofar, rule);
                    if (readingsrc) { source = p._string32(); } else { target = p._string32(); }
                    rlesofar = "";
                }

                if (c == '-') {
                    if (line.find("Source") != std::string::npos) {
                        readingsrc = true;
                    } else if (line.find("Target") != std::string::npos) {
                        readingsrc = false;
                    }
                } else if (rlesofar == "") {
                    cgsalvo<int16_t> cg;
                    cg.fromline(line);
                    if (cg.gliders.size() != 0) {
                        if (sdata[target].size() <= cg.gliders.size()) {
                            sdata[target].resize(cg.gliders.size() + 1);
                        }
                        sdata[target][cg.gliders.size()].emplace_back(source, cg);
                    }
                }
            }
            std::cout << "..." << filename << " successfully read." << std::endl;
        }
    };

    struct cghq {
        /*
        * Collection of slow-salvo recipes
        */

        std::map<std::string, cgfile> cgfiles;
        std::string rule;
        std::string datadir;
        lifetree_abstract<uint32_t> *lab;
        uint64_t stat_bespoke=0, stat_deep=0, stat_tree=0, stat_split=0, stat_reduce=0;

        uint64_t assertfile(std::string filename) {
            if (cgfiles.count(filename) == 0) {
                cgfiles[filename].readfile(filename, lab, rule);
            }
            return cgfiles[filename].sdata.size();
        }

        void assertbespoke(std::string dirname) {
            if (cgfiles.count(dirname) == 0) {
                std::ifstream f(dirname + "/filelist.txt");
                std::string line;
                while (std::getline(f, line)) {
                    std::string filename = dirname + "/" + line;
                    cgfiles[dirname].digestmc(filename, lab);
                }
            }
        }
        std::vector<pattern> depthExtras;
        std::vector<std::vector<coords64>> depthECCBoundary;
        std::vector<std::vector<std::pair<int64_t,pattern>>> depthSDimers;
        std::vector<pattern> depthavoid;
        std::vector<std::uint64_t> depthnbespoke;
        std::vector<std::double_t> depthStLen;

        void preparedepth(uint64_t depth, pattern extra, pattern work, classifier &cfier) {
            if (depthSDimers.size()==depth) {
                std::vector<std::pair<int64_t,pattern>> empty;
                depthSDimers.push_back(empty);
                pattern dummy(lab,"",rule);
                depthExtras.push_back(dummy);
                std::vector<coords64> noccoords;
                depthECCBoundary.push_back(noccoords);
                depthavoid.push_back(dummy);
                depthnbespoke.push_back(0);
                depthStLen.push_back(1.1);
            }
            depthExtras[depth]=extra;
            depthStLen[depth]=1.1;
            std::vector<coords64> noccoords;
            depthECCBoundary[depth] = noccoords;
            pattern stell = work & work[8];

            bitworld env = to_env(stell).flatlayer(0), live = stell.flatlayer(0);
            std::vector<bitworld> sclusters = cfier.getclusters(live, env, false);

            env = to_env(extra).flatlayer(0); live = extra.flatlayer(0);
            std::vector<bitworld> eclusters = cfier.getclusters(live, env, false);

            std::vector<coords64> sccenters,eccenters,accenters;
            sccenters=getccenters(sclusters);
            eccenters=getccenters(eclusters);

            for (uint64_t i = 0; i < sccenters.size(); i++) {
                accenters.push_back(sccenters[i]);
            }

            for (uint64_t i = 0; i < eccenters.size(); i++) {
                accenters.push_back(eccenters[i]);
            }

            dsds colours(sccenters.size()+1);
            std::vector<edge64> sgraph = spanning_graph(accenters);

            // Compute squared length of edges with an endpoint in stell:
            std::vector<std::pair<int64_t, edge64> > sedges;
            for (std::vector<edge64>::iterator it = sgraph.begin(); it != sgraph.end(); ++it) {
                if ((it->first < sccenters.size())||(it->second < sccenters.size())) {
                    coords64 a = accenters[it->first];
                    coords64 b = accenters[it->second];
                    int64_t xdiff = a.first - b.first;
                    int64_t ydiff = a.second - b.second;
                    int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
                    sedges.push_back(std::make_pair(sqlength, *it));
                }
            }

            // Sort edges into ascending order:
            std::sort(sedges.begin(), sedges.end());

            // Apply Kruskal's algorithm:
            std::set<uint64_t> eboundary;
            for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
                uint64_t a = it->second.first;
                uint64_t b = it->second.second;
                uint64_t e = 0;
                if (a >= sccenters.size()) { e = a - sccenters.size(); a = sccenters.size(); }
                if (b >= sccenters.size()) { e = b - sccenters.size(); b = sccenters.size(); } // vertices of extra has the same color
                if (!colours.connected(a, b)) {
                    colours.merge(a, b);
                    if (e != 0) { eboundary.emplace(e); }
                }
            }

            for(std::set<uint64_t> :: iterator it = eboundary.begin(); it != eboundary.end(); ++it) {
               depthECCBoundary[depth].push_back(eccenters[*it]);
            }

        }

        pattern precurse(pattern orig, classifier &cfier, int state, int initbail, int maxbail, uint32_t lastbail, int64_t *ideal, uint64_t depth) {

            std::cout << "precurse " << state << " " << initbail << " " << maxbail << " " << lastbail << " " << ((ideal==0)?"0":"i") << " " << depthExtras[depth].popcount((1 << 30) + 3) << " " << depth << std::endl;

            assertbespoke(datadir+"bespoke");

            pattern stell = orig & orig[8];
            pattern exsalvo = orig - stell;
            pattern diagonal = diagonalise(stell);
            pattern smallblock(lab, "2o$2o!", rule);
            pattern bigblock(lab, "4o$4o$4o$4o!", rule);

            pattern sword = diagonal.convolve(bigblock);

            uint64_t deep_bailout = initbail - 1;
            uint64_t tree_bailout = initbail * 100;
            int64_t maxDepth = 150 + deep_bailout * 10;

            std::vector<std::pair<int64_t,pattern>> sorted_dimers;
            uint64_t nbespoke;
            pattern avoid(lab, "", rule);

            if ((state==0)||(initbail>1)) {
                const int64_t bespokePenalty = -80;
                std::vector<std::pair<int64_t,pattern>> sorted_xdimers = dimerise(stell, cfier, maxDepth);

                int64_t priorityLimit = sorted_xdimers .begin()->first + maxDepth;

                for (auto it = cgfiles[datadir+"bespoke"].btargets.begin(); it != cgfiles[datadir+"bespoke"].btargets.end(); ++it) {
                    pattern sterm(lab, lab->_string32(*it), rule);
                    bitworld bw = stell.match(sterm).flatlayer(0);
                    while (bw.population()) {
                        int64_t bbox[4] = {0};
                        bitworld onecell = bw.get1cell();
                        bw -= onecell;
                        onecell.getbbox(bbox);
                        pattern subset = sterm(bbox[0], bbox[1]);
                        avoid += subset;
                        bitworld fsubset = subset.flatlayer(0);
                        fsubset.getbbox(bbox);
                        int64_t priority = bespokePenalty - 2*(bbox[0] * 2 + bbox[2] + bbox[1] * 2 + bbox[3] - 2);
                        if (priority<priorityLimit) {
                            sorted_dimers.push_back(std::make_pair(priority,subset));
                        }
                    }
                }
                nbespoke = sorted_dimers.size();
                for (uint64_t i = 0; i < sorted_xdimers.size(); i++) {
                    if ((sorted_xdimers[i].second & avoid).empty()) {
                        sorted_dimers.push_back(sorted_xdimers[i]);
                    }
                }
                depthStLen[depth] = 1.1; //dummy ... not 0.0 yet
            } else {
                sorted_dimers=depthSDimers[depth];
                avoid=depthavoid[depth];
                nbespoke=depthnbespoke[depth];
            }

            if (state != 0) {
                depthStLen[depth] = stlength(stell - avoid, depthECCBoundary[depth], cfier);
            }

            double stelllength = depthStLen[depth];

            if ((depth>0)&&(stelllength<0.5)) {
                std::cout << "\033[36;1mAlgorithm terminated with an opimal ST.\033[0m" << std::endl;
                return orig;
            }

            pattern eglider(lab, "3o$o$bo!", rule);

            int budget = (nbespoke>20) ? nbespoke + 10 : 30;

            /*if (state != 0) {

                std::cout << "Obtained " << (freshDimers?"fresh ":"") << depthDimers[depth].size() << " dimers/monomers";

                if (nbespoke) { std::cout << " (including " << nbespoke << " bespoke objects)"; }
                std::cout << "." << std::endl;

                bitworld env = to_env(stell).flatlayer(0);
                bitworld live = stell.flatlayer(0);

                std::vector<bitworld> clusters = cfier.getclusters(live, env, true);
                for (uint64_t i = 0; i < clusters.size(); i++) {
                    if (clusters[i].population() < MIN_EXCLUSION_SIZE) { smallobj += 1; }
                }
                std::cout << smallobj << " objects of < " << MIN_EXCLUSION_SIZE << " cells." << std::endl;
            }

            if (state == 0) {
                std::cout << "Obtained " << (freshDimers?"fresh ":"") << depthDimers[depth].size() << " dimers/monomers";
                if (nbespoke) { std::cout << " (including " << nbespoke << " bespoke objects)"; }
                std::cout << " budget " << budget << "." << std::endl;
            }*/

            // Display dimers:
            for (uint64_t i = 0; i < sorted_dimers.size(); i++) {
                pattern dimer = sorted_dimers[i].second;
                bitworld live = dimer.flatlayer(0);
                bitworld env = to_env(dimer).flatlayer(0);
                int64_t envbbox[4] = {0};
                env.getbbox(envbbox);

                pattern remainder = stell - dimer;
                pattern dcs = dimer.convolve(sword);

                if ((dcs & remainder).nonempty() && (dcs(12, 0) & remainder).nonempty() && (dcs(0, 12) & remainder).nonempty()) { continue; }

                std::map<std::string, int64_t> counts = cfier.census(live, env);
                std::ostringstream ss;
                uint64_t totobj = 0;

                for (auto it = counts.begin(); it != counts.end(); ++it) {
                    if (totobj != 0) { ss << "__"; }
                    if (it->second != 0) {
                        ss << it->first;
                        if (it->second != 1) { ss << "(" << it->second << ")"; }
                    }
                    totobj += it->second;
                }

                std::string lexrepr = ss.str();
                std::vector<std::string> prefices;

                prefices.push_back("bespoke");
                bool oneblock = false;
                if (lexrepr == "xs4_33") {
                    if (state == 0) { continue; }
                    if ((sorted_dimers.size() == 1) && (stell.popcount((1 << 30) + 3)==4)) { oneblock = true; }
                    prefices.push_back("longmove");
                } else if ((lexrepr == "xs6_696") || (lexrepr == "xs4_252")) {
                    prefices.push_back("edgy/xs4_33");
                } else {
                    budget--;
                    if ((state == 0) && (budget == 0)) { break; }
                    if ((state!=0) && (budget>0) && (initbail==0)) {continue; } //processed in budget last time
                    prefices.push_back("edgy/xs4_33");
                    if (totobj == 1) { //monomer
                        prefices.push_back("edgy/xs6_696");
                        prefices.push_back("edgy/xs4_252");
                    }
                }

                uint64_t bdiff = 0;
                if (oneblock) {
                    if (ideal != 0) {
                        int64_t bbox2[4] = {0};
                        dimer.getrect(bbox2);
                        bbox2[0] -= ideal[0];
                        bbox2[1] -= ideal[1];
                        bdiff = (bbox2[0] * bbox2[0]) + (bbox2[1] * bbox2[1]);
                    }
                    if (bdiff == 0) {
                        std::cout << "\033[36;1mAlgorithm terminated with single block.\033[0m" << std::endl;
                        return orig;
                    }
                }

                for (uint64_t z = 0; z < prefices.size(); z++) {
                   bool is_bespoke = (prefices[z] == "bespoke");
                   std::string filename = datadir + prefices[z];
                   if (!is_bespoke) {
                       filename += ("/" + lexrepr);
                   }
                   assertfile(filename);

                   for (uint64_t j = 0; j < (is_bespoke ? 1 : 4); j++) {
                      pattern tlt = dimer.shift(-envbbox[0], -envbbox[1]);
                      if (j & 1) {
                          if (tlt == tlt[1]) { continue; }
                          tlt = tlt[1];
                      }
                      if (j & 2) {
                          if (lexrepr == "xs4_33") { continue; }
                          //if (lexrepr == "xs4_252") { continue; }
                          tlt = tlt.transpose();
                      }

                      //std::cout << i << lexrepr << " z " << z << " j " << j << std::endl;

                      auto it = cgfiles[filename].sdata.find(tlt._string32());
                      if (it != cgfiles[filename].sdata.end()) {
                          uint64_t trycount = 0;
                          for (uint64_t k = 1; k < it->second.size(); k++) {
                              uint64_t n_recipes = it->second[k].size();
                              if (lexrepr == "xs4_33") {
                                  if (trycount > tree_bailout) { break; }
                                    //std::cout << "Attempting to construct xs4_33 with " << k << " gliders (" << n_recipes << " recipes)" << std::endl;
                              }
                              //std::cout << i << lexrepr << " z " << z << " j " << j <<  " k " << k<< std::endl;

                              for (uint64_t l = 0; l < n_recipes; l++) {
                                  cgsalvo<int16_t> cs = it->second[k][l].second;
                                  std::string srcstr = it->second[k][l].first;
                                  pattern source(lab, lab->_string32(srcstr), rule);
                                  // Determine whether it is worth proceeding:
                                  bool trythis = false;
                                  pattern xlt = source.shift(-cs.dx, -cs.dy);
                                  pattern altstell = remainder + xlt.shift(envbbox[0], envbbox[1]);
                                  double altstelllength = stelllength;
                                  uint64_t altbdiff = 0;
                                  bool oneblock_improvement = false;
                                  //std::cout << i << lexrepr << " z " << z << " j " << j << " k " << k << " l " << l << std::endl;

                                  if (oneblock) {
                                      int64_t bbox2[4] = {0};
                                      xlt.shift(envbbox[0], envbbox[1]).getrect(bbox2);
                                      bbox2[0] -= ideal[0];
                                      bbox2[1] -= ideal[1];
                                      altbdiff = (bbox2[0] * bbox2[0]) + (bbox2[1] * bbox2[1]);
                                      oneblock_improvement = (std::sqrt((double) altbdiff) <= std::sqrt((double) bdiff) - 25.0 + 0.1 * initbail);
                                      trythis = (altbdiff == 0) || oneblock_improvement;
                                  } else if (lexrepr == "xs4_33") {
                                      if (trycount == deep_bailout) {
                                          //std::cout << "Reached bailout " << deep_bailout << " for strategy 'deep'" << std::endl;
                                      } else if (trycount == tree_bailout) {
                                          //std::cout << "Reached bailout " << tree_bailout << " for strategy 'tree'" << std::endl;
                                      } else if (trycount > tree_bailout) { break; }
                                      altstelllength = stlength(altstell - avoid, depthECCBoundary[depth], cfier);
                                      trythis = (altstelllength <= stelllength - 15.0) || (trycount < deep_bailout) || (nbespoke >= 1);
                                  } else {
                                      trythis = true;
                                  }

                                  if (trythis) {
                                     pattern slt = source;
                                     for (uint64_t m = 0; m < cs.gliders.size(); m++) {
                                         std::pair<int16_t, uint8_t> ng = cs.gliders[m];
                                         int64_t posback = (m + 1) * 128;
                                         slt += eglider(posback + ng.first, posback)[ng.second];
                                     }
                                     uint64_t j2 = (cs.transpose ? 2 : 0) + cs.age;
                                     j2 ^= j;
                                     slt = slt.shift(-cs.dx, -cs.dy);
                                     pattern xlt = source.shift(-cs.dx, -cs.dy);
                                     if (j2 & 1) { slt = slt[1]; xlt = xlt[1]; }
                                     if (j2 & 2) { slt = slt.transpose(); xlt = xlt.transpose(); }

                                     pattern sltshift = slt.shift(envbbox[0], envbbox[1]);
                                     pattern newpat = sltshift + remainder;

                                     //std::cout << i << lexrepr << " z " << z << " j " << j << " k " << k << " l " << l << " trying" << std::endl;

                                     if (newpat[512 * (cs.gliders.size() + 1)] == stell) {
                                        if ((sltshift.convolve(sword) & remainder).empty()) {
                                            // std::cout << "Good match!" << std::endl;
                                        } else {
                                            // std::cout << "Inaccessible from infinity" << std::endl;
                                            continue;
                                        }
                                        int64_t posback = (cs.gliders.size() + 2) * 128;
                                        newpat += exsalvo(posback, posback);
                                        pattern altstell = remainder + xlt.shift(envbbox[0], envbbox[1]);

                                        if (oneblock) {
                                            if (altbdiff == 0) {
                                                std::cout << "\033[32;1mInitial block correctly emplaced\033[0m" << std::endl;
                                                return newpat;
                                            } else if (oneblock_improvement) {
                                                std::cout << "\033[32;1mInitial block moved towards target\033[0m" << std::endl;
                                                return newpat;
                                            }
                                        } else if (lexrepr == "xs4_33") {
                                            pattern newpat2 = newpat;
                                            if ((trycount >= lastbail) && (trycount < deep_bailout)) {
                                                // We now create a really large sword to ensure only the BR-most
                                                // block is moved by the 'deep' strategy:
                                                pattern qlt = sltshift.convolve(bigblock).convolve(bigblock);
                                                qlt += qlt(8, 8).convolve(bigblock).convolve(bigblock);
                                                if ((qlt.convolve(sword) & remainder).empty()) {
                                                    pattern empty(lab,"",rule);
                                                    preparedepth(depth+1,empty,newpat2,cfier); // no need to prepare spanning tree for shallow search
                                                    newpat2 = precurse(newpat, cfier, 0, 1, 1, 0, ideal, depth+1);
                                                    std::cout << "back to precurse A" << std::endl;
                                                }
                                            }
                                            if (newpat != newpat2) {
                                                stat_deep++;
                                                std::cout << (double) clock() << " \033[32;1mdeep   \033[0m" ;
                                                std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                                std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                                std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
				  			                    std::cout << " predeep population " << stell.popcount((1 << 30) + 3) << " --> ";
                                                std::cout << altstell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr << std::endl;
                                                return newpat2;
                                            }

                                            double nutil_old = stelllength;
                                            double nutil_new = altstelllength;
                                            if (nbespoke > 0) {
                                                pattern barrier = avoid.convolve(sword);
                                                pattern altrem = altstell - avoid;
                                                pattern rem = stell - avoid;
                                                rem += rem(0, 6); rem += rem(6, 0);
                                                altrem += altrem(0, 6); altrem += altrem(6, 0);
                                                rem += rem(0, 12); rem += rem(12, 0);
                                                altrem += altrem(0, 12); altrem += altrem(12, 0);
                                                uint64_t pop1 = 0;
                                                uint64_t pop2 = 0;
                                                for (uint64_t zz = 0; zz < 4; zz++) {
                                                    pop2 += (barrier & altrem).popcount((1 << 30) + 3);
                                                    pop1 += (barrier & rem).popcount((1 << 30) + 3);
                                                    barrier = barrier.convolve(sword).convolve(sword);
                                                }
                                                nutil_old += 0.1 * pop1;
                                                nutil_new += 0.1 * pop2;
                                            }
                                            if (nutil_new <= nutil_old - 15.0) {
                                                stat_tree++;
                                                std::cout << (double) clock() << " \033[32;1mtree   \033[0m: ";
                                                std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                                std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                                std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
				  			                    std::cout << " population " << stell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr;
                                                std::cout << " loss " << nutil_old << " --> " << nutil_new << std::endl;
                                                return newpat;
                                            }
                                        } else {
                                            if (is_bespoke) {
                                                stat_bespoke++;
                                                std::cout << (double) clock() << " \033[32;1mbespoke\033[0m: ";
                                            } else if (totobj == 1) {
                                                stat_reduce++;
                                                std::cout << (double) clock() << " \033[32;1mreduce \033[0m: ";
                                            } else {
                                                stat_split++;
                                                std::cout << (double) clock() << " \033[32;1msplit  \033[0m: ";
                                            }
                                            std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                            std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                            std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
                                            std::cout << " population " << stell.popcount((1 << 30) + 3) << " --> ";
                                            std::cout << altstell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr << std::endl;

                                            return newpat;
                                        }
                                     }
                                  }
                                  trycount += 1;
                              }
                          }
                      }
                   }
                }
            }
            // std::cout << "--------------------------------" << std::endl;
            depthSDimers[depth]=sorted_dimers;
            depthavoid[depth]=avoid;
            depthnbespoke[depth]=nbespoke;

            if ((maxbail != 0) && (initbail < maxbail)) {
                std::cout << "Increasing bailout to " << (initbail * 3) << std::endl;
                pattern ret=precurse(orig, cfier, state, initbail * 3, maxbail, initbail, ideal, depth);
                std::cout << "back to precurse B" << std::endl;
                return ret;
            }
            std::cout << "no improvement found" << std::endl;
            return orig;
        }

        pattern preiterate(pattern initial, classifier &cfier, int64_t *ideal, uint64_t depth, pattern extra) {
            std::cout << "preiterate " << ((ideal==0)?"0":"i") << " " << depth << " " << extra.popcount((1 << 30) + 3) << std::endl;
            pattern pcend = initial;
            pattern pcstart(lab, "", rule);
            pattern gliders(lab, "", rule);
            preparedepth(depth,extra,initial,cfier);

            bool progress = true;
            uint64_t chunk1minpop = 8, chunk2minpop = 8;
            while (pcstart != pcend) {

                if (progress) {
                    chunk1minpop = chunk2minpop = 8;
                    progress = false;
                }

                pcstart = pcend & pcend[8];
                pattern salvo = pcend - pcstart;
                if (salvo.nonempty() && gliders.nonempty()) {

                    int64_t diag = 0;
                    int64_t gliders_bbox[4] = {0};
                    int64_t pcend_bbox[4] = {0};

                    gliders.getrect(gliders_bbox);
                    pcend.getrect(pcend_bbox);

                    int64_t horizontal = (pcend_bbox[0] + pcend_bbox[2] - gliders_bbox[0]);
                    int64_t vertical   = (pcend_bbox[1] + pcend_bbox[3] - gliders_bbox[1]);

                    if (diag < horizontal) { diag = horizontal; }
                    if (diag < vertical) { diag = vertical; }

                    diag += 512;
                    diag -= (diag & 255);

                    gliders = gliders(diag, diag);
                }

                gliders += salvo;
                if (depth>0) {
                    if (depthStLen[depth]<0.5) {
                        std::cout << "\033[36;1mST optimal break.\033[0m" << std::endl;
                        break;
                    }
                }

                {
                    // save progress
                    std::ostringstream ss;
                    ss << "current" << depth << ".mc";
                    std::ofstream out(ss.str());
                    (pcstart + gliders).write_macrocell(out);
                }

                pattern inpat2 = pcstart;

                if (has_isolated_block(pcstart)) {
                    pattern chunk = get_lowerright(pcstart, inpat2, chunk1minpop, 32, cfier, extra); //
                    if (chunk != pcstart) {
                        pattern remainder = pcstart - chunk;
                        pcend = remainder + preiterate(chunk, cfier, 0, depth+1, extra + remainder);
                        std::cout << "back to preiterate A " << depthStLen[depth];
                        if (pcend == pcstart) {
                            std::cout << " with no progress" << std::endl;
                            chunk1minpop = 1+chunk.popcount((1 << 30) + 3);
                            pcstart = chunk; // to stay in the loop and try bigger chunk
                        } else {
                            std::cout << " with a progress" << std::endl;
                            progress = true;
                        }
                        continue;
                    }
                    inpat2 -= get_isolated_block(inpat2);
                }

                pcend = precurse(pcstart, cfier, 0, 1, 1, 0, ideal, depth);
                std::cout << "back to preiterate B " << depthStLen[depth];

                if (pcend != pcstart) {
                    std::cout << " with a progress" << std::endl;
                    progress= true; continue;
                }

                std::cout << " with no progress" << std::endl;

                if (inpat2.nonempty()) {
                    pattern chunk = get_lowerright(pcstart, inpat2, chunk2minpop, 25, cfier, extra);
                    if (chunk != pcstart) {
                        pattern remainder = pcstart - chunk;
                        pcend = remainder + preiterate(chunk, cfier, 0, depth+1, extra + remainder);
                        std::cout << "back to preiterate C " << depthStLen[depth];
                        if (pcend == pcstart) {
                            std::cout << " with no progress" << std::endl;
                            chunk2minpop = 1+chunk.popcount((1 << 30) + 3);
                            pcstart = chunk; // to stay in the loop and try bigger chunk
                        } else {
                            std::cout << " with a progress" << std::endl;
                            progress = true;
                        }
                        continue;
                    }
                }

                // attempt deeper constructions:
                pcend = precurse(pcstart, cfier, 1, 1, 4096, 0, ideal, depth);
                std::cout << "back to preiterate D " << depthStLen[depth];

                if (pcend != pcstart) {
                    std::cout << " with a progress" << std::endl;
                    progress= true; continue;
                }
                std::cout << " with no progress" << std::endl;

            }

            return (pcstart + gliders);
        }

        pattern preiterate(pattern initial, classifier &cfier, int64_t *ideal, uint64_t depth = 0) {
            std::cout << "preiterate0 " << ((ideal==0)?"0":"i") << " " << depth  << std::endl;
            lab = initial.getlab();
            rule = initial.getrule();

            pattern extra(lab, "", rule);
            return preiterate(initial, cfier, ideal, depth, extra);
        }

        pattern preiterate(pattern initial, classifier &cfier) {
            pattern findme(initial.getlab(), "3b2o$2bo2bo$bob2obo$obo2bobo$obo2bobo$bob2obo$2bo2bo$3b2o!", initial.getrule());
            pattern m = initial.match(findme);
            pattern im = initial - m.convolve(findme);

            if (m.empty()) {
                return preiterate(im, cfier, 0, 0);
            } else {
                int64_t ideal[4] = {0};
                m(3, 3).getrect(ideal);
                return preiterate(im, cfier, ideal, 0);
            }
        }

    };

}
I am still "not happy" with clusters being recompued all the times when each update replaces one or two of them with another cluster.
It would be nice to have a dynamic maintainance of clusters and the spanning graph (nonrecursve) (with census precomputed).
But seems these optimizations are not that important as testing the recipes comsumes much more time.

I have played with parameters more ... having 150 depth for early phases (bailout==1) and if there is a bespoke than 220 for later phases (not adding other bespokes) first try given 8205 gliders. I have still problems with early termination of precurse not working so the program waited till last bailout didn`t help ... end preiterate continued well. ... prolonging the run without a reason ... Seems I have found the bug ... in stellength maintainance:) ... now the algoritmh usually succeeds on first target (or rarely second one, very rarely third) (while there are no active bespokes), and algorithm slows down just a bit when bespokes are nearby. Maybe it's time to start thinking about the dynamic maintainance as dimerisation could become bottleneck of the search.

Current results are 8266/?, 8229/?, 8232/30, 8236/33, 8302/35 but I am still finding inoptimalities in the logs ... currently I understand what causes reaching maxbailout problem ... I could have only one target (block) with one faraway block on left and one on right. There is no place where to move the target to decrease stelllength sufficiently ... maybe recomputing the boundary among the chunk and the rest solves the problem.

And I have decided to save progress at most once per 30s (at each depth).

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » March 20th, 2023, 1:56 pm

OK, here is my current version: of slmake.h:

Code: Select all

#pragma once

#include "../lifelib/pattern2.h"
#include "../lifelib/spantree.h"
#include "../lifelib/classifier.h"
#include "../lifelib/ssplit.h"
#include <set>
#include <cstdlib>
#include <sstream>
#include <algorithm>

#define MIN_EXCLUSION_SIZE 32

/*
* Functionality for constructing constellations of still-lifes.
*/

namespace apg {

    /**
     * Computes the period-8 envelope of a pattern
     */
    pattern to_env(pattern x) {

        pattern y = x;
        pattern z = x;

        for (int i = 0; i < 7; i++) {
            z = z[1];
            y += z;
        }

        return y;
    }

    std::vector<coords64> getccenters(std::vector<bitworld> clusters) {
        std::vector<coords64> ccenters;
            for (uint64_t i = 0; i < clusters.size(); i++) {
                // Get bounding box:
                int64_t bbox[4] = {0};
                clusters[i].getbbox(bbox);
                int64_t mx2 = bbox[0] * 2 + bbox[2] - 1;
                int64_t my2 = bbox[1] * 2 + bbox[3] - 1;
                coords64 m2(mx2, my2);
                ccenters.push_back(m2);
            }
        return ccenters;
    }


    double stlength(pattern stell, std::vector<coords64> eccenters, classifier &cfier) {

        bitworld env = to_env(stell).flatlayer(0);
        bitworld live = stell.flatlayer(0);

        std::vector<bitworld> clusters = cfier.getclusters(live, env, false);
        std::vector<coords64> ccenters = getccenters(clusters);
        const uint64_t ccenters_size = ccenters.size();

        for(std::vector<coords64>::iterator it = eccenters.begin(); it != eccenters.end(); ++it) {
            ccenters.push_back(*it);
        }

        dsds colours(ccenters_size+1);
        std::vector<edge64> sgraph = spanning_graph(ccenters);

        // Compute squared length of edges with an endpoint in stell:
        std::vector<std::pair<int64_t, edge64> > sedges;
        for (std::vector<edge64>::iterator it = sgraph.begin(); it != sgraph.end(); ++it) {
            if ((it->first < ccenters_size)||(it->second < ccenters_size)) {
                coords64 a = ccenters[it->first];
                coords64 b = ccenters[it->second];
                int64_t xdiff = a.first - b.first;
                int64_t ydiff = a.second - b.second;
                int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
                sedges.push_back(std::make_pair(sqlength, *it));
            }
        }

        // Sort edges into ascending order:
        std::sort(sedges.begin(), sedges.end());

        double length=0;
        // Apply Kruskal's algorithm:
        for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
            uint64_t a = it->second.first;
            uint64_t b = it->second.second;
            bool internal=true;
            if (a >= ccenters_size) { a = ccenters_size; internal=false;}
            if (b >= ccenters_size) { b = ccenters_size; internal=false;} // vertices of extra has the same color
            if (!colours.connected(a, b)) {
                colours.merge(a, b);
                if (internal) { length+= std::sqrt(it->first); }
            }
        }
        return 0.5 * length;
    }

    pattern diagonalise(pattern inp) {
        /*
        * Create a diagonal line longer than the diameter of a pattern.
        */

        int64_t bbox[4] = {0};
        inp.getrect(bbox);

        pattern diagonal(inp.getlab(), "o$bo$2bo$3bo$4bo$5bo$6bo$7bo$8bo$9bo$10bo$11bo$12bo$13bo$14bo$15bo!", inp.getrule());
        for (uint64_t i = 4; i < 64; i++) {
            if (((1 << i) >= bbox[2]) && ((1 << i) >= bbox[3])) { break; }
            diagonal += diagonal(1 << i, 1 << i);
        }

        return diagonal;
    }

    pattern get_exclusions(pattern inpat, classifier &cfier) {

        auto lab = inpat.getlab();

        pattern excl(lab, "", "b3s23");

        bitworld live = inpat.flatlayer(0);
        std::vector<bitworld> clusters = cfier.getclusters(live, live, true);
        for (uint64_t i = 0; i < clusters.size(); i++) {
            if (clusters[i].population() >= MIN_EXCLUSION_SIZE) {
                excl += pattern(lab, lab->demorton(clusters[i], 1), "b3s23");
            }
        }

        return excl;
    }

    uint64_t excluded_popcount(pattern inpat, classifier &cfier) {
        pattern excl = get_exclusions(inpat, cfier);
        return (inpat - excl).popcount((1 << 30) + 3);
    }

    pattern cell_lowerright(pattern inpat) {
        // Get a reasonably lower-right cell of an input pattern.
        bitworld bw = inpat.flatlayer(0);
        bw = bw.br1cell();
        auto lab = inpat.getlab();
        pattern brcell(lab, lab->demorton(bw, 1), inpat.getrule());
        return brcell;
    }

    pattern bounding_hull(pattern x) {

        int64_t bbox[4] = {0};
        x.getrect(bbox);

        pattern y(x.getlab(), "bbo$bo$o!", "b3s23");
        y = y.shift(-1, -1);

        uint64_t p = bbox[2] + bbox[3];
        while (p > 0) {
            p = p >> 1;
            y = y.convolve(y);
        }

        return x.convolve(y).subrect(bbox);
    }

    pattern get_isolated_block(pattern inpat) {

        auto lab = inpat.getlab();
        pattern convrect(lab, lab->rectangle(-20, -20, 41, 41), "b3s23");
        return inpat & cell_lowerright(inpat).convolve(convrect);

    }

    bool has_isolated_block(pattern inpat) {

        pattern x = get_isolated_block(inpat);
        int64_t bbox[4] = {0};
        x.getrect(bbox);
        return (bbox[2] == 2) && (bbox[3] == 2);

    }

    pattern get_lowerright(pattern inpat, pattern inpat2, uint64_t minpop, int radius, classifier &cfier, pattern extra) {
        // Get a lower-right chunk of an input pattern.
        auto lab = inpat.getlab();
        pattern convrect(lab, lab->rectangle(-radius, -radius, 2 * radius + 1, 2 * radius + 1), "b3s23");
        pattern icell = cell_lowerright(inpat2);
        pattern diag = diagonalise(inpat);
        pattern sword(lab, "o$bo$2bo$3b2o$3b2o!", "b3s23");

        for (int i = 0; i < 6; i++) {
            sword = sword.convolve(sword);
        }

        sword = sword.convolve(diag).convolve(convrect);
        pattern chunk(lab, "", "b3s23");

        for (;;) {
            pattern newchunk = chunk + icell;

            while (chunk != newchunk) {
                chunk = newchunk & inpat;
                newchunk = bounding_hull(chunk).convolve(sword) & inpat;
            }

            pattern rempat = inpat - chunk;

            if (rempat.empty()) { break; }

            uint64_t epc = excluded_popcount(chunk, cfier);
            if (epc < minpop) {
                icell = icell.convolve(convrect);
            } else {
                std::cout << "\033[36;1mFound " << epc;
                std::cout << "-cell chunk; " << excluded_popcount(rempat, cfier);
                std::cout << " + " << extra.popcount((1 << 30) + 3);
                std::cout << " cells remain.\033[0m" << std::endl;
                break;
            }
        }
        return chunk;
    }

    std::vector<std::pair<int64_t,pattern>> dimerise(pattern stell, classifier &cfier, int64_t maxDepth) {
        /*
        * Find promising still-life pairs and individual still-lifes
        * which the compiler has a good chance of being able to build.
        */

        /*
          dimers have monomer center distances at most 25\sqrt{2} so Manhattan distance at most 50
          for a monomer A center of the most distance dimmer containing A is from center of A in Manhattan distance at most 25
          We want A to be processed after last dimmer containing A so we should penalise A by Manhattan distance > 25
          We use 2 points coordinates so the penalty should be > 50
          (After that we try to reduce a tub or beehive to block, and other monomer than tub, beehive or block to tub, beehive or block)

          Monomer blocks are processed differently, we transform them to blocks, but we should try to improve a
          chance it will create dimmer to be processed.
          The heuristic trying to shorten (global) minimum spanning forest looks well as the most distant blocks tend to move
          towards the rest.

          moving block to Manhattan distance 36 is rather cheap, it seems to me they should be
          "invited to follow monomers" from around such a distance.

        */

        const int64_t diPenalty = 0;
        // bespokePenalty=-80 < -50 to prefere be broken to clusters before the clusters could become part of active dimers

        const int64_t monoPenalty = 51;

        const int64_t blockPenalty = 120;

        std::vector<std::pair<int64_t,pattern>> sorted_dimers;
        //std::vector<std::vector<pattern> > sdimers;

        bitworld env = to_env(stell).flatlayer(0);
        bitworld live = stell.flatlayer(0);

        std::vector<bitworld> clusters = cfier.getclusters(live, env, true);
        std::vector<coords64> ccenters;

        std::set<std::pair<int64_t, edge64>> edgelist_sorted;

        for (uint64_t i = 0; i < clusters.size(); i++) {
            // Get bounding box:
            int64_t bbox[4] = {0};
            clusters[i].getbbox(bbox);
            int64_t mx2 = bbox[0] * 2 + bbox[2] - 1;
            int64_t my2 = bbox[1] * 2 + bbox[3] - 1;
            coords64 m2(mx2, my2);
            ccenters.push_back(m2);

            // penalty to ensure that singleton blocks appear after everything else:
            int64_t pen = ((bbox[2] == 2) && (bbox[3] == 2)) ? blockPenalty : monoPenalty;

            edgelist_sorted.emplace(pen - 2*(mx2 + my2), edge64(i, i));
        }

        std::vector<edge64> edgelist_unsorted = spanning_graph(ccenters);

        for (uint64_t i = 0; i < edgelist_unsorted.size(); i++) {
            edge64 edge = edgelist_unsorted[i];

            int64_t x1 = ccenters[edge.first].first;
            int64_t y1 = ccenters[edge.first].second;
            int64_t x2 = ccenters[edge.second].first;
            int64_t y2 = ccenters[edge.second].second;

            if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > 5000) { continue; }
            edgelist_sorted.emplace(diPenalty - (x1 + y1 + x2 + y2), edge);
        }

        lifetree_abstract<uint32_t>* lab = stell.getlab();
        std::set<edge64> edgedump;

        int64_t priorityLimit = edgelist_sorted.begin()->first + maxDepth;
        uint64_t ActiveMonomers=0;

        for (auto it = edgelist_sorted.begin(); it != edgelist_sorted.end(); ++it) {
            if (it->first >= priorityLimit) {break;}
            edge64 edge = it->second;
            if (edge.first == edge.second) {
                // Include monomers:
                ActiveMonomers++;
                uint64_t i = edge.first;
                pattern monomer(lab, lab->demorton(clusters[i], 1), stell.getrule());
                sorted_dimers.push_back(std::make_pair(it->first,monomer));
            } else {
                // Include dimers:
                uint64_t idLo = (edge.first < edge.second) ? edge.first : edge.second;
                uint64_t idHi = (edge.first > edge.second) ? edge.first : edge.second;
                bitworld bw = clusters[edge.first];
                bw += clusters[edge.second];
                pattern dimer(lab, lab->demorton(bw, 1), stell.getrule());
                edge64 newedge(idLo, idHi);
                if (edgedump.count(newedge) == 0) {
                    sorted_dimers.push_back(std::make_pair(it->first,dimer));
                    edgedump.insert(newedge);
                }
            }
        }
        std::cout << "considered clusters/dimers/monomers/bespokes/active bespokes/early targets/later targets/early maxDepth/later maxdepth: " <<
             clusters.size() << "/"<< sorted_dimers.size()-ActiveMonomers << "/" << ActiveMonomers << "/";
        return sorted_dimers;
    }

    template <typename T> struct cgsalvo {

        std::vector<std::pair<T, char> > gliders;
        T dx;
        T dy;
        bool age;
        bool transpose;

        void glidermatch(pattern pat) {

            std::map<std::pair<int64_t, int64_t>, uint8_t> gmap;
            std::vector<pattern> matches;

            pattern a_glider(pat.getlab(), "3o$o$bo!", pat.getrule());

            int64_t bbox[4] = {0};

            for (uint64_t i = 0; i < 4; i++) {
                bitworld bw = pat.match(a_glider).flatlayer(0);
                a_glider = a_glider[1];

                while (bw.population()) {
                    bitworld onecell = bw.get1cell();
                    bw -= onecell;
                    onecell.getbbox(bbox);
                    int64_t lane = (bbox[0] - bbox[1]);
                    uint8_t phase = (uint8_t) (((bbox[1] & 1) << 2) + i);
                    gmap[std::pair<int64_t, int64_t>(bbox[0] + bbox[1], lane)] = phase;
                }
            }

            for (auto it = gmap.begin(); it != gmap.end(); ++it) {
                T lane = it->first.second;
                uint8_t phase = it->second;
                gliders.emplace_back(lane, phase);
            }
        }

        std::pair<pattern, pattern> frompattern(pattern pat) {

            int64_t bbox[4] = {0};
            pattern fin = pat[1 << 20];
            
            to_env(fin).getrect(bbox);
            pattern centred = pat(-bbox[0], -bbox[1]);
            fin = fin(-bbox[0], -bbox[1]);

            pattern target = centred & centred[8];
            pattern gliders = centred - target;

            glidermatch(gliders);

            dx = 0;
            dy = 0;
            age = 0;
            transpose = 0;

            return std::pair<pattern, pattern>(target, fin);
        }

        void fromline(std::string line) {
            std::vector<std::string> colons = string_split(line, ':');
            if (colons.size() >= 3) {

                std::string t = colons[colons.size() - 2];
                std::vector<std::string> gstrings = string_split(t, ' ');

                for (uint64_t i = 0; i < gstrings.size(); i++) {
                    std::string g = gstrings[i];
                    if (g != "") {
                        char lastchar = g[g.length() - 1];
                        if ((lastchar == 'E') || (lastchar == 'O')) {
                            T j = (std::stoll(g.substr(0, g.length() - 1)) - 1) / 2;
                            gliders.emplace_back(j, ((lastchar == 'E') ? 0 : 1));
                        }
                    }
                }

                std::string s = colons[colons.size() - 3];
                std::replace(s.begin(), s.end(), '(', ' ');
                std::replace(s.begin(), s.end(), ')', ' ');
                std::replace(s.begin(), s.end(), ',', ' ');
                std::vector<std::string> hstrings = string_split(s, ' ');
                std::vector<std::string> hstrings2;

                for (uint64_t i = 0; i < hstrings.size(); i++) {
                    std::string h = hstrings[i];
                    if (h != "") { hstrings2.push_back(h); }
                }
                if (hstrings2.size() == 3) {
                    dx = std::stoll(hstrings2[0]);
                    dy = std::stoll(hstrings2[1]);
                    transpose = (hstrings2[2] == "T");
                }

                std::string r = colons[colons.size() - 1];
                if (r.find("o") != std::string::npos) { age = 1; }
                if (r.find("e") != std::string::npos) { age = 0; }

            }
        }

    };

    struct cgfile {
        std::map<std::string, std::vector<std::vector<std::pair<std::string, cgsalvo<int16_t>>>>> sdata;
        std::set<std::string> btargets;

        void digestmc(std::string filename, lifetree_abstract<uint32_t> *lab) {

            pattern x(lab, filename);
            uint64_t period = x[1 << 20].ascertain_period();
            std::cout << " -- " << filename << " has period " << period << "." << std::endl;

            for (uint64_t i = 0; i < period; i++) {
                for (uint64_t j = 0; j < 2; j++) {
                    cgsalvo<int16_t> cg;
                    std::pair<pattern, pattern> respair = cg.frompattern(x);
                    std::string source = respair.first._string32();
                    std::string target = respair.second._string32();
                    btargets.insert(target);

                    if (sdata[target].size() <= cg.gliders.size()) {
                        sdata[target].resize(cg.gliders.size() + 1);
                    }
                    sdata[target][cg.gliders.size()].emplace_back(source, cg);

                    x = x.transpose();
                }
                x = x[1];
            }
        }

        void readfile(std::string filename, lifetree_abstract<uint32_t> *lab, std::string rule) {
            std::ifstream f(filename);
            std::string line;
            std::string rlesofar;
            std::string target;
            std::string source;
            bool readingsrc = false;

            if (!f.good()) { return; }

            std::cout << "Reading file " << filename << "..." << std::endl;
            while (std::getline(f, line)) {
                if (line.empty()) { continue; }
                char c = line[0];

                if ((c == ' ') || (c == '*')) {
                    if (rlesofar != "") { rlesofar += "$"; }
                    rlesofar += line;
                } else if (rlesofar != "") {
                    std::replace( rlesofar.begin(), rlesofar.end(), ' ', 'b');
                    std::replace( rlesofar.begin(), rlesofar.end(), '*', 'o');
                    rlesofar += "!";
                    // std::cout << rlesofar << std::endl;
                    pattern p(lab, rlesofar, rule);
                    if (readingsrc) { source = p._string32(); } else { target = p._string32(); }
                    rlesofar = "";
                }

                if (c == '-') {
                    if (line.find("Source") != std::string::npos) {
                        readingsrc = true;
                    } else if (line.find("Target") != std::string::npos) {
                        readingsrc = false;
                    }
                } else if (rlesofar == "") {
                    cgsalvo<int16_t> cg;
                    cg.fromline(line);
                    if (cg.gliders.size() != 0) {
                        if (sdata[target].size() <= cg.gliders.size()) {
                            sdata[target].resize(cg.gliders.size() + 1);
                        }
                        sdata[target][cg.gliders.size()].emplace_back(source, cg);
                    }
                }
            }
            std::cout << "..." << filename << " successfully read." << std::endl;
        }
    };

    struct cghq {
        /*
        * Collection of slow-salvo recipes
        */

        std::map<std::string, cgfile> cgfiles;
        std::string rule;
        std::string datadir;
        lifetree_abstract<uint32_t> *lab;
        uint64_t stat_bespoke=0, stat_deep=0, stat_tree=0, stat_split=0, stat_reduce=0;

        uint64_t assertfile(std::string filename) {
            if (cgfiles.count(filename) == 0) {
                cgfiles[filename].readfile(filename, lab, rule);
            }
            return cgfiles[filename].sdata.size();
        }

        void assertbespoke(std::string dirname) {
            if (cgfiles.count(dirname) == 0) {
                std::ifstream f(dirname + "/filelist.txt");
                std::string line;
                while (std::getline(f, line)) {
                    std::string filename = dirname + "/" + line;
                    cgfiles[dirname].digestmc(filename, lab);
                }
            }
        }
        std::vector<pattern> depthExtras;
        std::vector<std::vector<coords64>> depthECCBoundary;
        std::vector<std::vector<std::pair<int64_t,pattern>>> depthSDimers;
        std::vector<pattern> depthavoid;
        std::vector<std::uint64_t> depthnbespoke;
        std::vector<std::uint64_t> depthnblocks;
        std::vector<std::int64_t> depthEarlyPriorityLimit;
        std::vector<std::double_t> depthStLen;
        std::vector<std::double_t> depthNextSaveTime;


        void preparedepth(uint64_t depth, pattern extra, pattern work, classifier &cfier) {
            if (depthSDimers.size()==depth) {
                std::vector<std::pair<int64_t,pattern>> empty;
                depthSDimers.push_back(empty);
                pattern dummy(lab,"",rule);
                depthExtras.push_back(dummy);
                std::vector<coords64> noccoords;
                depthECCBoundary.push_back(noccoords);
                depthavoid.push_back(dummy);
                depthnbespoke.push_back(0);
                depthnblocks.push_back(0);
                depthEarlyPriorityLimit.push_back(0);
                depthStLen.push_back(1.1);
                depthNextSaveTime.push_back(0.0);
            }
            depthExtras[depth]=extra;
            depthStLen[depth]=1.1;
            std::vector<coords64> noccoords;
            depthECCBoundary[depth] = noccoords;
            pattern stell = work & work[8];

            bitworld env = to_env(stell).flatlayer(0), live = stell.flatlayer(0);
            std::vector<bitworld> sclusters = cfier.getclusters(live, env, false);

            env = to_env(extra).flatlayer(0); live = extra.flatlayer(0);
            std::vector<bitworld> eclusters = cfier.getclusters(live, env, false);

            std::vector<coords64> sccenters,eccenters,accenters;
            sccenters=getccenters(sclusters);
            eccenters=getccenters(eclusters);

            for (uint64_t i = 0; i < sccenters.size(); i++) {
                accenters.push_back(sccenters[i]);
            }

            for (uint64_t i = 0; i < eccenters.size(); i++) {
                accenters.push_back(eccenters[i]);
            }

            dsds colours(sccenters.size()+1);
            std::vector<edge64> sgraph = spanning_graph(accenters);

            // Compute squared length of edges with an endpoint in stell:
            std::vector<std::pair<int64_t, edge64> > sedges;
            for (std::vector<edge64>::iterator it = sgraph.begin(); it != sgraph.end(); ++it) {
                if ((it->first < sccenters.size())||(it->second < sccenters.size())) {
                    coords64 a = accenters[it->first];
                    coords64 b = accenters[it->second];
                    int64_t xdiff = a.first - b.first;
                    int64_t ydiff = a.second - b.second;
                    int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
                    sedges.push_back(std::make_pair(sqlength, *it));
                }
            }

            // Sort edges into ascending order:
            std::sort(sedges.begin(), sedges.end());

            // Apply Kruskal's algorithm:
            std::set<uint64_t> eboundary;
            for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
                uint64_t a = it->second.first;
                uint64_t b = it->second.second;
                uint64_t e = 0;
                if (a >= sccenters.size()) { e = a - sccenters.size(); a = sccenters.size(); }
                if (b >= sccenters.size()) { e = b - sccenters.size(); b = sccenters.size(); } // vertices of extra has the same color
                if (!colours.connected(a, b)) {
                    colours.merge(a, b);
                    if (e != 0) { eboundary.emplace(e); }
                }
            }

            for(std::set<uint64_t> :: iterator it = eboundary.begin(); it != eboundary.end(); ++it) {
               depthECCBoundary[depth].push_back(eccenters[*it]);
            }

        }

        pattern precurse(pattern orig, classifier &cfier, int state, int initbail, int maxbail, uint32_t lastbail, int64_t *ideal, uint64_t depth, uint64_t shallowFlags = 0) {

            std::cout << "precurse " << state << " " << initbail << " " << maxbail << " " << lastbail << " " << ((ideal==0)?"0":"i") << " " << depthExtras[depth].popcount((1 << 30) + 3) << " " << depth << std::endl;

            assertbespoke(datadir+"bespoke");

            pattern stell = orig & orig[8];
            pattern exsalvo = orig - stell;
            pattern diagonal = diagonalise(stell);
            pattern smallblock(lab, "2o$2o!", rule);
            pattern bigblock(lab, "4o$4o$4o$4o!", rule);
            pattern utilhash(lab,"o5bo5bo5bo5$o5bo5bo5bo5$o5bo5bo5bo5$o5bo5bo5bo5!",rule);

            pattern sword = diagonal.convolve(bigblock);
            /*pattern swordExtender(lab,"o12o12$o!",rule);
            pattern extSword = sword.convolve(swordExtender);*/

            uint64_t deep_bailout = initbail - 1, last_deep_bailout = lastbail - 1;
            uint64_t tree_bailout = initbail * 100, last_tree_bailout = lastbail * 100;
            const int64_t earlyMaxDepth = 150;
            int64_t laterMaxDepth = 220; if (shallowFlags>0) {laterMaxDepth = earlyMaxDepth;}

            std::vector<std::pair<int64_t,pattern>> sorted_dimers;
            uint64_t nbespoke, nblocks=0;
            pattern avoid(lab, "", rule);
            int64_t earlyPriorityLimit;

            if ((state==0)) {
                const int64_t bespokePenalty = -80;
                std::vector<std::pair<int64_t,pattern>> sorted_xdimers = dimerise(stell, cfier, laterMaxDepth);

                earlyPriorityLimit = sorted_xdimers .begin()->first + earlyMaxDepth;
                uint64_t nAllBespokes=0;

                for (auto it = cgfiles[datadir+"bespoke"].btargets.begin(); it != cgfiles[datadir+"bespoke"].btargets.end(); ++it) {
                    pattern sterm(lab, lab->_string32(*it), rule);
                    bitworld bw = stell.match(sterm).flatlayer(0);
                    while (bw.population()) {
                        int64_t bbox[4] = {0};
                        bitworld onecell = bw.get1cell();
                        bw -= onecell;
                        onecell.getbbox(bbox);
                        pattern subset = sterm(bbox[0], bbox[1]);
                        avoid += subset;
                        bitworld fsubset = subset.flatlayer(0);
                        fsubset.getbbox(bbox);
                        int64_t priority = bespokePenalty - 2*(bbox[0] * 2 + bbox[2] + bbox[1] * 2 + bbox[3] - 2);
                        nAllBespokes++;
                        if (priority<earlyPriorityLimit) {
                            sorted_dimers.push_back(std::make_pair(priority,subset));
                        }
                    }
                }
                nbespoke = sorted_dimers.size(); //bespokes are filtered by priority, but unsorted at front of others
                std::cout << nAllBespokes << "/" << nbespoke << "/";
                if (nbespoke==0) { laterMaxDepth = earlyMaxDepth;}
                int64_t laterPriorityLimit = earlyPriorityLimit - earlyMaxDepth + laterMaxDepth;

                uint64_t nEarlyTargets=nbespoke;
                for (uint64_t i = 0; i < sorted_xdimers.size(); i++) {
                    if (sorted_xdimers[i].first>=laterPriorityLimit) {break;}
                    if ((sorted_xdimers[i].second & avoid).empty()) {
                        if (sorted_xdimers[i].first<earlyPriorityLimit) {nEarlyTargets++;}
                        sorted_dimers.push_back(sorted_xdimers[i]);
                    }
                }
                std::cout << nEarlyTargets << "/" << sorted_dimers.size() << "/" << earlyMaxDepth << "/" << laterMaxDepth << std::endl;
                depthStLen[depth] = 1.1; //dummy ... not 0.0 yet
            } else {
                sorted_dimers=depthSDimers[depth];
                avoid=depthavoid[depth];
                nbespoke=depthnbespoke[depth];
                nblocks=depthnblocks[depth];
                earlyPriorityLimit=depthEarlyPriorityLimit[depth];
            }

            double stelllength = depthStLen[depth];

            if ((depth>0)&&(stelllength<0.5)) {
                std::cout << "\033[36;1mAlgorithm terminated with an opimal ST.\033[0m" << std::endl;
                return orig;
            }

            pattern eglider(lab, "3o$o$bo!", rule);

            int budget = (nbespoke>20) ? nbespoke + 10 : 30; // budget rarely stops the state==0 phase
            bool promissingBlockExists = false;
            /*if (state != 0) {

                std::cout << "Obtained " << (freshDimers?"fresh ":"") << depthDimers[depth].size() << " dimers/monomers";

                if (nbespoke) { std::cout << " (including " << nbespoke << " bespoke objects)"; }
                std::cout << "." << std::endl;

                bitworld env = to_env(stell).flatlayer(0);
                bitworld live = stell.flatlayer(0);

                std::vector<bitworld> clusters = cfier.getclusters(live, env, true);
                for (uint64_t i = 0; i < clusters.size(); i++) {
                    if (clusters[i].population() < MIN_EXCLUSION_SIZE) { smallobj += 1; }
                }
                std::cout << smallobj << " objects of < " << MIN_EXCLUSION_SIZE << " cells." << std::endl;
            }

            if (state == 0) {
                std::cout << "Obtained " << (freshDimers?"fresh ":"") << depthDimers[depth].size() << " dimers/monomers";
                if (nbespoke) { std::cout << " (including " << nbespoke << " bespoke objects)"; }
                std::cout << " budget " << budget << "." << std::endl;
            }*/

            // Display dimers:
            for (uint64_t i = 0; i < sorted_dimers.size(); i++) {
                int64_t priority = sorted_dimers[i].first; pattern dimer = sorted_dimers[i].second;
                bitworld live = dimer.flatlayer(0);
                bitworld env = to_env(dimer).flatlayer(0);
                int64_t envbbox[4] = {0};
                env.getbbox(envbbox);

                pattern remainder = stell - dimer;
                pattern dcs = dimer.convolve(sword);

                if ((dcs & remainder).nonempty() && (dcs(12, 0) & remainder).nonempty() && (dcs(0, 12) & remainder).nonempty()) { continue; }
                // ^ one of the 3 must be empty ?! I would expect pattern dcs = dimer.convolve(extSword) ... (dcs & remainder).nonempty()

                std::map<std::string, int64_t> counts = cfier.census(live, env);
                std::ostringstream ss;
                uint64_t totobj = 0;

                for (auto it = counts.begin(); it != counts.end(); ++it) {
                    if (totobj != 0) { ss << "__"; }
                    if (it->second != 0) {
                        ss << it->first;
                        if (it->second != 1) { ss << "(" << it->second << ")"; }
                    }
                    totobj += it->second;
                }

                std::string lexrepr = ss.str();
                std::vector<std::string> prefices;

                if (i < nbespoke) {prefices.push_back("bespoke");}
                bool oneblock = false;
                if (lexrepr == "xs4_33") {
                    if (state == 0) { nblocks++;continue; }
                    if ((sorted_dimers.size() == 1) && (stell.popcount((1 << 30) + 3)==4)) { oneblock = true; }
                    prefices.push_back("longmove");
                } else if (initbail>5) {continue; // initbail 1->1->3->9->27...}
                } else if ((lexrepr == "xs6_696") || (lexrepr == "xs4_252")) {
                    if ((initbail==1) == (priority >= earlyPriorityLimit)) { continue; }
                    if (initbail==1) {
                        budget--;
                        if ((state == 0) && (budget == 0)) { break; }
                        if ((state != 0) && (budget>0)) {continue; } //processed in budget last time
                    }
                    prefices.push_back("edgy/xs4_33");
                } else {
                    if ((initbail==1) == (priority >= earlyPriorityLimit)) { continue; }
                    if (initbail==1) {
                        budget--;
                        if ((state == 0) && (budget == 0)) { break; }
                        if ((state != 0) && (budget>0)) {continue; } //processed in budget last time
                    }
                    prefices.push_back("edgy/xs4_33");
                    if (totobj == 1) { //monomer
                        prefices.push_back("edgy/xs6_696");
                        prefices.push_back("edgy/xs4_252");
                    }
                }

                if (lexrepr == "xs4_33") { // test the block is promissing
                    if (initbail>2) {promissingBlockExists = true;}
                    if (promissingBlockExists) {
                        if (initbail>2) {
                            if ((nbespoke == 0) && (nblocks>1)) {
                                double altstelllength = stlength(remainder - avoid, depthECCBoundary[depth], cfier);
                                if (altstelllength > stelllength - 15.0) {
                                    pattern empty(lab,"",rule);
                                    preparedepth(depth+1,empty,remainder,cfier); // no need to prepare spanning tree for shallow search
                                    std::cout << "\033[33;1m";
                                    pattern newpatt = precurse(remainder, cfier, 0, 1, 1, 0, ideal, depth+1, 2);
                                    std::cout << "\033[0m";
                                    if (newpatt == remainder) {
                                       continue;
                                    }
                                }
                            }
                        }
                    } else {
                        if ((nbespoke > 0)||oneblock) { promissingBlockExists = true;}
                        else {
                            double altstelllength = stlength(remainder - avoid, depthECCBoundary[depth], cfier);
                            if (altstelllength <= stelllength - 15.0) {
                                promissingBlockExists = true;
                            } else {
                                pattern empty(lab,"",rule);
                                preparedepth(depth+1,empty,remainder,cfier); // no need to prepare spanning tree for shallow search
                                std::cout << "\033[35;1m";
                                pattern newpatt = precurse(remainder, cfier, 0, 1, 1, 0, ideal, depth+1, 2);
                                std::cout << "\033[0m";
                                if (newpatt != remainder) {
                                     promissingBlockExists = true;
                                } else {
                                   continue;
                                }
                            }
                        }
                    }
                }

                uint64_t bdiff = 0;
                if (oneblock) {
                    if (ideal != 0) {
                        int64_t bbox2[4] = {0};
                        dimer.getrect(bbox2);
                        bbox2[0] -= ideal[0];
                        bbox2[1] -= ideal[1];
                        bdiff = (bbox2[0] * bbox2[0]) + (bbox2[1] * bbox2[1]);
                    }
                    if (bdiff == 0) {
                        std::cout << "\033[36;1mAlgorithm terminated with single block.\033[0m" << std::endl;
                        return orig;
                    }
                }

                for (uint64_t z = 0; z < prefices.size(); z++) {
                   bool is_bespoke = (prefices[z] == "bespoke");
                   std::string filename = datadir + prefices[z];
                   if (!is_bespoke) {
                       filename += ("/" + lexrepr);
                   }
                   assertfile(filename);

                   for (uint64_t j = 0; j < (is_bespoke ? 1 : 4); j++) {
                      pattern tlt = dimer.shift(-envbbox[0], -envbbox[1]);
                      if (j & 1) {
                          if (tlt == tlt[1]) { continue; }
                          tlt = tlt[1];
                      }
                      if (j & 2) {
                          if (lexrepr == "xs4_33") { continue; }
                          //if (lexrepr == "xs4_252") { continue; } ??
                          tlt = tlt.transpose();
                      }

                      //std::cout << i << lexrepr << " z " << z << " j " << j << std::endl;

                      auto it = cgfiles[filename].sdata.find(tlt._string32());
                      if (it != cgfiles[filename].sdata.end()) {
                          uint64_t trycount = 0;
                          for (uint64_t k = 1; k < it->second.size(); k++) {
                              uint64_t n_recipes = it->second[k].size();
                              double tree_req_progress = 4*k-initbail/10;
                              if (tree_req_progress<15.0) {
                                  tree_req_progress=15.0;
                              }
                              if (lexrepr == "xs4_33") {
                                  if (trycount > tree_bailout) { break; }
                                    //std::cout << "Attempting to construct xs4_33 with " << k << " gliders (" << n_recipes << " recipes)" << std::endl;
                              }
                              //std::cout << i << lexrepr << " z " << z << " j " << j <<  " k " << k<< std::endl;

                              for (uint64_t l = 0; l < n_recipes; l++) {
                                  cgsalvo<int16_t> cs = it->second[k][l].second;
                                  std::string srcstr = it->second[k][l].first;
                                  pattern source(lab, lab->_string32(srcstr), rule);
                                  // Determine whether it is worth proceeding:
                                  bool trythis = false;
                                  pattern xlt = source.shift(-cs.dx, -cs.dy);
                                  pattern altstell = remainder + xlt.shift(envbbox[0], envbbox[1]);
                                  double altstelllength = stelllength;
                                  uint64_t altbdiff = 0;
                                  bool oneblock_improvement = false;
                                  //std::cout << i << lexrepr << " z " << z << " j " << j << " k " << k << " l " << l << std::endl;

                                  if (oneblock) {
                                      int64_t bbox2[4] = {0};
                                      xlt.shift(envbbox[0], envbbox[1]).getrect(bbox2);
                                      bbox2[0] -= ideal[0];
                                      bbox2[1] -= ideal[1];
                                      altbdiff = (bbox2[0] * bbox2[0]) + (bbox2[1] * bbox2[1]);
                                      oneblock_improvement = (std::sqrt((double) altbdiff) <= std::sqrt((double) bdiff) - 25.0 + 0.1 * initbail);
                                      trythis = (altbdiff == 0) || oneblock_improvement;
                                  } else if (lexrepr == "xs4_33") {
                                      /*if (trycount == deep_bailout) {
                                          //std::cout << "Reached bailout " << deep_bailout << " for strategy 'deep'" << std::endl;
                                      } else if (trycount == tree_bailout) {
                                          //std::cout << "Reached bailout " << tree_bailout << " for strategy 'tree'" << std::endl;
                                      } else*/
                                      if (trycount > tree_bailout) { break; }
                                      altstelllength = stlength(altstell - avoid, depthECCBoundary[depth], cfier);
                                      trythis = (altstelllength <= stelllength - tree_req_progress) || (trycount < deep_bailout) || (nbespoke >= 1);
                                  } else {
                                      trythis = true;
                                  }

                                  if (trythis) {
                                     pattern slt = source;
                                     for (uint64_t m = 0; m < cs.gliders.size(); m++) {
                                         std::pair<int16_t, uint8_t> ng = cs.gliders[m];
                                         int64_t posback = (m + 1) * 128;
                                         slt += eglider(posback + ng.first, posback)[ng.second];
                                     }
                                     uint64_t j2 = (cs.transpose ? 2 : 0) + cs.age;
                                     j2 ^= j;
                                     slt = slt.shift(-cs.dx, -cs.dy);
                                     pattern xlt = source.shift(-cs.dx, -cs.dy);
                                     if (j2 & 1) { slt = slt[1]; xlt = xlt[1]; }
                                     if (j2 & 2) { slt = slt.transpose(); xlt = xlt.transpose(); }

                                     pattern sltshift = slt.shift(envbbox[0], envbbox[1]);
                                     pattern newpat = sltshift + remainder;

                                     //std::cout << i << lexrepr << " z " << z << " j " << j << " k " << k << " l " << l << " trying" << std::endl;

                                     if (newpat[512 * (cs.gliders.size() + 1)] == stell) {
                                        if ((sltshift.convolve(sword) & remainder).empty()) {
                                            // std::cout << "Good match!" << std::endl;
                                        } else {
                                            // std::cout << "Inaccessible from infinity" << std::endl;
                                            continue;
                                        }
                                        int64_t posback = (cs.gliders.size() + 2) * 128;
                                        newpat += exsalvo(posback, posback);
                                        pattern altstell = remainder + xlt.shift(envbbox[0], envbbox[1]);

                                        if (oneblock) {
                                            if (altbdiff == 0) {
                                                std::cout << "\033[32;1mInitial block correctly emplaced\033[0m" << std::endl;
                                                return newpat;
                                            } else if (oneblock_improvement) {
                                                std::cout << "\033[32;1mInitial block moved towards target\033[0m" << std::endl;
                                                return newpat;
                                            }
                                        } else if (lexrepr == "xs4_33") {
                                            pattern newpat2 = newpat;
                                            if ((trycount >= last_deep_bailout) && (trycount < deep_bailout)) {
                                                // We now create a really large sword to ensure only the BR-most
                                                // block is moved by the 'deep' strategy:
                                                pattern qlt = sltshift.convolve(bigblock).convolve(bigblock);
                                                qlt += qlt(8, 8).convolve(bigblock).convolve(bigblock);
                                                if ((qlt.convolve(sword) & remainder).empty()) {
                                                    pattern empty(lab,"",rule);
                                                    preparedepth(depth+1,empty,newpat2,cfier); // no need to prepare spanning tree for shallow search
                                                    std::cout << "\033[36;1m";
                                                    newpat2 = precurse(newpat, cfier, 0, 1, 1, 0, ideal, depth+1, 1);
                                                    std::cout << "\033[0m";
                                                    std::cout << "back to precurse A" << std::endl;
                                                }
                                            }
                                            if (newpat != newpat2) {
                                                stat_deep++;
                                                std::cout << (double) clock() << " \033[32;1mdeep   \033[0m" ;
                                                std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                                std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                                std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
				  			                    std::cout << " predeep population " << stell.popcount((1 << 30) + 3) << " --> ";
                                                std::cout << altstell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr << std::endl;
                                                return newpat2;
                                            }

                                            if (trycount >= last_tree_bailout) {
                                                double nutil_old = stelllength;
                                                double nutil_new = altstelllength;
                                                if (nbespoke > 0) {
                                                    pattern barrier = avoid.convolve(sword);
                                                    pattern altrem = altstell - avoid, rem = stell - avoid;
                                                    altrem = altrem.convolve(utilhash), rem = rem.convolve(utilhash);
                                                    /*altrem += altrem(0, 6); altrem += altrem(6, 0);
                                                    altrem += altrem(0, 12); altrem += altrem(12, 0);
                                                    rem += rem(0, 6); rem += rem(6, 0); // ?? why not convolve?
                                                    rem += rem(0, 12); rem += rem(12, 0); */
                                                    uint64_t pop1 = 0;
                                                    uint64_t pop2 = 0;
                                                    for (uint64_t zz = 0; zz < 4; zz++) {
                                                        pop2 += (barrier & altrem).popcount((1 << 30) + 3);
                                                        pop1 += (barrier & rem).popcount((1 << 30) + 3);
                                                        barrier = barrier.convolve(sword).convolve(sword);
                                                    }
                                                    nutil_old += 0.1 * pop1;
                                                    nutil_new += 0.1 * pop2;
                                                }
                                                if (nutil_new <= nutil_old - tree_req_progress) {
                                                    stat_tree++;
                                                    std::cout << (double) clock() << " \033[32;1mtree   \033[0m: ";
                                                    std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                                    std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                                    std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
				  			                        std::cout << " population " << stell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr;
                                                    std::cout << " loss " << nutil_old << " --> " << nutil_new << std::endl;
                                                    depthStLen[depth] = altstelllength;
                                                    return newpat;
                                                }
                                            }
                                        } else {
                                            if (shallowFlags<2) {
                                                if (is_bespoke) {
                                                    stat_bespoke++;
                                                    std::cout << (double) clock() << " \033[32;1mbespoke\033[0m: ";
                                                } else if (totobj == 1) {
                                                    stat_reduce++;
                                                    std::cout << (double) clock() << " \033[32;1mreduce \033[0m: ";
                                                } else {
                                                    stat_split++;
                                                    std::cout << (double) clock() << " \033[32;1msplit  \033[0m: ";
                                                }
                                            } else {
                                                std::cout << (double) clock() << " promise ";
                                            }
                                            std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                            std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                            std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
                                            std::cout << " population " << stell.popcount((1 << 30) + 3) << " --> ";
                                            std::cout << altstell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr << std::endl;

                                            return newpat;
                                        }
                                     }
                                  }
                                  trycount += 1;
                              }
                          }
                      }
                   }
                }
            }
            // std::cout << "--------------------------------" << std::endl;
            depthSDimers[depth]=sorted_dimers;
            depthavoid[depth]=avoid;
            depthnbespoke[depth]=nbespoke;
            depthnblocks[depth]=nblocks;
            depthEarlyPriorityLimit[depth]=earlyPriorityLimit;
            if ((state == 0) && (shallowFlags==0)) {
                depthStLen[depth] = stlength(stell - avoid, depthECCBoundary[depth], cfier);
                std::cout << "stelllength " << depthStLen[depth] << std::endl;
            }

            if ((maxbail != 0) && (initbail < maxbail)) {
                if (promissingBlockExists) {
                    std::cout << "Increasing bailout to " << (initbail * 3) << std::endl;
                    pattern ret=precurse(orig, cfier, state, initbail * 3, maxbail, initbail, ideal, depth);
                    std::cout << "back to precurse B" << std::endl;
                    return ret;
                } else {
                    std::cout << "no promissing block ... ";
                }
            }
            std::cout << "no improvement found" << std::endl;
            return orig;
        }

        pattern preiterate(pattern initial, classifier &cfier, int64_t *ideal, uint64_t depth, pattern extra) {
            std::cout << "preiterate " << ((ideal==0)?"0":"i") << " " << depth << " " << extra.popcount((1 << 30) + 3) << std::endl;
            pattern pcend = initial;
            pattern pcstart(lab, "", rule);
            pattern gliders(lab, "", rule);
            preparedepth(depth,extra,initial,cfier);

            //uint64_t chunk1minpop = 8, chunk2minpop = 8;
            while (pcstart != pcend) {

                pcstart = pcend & pcend[8];
                pattern salvo = pcend - pcstart;
                if (salvo.nonempty() && gliders.nonempty()) {

                    int64_t diag = 0;
                    int64_t gliders_bbox[4] = {0};
                    int64_t pcend_bbox[4] = {0};

                    gliders.getrect(gliders_bbox);
                    pcend.getrect(pcend_bbox);

                    int64_t horizontal = (pcend_bbox[0] + pcend_bbox[2] - gliders_bbox[0]);
                    int64_t vertical   = (pcend_bbox[1] + pcend_bbox[3] - gliders_bbox[1]);

                    if (diag < horizontal) { diag = horizontal; }
                    if (diag < vertical) { diag = vertical; }

                    diag += 512;
                    diag -= (diag & 255);

                    gliders = gliders(diag, diag);
                }

                gliders += salvo;
                if (clock()>=depthNextSaveTime[depth]) {
                    depthNextSaveTime[depth] = clock()+30*CLOCKS_PER_SEC;
                    // save progress
                    std::ostringstream ss;
                    ss << "current" << depth << ".mc";
                    std::ofstream out(ss.str());
                    (pcstart + gliders).write_macrocell(out);
                }

                if (depth>0) {
                    if (depthStLen[depth]<0.5) {
                        std::cout << "\033[36;1m Reached optimal ST -> break.\033[0m" << std::endl;
                        break;
                    }
                }

                pattern inpat2 = pcstart;

                if (has_isolated_block(pcstart)) {
                    pattern chunk = get_lowerright(pcstart, inpat2, 8, 32, cfier, extra); //
                    if (chunk != pcstart) {
                        pattern remainder = pcstart - chunk;
                        pcend = remainder + preiterate(chunk, cfier, 0, depth+1, extra + remainder);
                        std::cout << "back to preiterate A " << depth << " " << depthStLen[depth+1];
                        if (pcend == pcstart) {
                            std::cout << " with no progress" << std::endl;
                        } else {
                            std::cout << " with a progress" << std::endl;
                            continue;
                        }
                    }
                    inpat2 -= get_isolated_block(inpat2);
                }

                pcend = precurse(pcstart, cfier, 0, 1, 1, 0, ideal, depth);
                std::cout << "back to preiterate B " << depth << " " << depthStLen[depth];
                if (pcend != pcstart) {
                    std::cout << " with a progress" << std::endl;
                    continue;
                }

                if (depth>0) {
                    if (depthStLen[depth]<0.5) {
                        std::cout << "\033[36;1m with optimal ST -> break.\033[0m" << std::endl;
                        break;
                    }
                }

                std::cout << " with no progress" << std::endl;

                if (inpat2.nonempty()) {
                    pattern chunk = get_lowerright(pcstart, inpat2, 8, 25, cfier, extra);
                    if (chunk != pcstart) {
                        pattern remainder = pcstart - chunk;
                        pcend = remainder + preiterate(chunk, cfier, 0, depth+1, extra + remainder);
                        std::cout << "back to preiterate C " << depth << " " << depthStLen[depth+1];
                        if (pcend == pcstart) {
                            std::cout << " with no progress" << std::endl;
                        } else {
                            std::cout << " with a progress" << std::endl;
                            continue;
                        }
                    }
                }

                // attempt deeper constructions:
                pcend = precurse(pcstart, cfier, 1, 1, 4096, 0, ideal, depth);
                std::cout << "back to preiterate D " << depth << " " << depthStLen[depth];

                if (pcend != pcstart) {
                    std::cout << " with a progress" << std::endl;
                    continue;
                }

                if (depth>0) {
                    if (depthStLen[depth]<0.5) {
                        std::cout << "\033[36;1m with optimal ST -> break.\033[0m" << std::endl;
                        break;
                    }
                }

                std::cout << " ending with no progress" << std::endl;
            }

            return (pcstart + gliders);
        }

        pattern preiterate(pattern initial, classifier &cfier, int64_t *ideal, uint64_t depth = 0) {
            std::cout << "preiterate0 " << ((ideal==0)?"0":"i") << " " << depth  << std::endl;
            lab = initial.getlab();
            rule = initial.getrule();

            pattern extra(lab, "", rule);
            return preiterate(initial, cfier, ideal, depth, extra);
        }

        pattern preiterate(pattern initial, classifier &cfier) {
            pattern findme(initial.getlab(), "3b2o$2bo2bo$bob2obo$obo2bobo$obo2bobo$bob2obo$2bo2bo$3b2o!", initial.getrule());
            pattern m = initial.match(findme);
            pattern im = initial - m.convolve(findme);

            if (m.empty()) {
                return preiterate(im, cfier, 0, 0);
            } else {
                int64_t ideal[4] = {0};
                m(3, 3).getrect(ideal);
                return preiterate(im, cfier, ideal, 0);
            }
        }

    };

}
May be next "plenty of time" could be spent on dynamization, but there are still situations (with bespoke active) when a lot of steps are tried unsuccessfully, what majorizes the running time and I do not expect it could be avoided.

Comparision with version on previous post:
8315/38->8259/28, 8347/27->8225/28, 8372/39->8357/72!!!, 8310/38->8249/29, 8344/33->8222/29.
Seems there could be problems sometimes, but overall an improvement (I do not think the filtering current*.mc progress influenced the times much).

---------------

I start thinking about giving this topics to some students as a practice in codding ... dynamic voronoy/delanau maintainance adding/removing cluster centers. (Point location from voronoy allow easy classify pattern differences and invoke the dynamic change) persistent techniques could be helpfull if deeper search will be introduced rather to current greedy approach (but seems it works well).
cluster centers (vertices of delanau ... monomers, pairs of vertices close in delanau graph are dimers, recompute dimers containing newly created cluster is just short search around the cluster (in delanau) once a dimer is created its lexrepr could be computed (surely as well for clusters))
MST subgraph of Delanau ...

The frontier with priorities as in my variant of the search would specify recepies for which dimers/monomers/bespokes should be tried, (maintaining dimmers + monomers in the priority order whole time and bespokes in another "queue")
(partially persistent) dynamic maintainance of MST could help in evaluation of tree moves quickly (but not in utility computation to "avoid avoid").

Such dynamic approach would allow after examining dimmer make notion there is no reason to try it again (as there is no recipe for it) or if there is just one recipe (applies also for cluster/bespoke) blocked by another cluster ... make note not to test it while the cluster is present.

This would probably save a lot of work done now (when several targets are tested repeatedly after each update even when they are still blocked by he same easiy tested reason.

I did some experiments with preparing lexrepr's for given dimmer in precurse, to use the shortest salvo among all targets (dead end when frontier was not used), but the problem was a lot of preparation with an early hit and the preparation was thrown away. But in dynamic mainainance scenario this neednot be toally off (global priority queue of recipes with implicit delete (variant with small positive integer keys)? ... allowing us to remove an item temporarily ... to "after blocking cluster removal reinsert storage").

With dynamic mainainance there will be no need for recursion calls from preiterate (no need to locate chunks).

So far I got to salvo of 8178 gliders (for RiskDBCA_m3_p4_fc_#cpo) ... 8166 after about 105 more atempts with various infile.mc s... 8178->(32)8177->(30)8176->(18)8174->(20)8168->(4)8164.

It seems it will be hard to find a student to do the dynamization of the underlying graph, I have just checked for dynamic maintainance of a delaunay tree (on delaunay triangulation for point location) and it expects updates are at random ... and in our case there are very biased to maximal x+y.
I am not sure how much should it be changed to work well in our case (I did not went through the paper thoroughly yet).
Current heuristic is rather good in finding recipes for the firtst target on the list, but when this is not the case, it spends a lot of time trying the target over and over even in cases the obsacle is still there. The more such targets are there, the process slows more. Dynamic mainanance of targets and tried recipes ... would fight against this problem. (Even the test ... the deep strategy is worth trying would be for free if we maintain a list of recipes the block is obstacle for.)

I have tried positioning all parameers independently and finally got (working) 8144 gliders recipe.
Running the cycle once again could make an improvement, but I bet I am close to a local minimum obtainable by my current version of slsparse, so I would continue on other stages of RCT15 build process. I still have in mind the dynamization of slmake would probably lead to significant performance improvement maybe even allowing some search rather to stay just greedy (except the shallow search in block monomers procesing).

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » May 5th, 2023, 1:11 pm

Great work on improving both the runtime and the glider cost!

I'm going to make a modification to the code to add 'diagnostics'. A diagnostic will be produced each time a progress step is made, and it will contain the change in spanning tree length, number of gliders used in that step, and change in the quantities of every object. For example, it might be:
Step reduced spanning tree length by 11.37, constructed a loaf (xs7_2596), and consumed a block (xs4_33), taking 6 gliders.
This could be viewed as a datapoint for a linear model:

Code: Select all

6 = 11.37 * stlength + xs7_2596 - xs4_33 + residual
When running, slmake will be able to generate lots of these diagnostics (one for each step of each construction, so hundreds of diagnostics for something like a DBCA or ECCA). Running this over a large library of test patterns (Herschel tracks, reflectors, signal circuitry, construction arms, etc.) will enable tens of thousands of datapoints to be collected, sufficient to run a linear regression to find out the average glider cost of each type of object and each unit of spanning tree length.

Having a trained linear model would be particularly helpful for a variety of reasons: it would give a cost estimate that can be evaluated on a given pattern in milliseconds, compared with the minutes/hours needed to run slmake, and the linear estimate would be much less noisy. It would be a huge improvement over using population as a metric (which is equivalent to the very crude linear model where each object costs its cell count and each unit of spanning tree length costs zero), so it would help human designers of circuitry to decide between alternative solutions.

Most importantly, though, having a cheap noiseless cost estimate is exactly what we need for a beam search: we'd do a breadth-first tree search and restrict to the N patterns which minimise the sum "(number of gliders used so far) + (estimated cost to build the remainder of the pattern)". Over the course of the execution of the beam search, that sum will drift from the original estimate to the actual glider count in the best salvo that it finds.

An improved linear model could then be trained on the diagnostics from the steps of the best salvo from running the beam search on each test pattern in our library, which would in turn make the beam search more accurate. (This should very quickly converge; I wouldn't expect significant benefits from further iterations.)
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » May 6th, 2023, 8:38 am

calcyman wrote:
May 5th, 2023, 1:11 pm
Great work on improving both the runtime and the glider cost!

:) thanks
calcyman wrote:
May 5th, 2023, 1:11 pm
I'm going to make a modification to the code to add 'diagnostics'. A diagnostic will be produced each time a progress step is made, and it will contain the change in spanning tree length, number of gliders used in that step, and change in the quantities of every object. For example, it might be:
Step reduced spanning tree length by 11.37, constructed a loaf (xs7_2596), and consumed a block (xs4_33), taking 6 gliders.
This could be viewed as a datapoint for a linear model:

Code: Select all

6 = 11.37 * stlength + xs7_2596 - xs4_33 + residual
When running, slmake will be able to generate lots of these diagnostics (one for each step of each construction, so hundreds of diagnostics for something like a DBCA or ECCA). Running this over a large library of test patterns (Herschel tracks, reflectors, signal circuitry, construction arms, etc.) will enable tens of thousands of datapoints to be collected, sufficient to run a linear regression to find out the average glider cost of each type of object and each unit of spanning tree length.

Having a trained linear model would be particularly helpful for a variety of reasons: it would give a cost estimate that can be evaluated on a given pattern in milliseconds, compared with the minutes/hours needed to run slmake, and the linear estimate would be much less noisy. It would be a huge improvement over using population as a metric (which is equivalent to the very crude linear model where each object costs its cell count and each unit of spanning tree length costs zero), so it would help human designers of circuitry to decide between alternative solutions.

Most importantly, though, having a cheap noiseless cost estimate is exactly what we need for a beam search: we'd do a breadth-first tree search and restrict to the N patterns which minimise the sum "(number of gliders used so far) + (estimated cost to build the remainder of the pattern)". Over the course of the execution of the beam search, that sum will drift from the original estimate to the actual glider count in the best salvo that it finds.

An improved linear model could then be trained on the diagnostics from the steps of the best salvo from running the beam search on each test pattern in our library, which would in turn make the beam search more accurate. (This should very quickly converge; I wouldn't expect significant benefits from further iterations.)
Yep, that could help. It is almost perpendicullar to what I have in the long term plan ... to maintain pattern dynamically rather recomputing pattern characteristics after each change. That would probably enable to mainain informations such as ... this cluster X (or bespoke) cannot be processed while other cluster Y exists so X would be removed from the priority queue until Y is transformed ... preventing a lot of "nothing to do work".
I would definitely want to try building rather "functional paterns" than fixed patterns and dynamic maintainance provides better understanding so it could help in this task.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » May 8th, 2023, 12:33 pm

I've gathered 14570 diagnostics from various different input patterns taken from self-constructing circuitry. Running a linear regression produced the following glider costs associated with each object:

Code: Select all

costs["stlength"] = 0.09;
costs["xp2_7"] = 4.67;
costs["xs4_33"] = 4.93;
costs["xs6_696"] = 6.15;
costs["xs8_6996"] = 6.85;
costs["xs7_2596"] = 7.14;
costs["xs4_252"] = 7.26;
costs["xs5_253"] = 7.43;
costs["xs6_356"] = 7.67;
costs["xs6_25a4"] = 8.16;
costs["xs7_25ac"] = 8.68;
costs["xs8_69ic"] = 10.1;
costs["xs12_g8o653z11"] = 10.5;
costs["xs7_178c"] = 11.2;
costs["xs6_39c"] = 13.7;
costs["xs11_g0s453z11"] = 14.3;
costs["xs9_31ego"] = 14.5;
costs["xs8_3pm"] = 14.7;
costs["xs12_330fho"] = 14.8;
costs["xs6_bd"] = 15.1;
costs["xs8_178k8"] = 15.1;
costs["xs8_25ak8"] = 15.3;
costs["xs10_178kk8"] = 15.4;
costs["xs10_32qr"] = 15.5;
costs["xs8_35ac"] = 17.4;
costs["xp8_wgovnz234z33"] = 28.7;
costs["xp8_gk2gb3z11"] = 29.6;
costs["xp8_g3jgz1ut"] = 33;
costs["xs19_4aar0rbzx121"] = 39.8;
costs["xs14_354cgs26"] = 41.4;
costs["xs17_39u08kcz321"] = 52.4;
costs["xs31_0g89fgkcz8llljgzx641"] = 64.1;
For objects not on this list, we can estimate their cost as population*2.
Hippo.69 wrote:
May 6th, 2023, 8:38 am
calcyman wrote:
May 5th, 2023, 1:11 pm
Great work on improving both the runtime and the glider cost!

:) thanks
Quick question: slmake occasionally 'concentrates' on a subset of the pattern (and does so recursively). The original behaviour was to minimise the spanning tree of that active subset, whereas it looks like you may have changed the behaviour to minimise the spanning tree of the whole pattern. Unfortunately, that causes some serious slowdowns in the following situation:
  • The active subset of the pattern contains a block near the front, and separated from the rest of the active pattern (so that block has priority over the other monomers/dimers).
  • As such, slmake tries to move this block to reduce the spanning tree length (understandably).
  • However, there's an edge-case where the inactive part of the pattern is at the other side of the block, so moving the block cannot decrease the total spanning tree length.
This causes a lot of cyan lines to be printed, and the bailout increases to very high values (suggesting that something isn't working properly -- the point of the bailout was so that it 'gives up' on a block and tries other objects in the pattern, but that doesn't seem to be happening here?).
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » May 8th, 2023, 5:11 pm

calcyman wrote:
May 8th, 2023, 12:33 pm
I've gathered 14570 diagnostics from various different input patterns taken from self-constructing circuitry. Running a linear regression produced the following glider costs associated with each object:

Code: Select all

costs["stlength"] = 0.09;
costs["xp2_7"] = 4.67;
costs["xs4_33"] = 4.93;
costs["xs6_696"] = 6.15;
costs["xs8_6996"] = 6.85;
costs["xs7_2596"] = 7.14;
costs["xs4_252"] = 7.26;
costs["xs5_253"] = 7.43;
costs["xs6_356"] = 7.67;
costs["xs6_25a4"] = 8.16;
costs["xs7_25ac"] = 8.68;
costs["xs8_69ic"] = 10.1;
costs["xs12_g8o653z11"] = 10.5;
costs["xs7_178c"] = 11.2;
costs["xs6_39c"] = 13.7;
costs["xs11_g0s453z11"] = 14.3;
costs["xs9_31ego"] = 14.5;
costs["xs8_3pm"] = 14.7;
costs["xs12_330fho"] = 14.8;
costs["xs6_bd"] = 15.1;
costs["xs8_178k8"] = 15.1;
costs["xs8_25ak8"] = 15.3;
costs["xs10_178kk8"] = 15.4;
costs["xs10_32qr"] = 15.5;
costs["xs8_35ac"] = 17.4;
costs["xp8_wgovnz234z33"] = 28.7;
costs["xp8_gk2gb3z11"] = 29.6;
costs["xp8_g3jgz1ut"] = 33;
costs["xs19_4aar0rbzx121"] = 39.8;
costs["xs14_354cgs26"] = 41.4;
costs["xs17_39u08kcz321"] = 52.4;
costs["xs31_0g89fgkcz8llljgzx641"] = 64.1;
For objects not on this list, we can estimate their cost as population*2.
I think we should evaluate the objects diferently depending on their rotation. That would give us better estimates ...
(semisnark looks cheaper with boat in some rotation and state than with tube and vice versa).
calcyman wrote:
May 8th, 2023, 12:33 pm
Quick question: slmake occasionally 'concentrates' on a subset of the pattern (and does so recursively). The original behaviour was to minimise the spanning tree of that active subset, whereas it looks like you may have changed the behaviour to minimise the spanning tree of the whole pattern. Unfortunately, that causes some serious slowdowns in the following situation:
  • The active subset of the pattern contains a block near the front, and separated from the rest of the active pattern (so that block has priority over the other monomers/dimers).
  • As such, slmake tries to move this block to reduce the spanning tree length (understandably).
  • However, there's an edge-case where the inactive part of the pattern is at the other side of the block, so moving the block cannot decrease the total spanning tree length.
This causes a lot of cyan lines to be printed, and the bailout increases to very high values (suggesting that something isn't working properly -- the point of the bailout was so that it 'gives up' on a block and tries other objects in the pattern, but that doesn't seem to be happening here?).
Yes, I am not sure how much we need the recursive portion of the slsparse. When I already use filtering by the "frontier".
There was almost fatal problem with 3 blocks remaining were the closest is in the middle and the remaining two atracted to different sides ... where there was no possible improvement in the spaning tree length. I was happy that I found some solution preventing "infinite recursion" for this case. Yes we probably describe the same situation. Where running time increased to almost the time of original slparse :( and I did experimented with only one "functional pattern".

I have experimented with returning small chunks back, but that didnot work well with the recursive approach.
I definitely do not want to use recursive approach in the planned rebuild. Feel free to experiment ....

Hmm thinking about it the problem is that there is part of the pattern out of current chunk, which is connected by almost straight line through current chunk to rest of the pattern. If we found a motivation to solve "smaller" of isolated parts before the rest of small current chunk ... it would solve our problem ... . (Extend the chunk by the smaller of two neighbours)

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » May 8th, 2023, 7:16 pm

Hippo.69 wrote:
May 8th, 2023, 5:11 pm
Yes, I am not sure how much we need the recursive portion of the slsparse. When I already use filtering by the "frontier".
The recursive divide-and-conquer approach is because slmake's runtime per step is (at least) linear in the number of objects, so the total runtime would be (at least) quadratic. Whereas this allows slmake to forget about the rest of the pattern and consequently run much more quickly.
There was almost fatal problem with 3 blocks remaining were the closest is in the middle and the remaining two atracted to different sides ... where there was no possible improvement in the spaning tree length.
I'm somewhat confused: it seems like removing the 'eccenters' from the spanning tree calculation solves this problem. That way, it will try to minimise the spanning tree of just this chunk, rather than the pattern as a whole (and commenting out the loop which concatenates eccenters to sccenters empirically works). Is there a reason that you added eccenters in the first place?
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » May 9th, 2023, 1:51 am

calcyman wrote:
May 8th, 2023, 7:16 pm
Hippo.69 wrote:
May 8th, 2023, 5:11 pm
Yes, I am not sure how much we need the recursive portion of the slsparse. When I already use filtering by the "frontier".
The recursive divide-and-conquer approach is because slmake's runtime per step is (at least) linear in the number of objects, so the total runtime would be (at least) quadratic. Whereas this allows slmake to forget about the rest of the pattern and consequently run much more quickly.
There was almost fatal problem with 3 blocks remaining were the closest is in the middle and the remaining two atracted to different sides ... where there was no possible improvement in the spaning tree length.
I'm somewhat confused: it seems like removing the 'eccenters' from the spanning tree calculation solves this problem. That way, it will try to minimise the spanning tree of just this chunk, rather than the pattern as a whole (and commenting out the loop which concatenates eccenters to sccenters empirically works). Is there a reason that you added eccenters in the first place?
Sure, without eccenters you tend to solve the chunk to the nearest neighbour, creating long edge among the rest of the patern. Than you have to solve this extra long move. What I should do is to remember which parts of out of chunk pattern belong to which eccenter (with minimal x+y?) and when the chunk becomes small, I should finish working on it and instead extend chunk by the part with maximal minimal x+y.

That would tend to do the shrinking of the spanning tree gradually.

When I filter out processing dimmers with small x+y, the chunking is not that much important.
Hmmm ... OK paterns of long wide V of blocks with angle about 170 degrees (40 degrees, than 50 in absolute) would be the hedeache of both the strategies.
Original strategy would create twice as big salvo, while the eccenters strategy creates donkee with two carrots problem.

May be the filtering strategy by maximal x+y is wrong. Filtering by the V (not using chunks) from the cluster with maximal x+y would prevent the
two carrots problem. Just think about correct filtering angle the long jumps in the middle become efective enough.

(I am not sure how it would work In the scenario we know which clusers prevent what moves ... should I maintain the two neighbouring V clusters "pulling back" as "disjunctivly" preventing the long move ... for all moves so the leading block disappears from the frontier ... what would apply to its neighbours as well (beeing preventing disjunctivly by it and another neighbour ...) till a cluster faraway from the V edge is changed what means reconsidering move in neighbour prevented by the changed cluster ... It seems to me this couild be the solution ... waiting for dynamic maintainance of the pattern/underlying graph of clusters).

... uff I did the work delayed due to Conway game of Life and I can look at CgoL again ...
The way eccenters are computed does not identify the spanning tree components outside the chunk. So that would require an extra processing ... but I do not think it is necessary unless eccenters size == 2 (no Donkey carrot for other cases).
I am inclining to let prepare depth create output list to be fed to "pcend and extra" at the start of preiterate ... And preiterate should return both (pcstart+gliders) and extra ... that would allow to correct donkey + 2 carrots split and if V filtering would be added, it should work much better.
Hmm, there will be problem with preiterate trying to split o chunk and "the small carrot" extra again. May be carrot should become new variable the search works with.

Maybe we could change chunk definition to consider the V frontier and terminate preierate whenever extra enters the V frontier. In that case the returned chunk will not be created again and the recursion would follow naturally without need of extra/carrot hacking.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » May 9th, 2023, 7:44 am

These are excellent points! I especially like your analogy to https://en.wikipedia.org/wiki/Buridan%27s_ass
When I filter out processing dimmers with small x+y, the chunking is not that much important.
That's reassuring. I'd like to get rid of the chunking as well, because it makes both the beam search and the following idea (donkey avoidance) much simpler:
Hippo.69 wrote:
May 9th, 2023, 1:51 am
So that would require an extra processing ... but I do not think it is necessary unless eccenters size == 2 (no Donkey carrot for other cases).
A simpler idea (which doesn't depend on the distinction between eccenters and other vertices) might be the following: when we compute the spanning tree, we describe a block as a 'donkey' if either:
  • it has exactly 2 neighbours in the spanning tree, and the angle between them is >= 135 degrees;
  • it has >= 3 neighbours in the spanning tree, and it lies within their convex hull.
We can check these conditions very cheaply (integer-only arithmetic, no trigonometry). If the vectors pointing from the block to each of its neighbours are {(x_1, y_1), ..., (x_n, y_n)}, then the first condition is just:

n=2 and x_1 y_1 + x_2 y_2 + abs(x_2 y_1 - x_1 y_2) <= 0

and the second condition is saying that for every neighbour i, there exist neighbours j and k such that:

x_i y_j - x_j y_i is strictly positive and x_i y_k - x_k y_i is strictly negative

Since the spanning graph (of which the spanning tree is a subgraph) was constructed in such a way that every vertex has n <= 8 neighbours, and this check only takes O(n^2) integer operations, the runtime of the donkey check is utterly negligible.

Now that we have a succinct definition of a 'donkey', we can skip over donkeys when iterating through the objects. Note that, in particular, every tree has at least 2 leaves (degree-1 vertices), and a leaf of the spanning tree can never be a donkey, so we can't get stuck in the situation where all objects are donkeys.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » May 9th, 2023, 10:27 am

calcyman wrote:
May 9th, 2023, 7:44 am
A simpler idea (which doesn't depend on the distinction between eccenters and other vertices) might be the following: when we compute the spanning tree, we describe a block as a 'donkey' if either:
  • it has exactly 2 neighbours in the spanning tree, and the angle between them is >= 135 degrees;
  • it has >= 3 neighbours in the spanning tree, and it lies within their convex hull.
I do not think 3 neighbours is the problem. We want to order "in backwards view" the targets prefering those closest to "source of salvo".
I think I have actually increased the constraint for which block move is acceptable not to do only block migration.
When a block is close to line segment connecting its closest neighbours, its removal almost does not change spanning tree length. Moving anywhere could hardly shrink the spanning length. So two neighbours are the problem. (Three neighbours in the spanning tree usually means, there is another neighbour to be processed with higher priority.)

Hmm, still thinking about it, but V would not help (in general) ... for a pattern ... big horseshoe (almost circle ... opened on the distant end) consisting of only blocks. The soluion could be "deep move" sidesteping a nearby block than hitting the furthermost block to sufficiently shorten the spanning tree. A lot of deep moves could be required to get rid of one of carrots. The strategy to concentrate on just one carrot would work better for this case.

Following idea does not match well to this case, but there is also an option to skip building several blocks ... that would lead to salvos requiring several starting blocks ... and we could be forced to do deep moves... (But for example for DBCA, I needed to move the starting block 10 times (if I remember well) by 36 steps in manhatan metrics. It would be cheaper to left several blocks behind me, and if required starting blocks will be nearby, it would be cheaper to start from these remnants than building them from one block. But let's not complicate the matters now...

May be we should create an incentive to move the nearest blocks forward for the donkey carrot case. That would work with flat frontier and after a while the leaves would become active starting shrinking the spanning tree.
(Stopping work with chunk when an eccenter's x+y becomes the biggest).
That would force next time the chunk would contain the eccenter.

I hope I will make some experiments already tonight ....
Than I should again mostly work on the ECCA.

I am currently experimenting with an antidonkey strategy, but I need some Donkey problem causing infiles for testing. (I got 7887 on your DBCA version with trash start SW on first attempt, just starting experiments with parametrisation ... but the hack have not applied).
The evaluation is not still debugged, but I am experimenting with prefering donkey preventing moves to increasing bailout, what applies even to the tested pattern ... will it really increase the glider count? ... yep 7964. ... let me fix the evaluation ... now when the evaluation is not completely wrong, I got 7887. I prioritized it before "yellow and purple", but not before "cyan" :) Actually it always used 3 gliders salvo and the local problem was always solved by just one such salvo ... just the 3 gliders salvo was deeper than the deep strategy had time to find. So further experiments could show it is better to use this "heuristics" than trying deep at all. Now I am going to skip evaluating donkey when there are not exactly two carrots. I hope it would work well giving reasobably small salvos while maintaining high speed.

Seems there was 2 carrots problem just twice during solving the pattern, and both times it was solved naturally without using the heuristic.
(resulting in 7887 gliders big salvo ... probably different...(golly estimated the xor of these two paterns to took 50 minutes). So I definitely need infile with donkey - carrot problem to be able to test changes.

Oh the salvos are the same ... surprisingly, when in one case deep strategy was used and in another "donkey prevention". (On several places in the salvo). I am somehow confused. Oh all three 7887 result have the same salvo (when L1 optimization is turned off).

I have tried to ignore the 2 carrots condition and initbail condition as well ... and with the tuned evauation it produced 7864 salvo.
So it could really be better strategy than to go for deep.

Code: Select all

#pragma once

#include "../lifelib/pattern2.h"
#include "../lifelib/spantree.h"
#include "../lifelib/classifier.h"
#include "../lifelib/ssplit.h"
#include <set>
#include <cstdlib>
#include <sstream>
#include <algorithm>

#define MIN_EXCLUSION_SIZE 32

/*
* Functionality for constructing constellations of still-lifes.
*/

namespace apg {

    /**
     * Computes the period-8 envelope of a pattern
     */
    pattern to_env(pattern x) {

        pattern y = x;
        pattern z = x;

        for (int i = 0; i < 7; i++) {
            z = z[1];
            y += z;
        }

        return y;
    }

    std::vector<coords64> getccenters(std::vector<bitworld> clusters) {
        std::vector<coords64> ccenters;
            for (uint64_t i = 0; i < clusters.size(); i++) {
                // Get bounding box:
                int64_t bbox[4] = {0};
                clusters[i].getbbox(bbox);
                int64_t mx2 = bbox[0] * 2 + bbox[2] - 1;
                int64_t my2 = bbox[1] * 2 + bbox[3] - 1;
                coords64 m2(mx2, my2);
                ccenters.push_back(m2);
            }
        return ccenters;
    }


    double stlength(pattern stell, std::vector<coords64> eccenters, classifier &cfier) {

        bitworld env = to_env(stell).flatlayer(0);
        bitworld live = stell.flatlayer(0);

        std::vector<bitworld> clusters = cfier.getclusters(live, env, false);
        std::vector<coords64> ccenters = getccenters(clusters);
        const uint64_t ccenters_size = ccenters.size();

        for(std::vector<coords64>::iterator it = eccenters.begin(); it != eccenters.end(); ++it) {
            ccenters.push_back(*it);
        }

        dsds colours(ccenters_size+1);
        std::vector<edge64> sgraph = spanning_graph(ccenters);

        // Compute squared length of edges with an endpoint in stell:
        std::vector<std::pair<int64_t, edge64> > sedges;
        for (std::vector<edge64>::iterator it = sgraph.begin(); it != sgraph.end(); ++it) {
            if ((it->first < ccenters_size)||(it->second < ccenters_size)) {
                coords64 a = ccenters[it->first];
                coords64 b = ccenters[it->second];
                int64_t xdiff = a.first - b.first;
                int64_t ydiff = a.second - b.second;
                int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
                sedges.push_back(std::make_pair(sqlength, *it));
            }
        }

        // Sort edges into ascending order:
        std::sort(sedges.begin(), sedges.end());

        double length=0;
        // Apply Kruskal's algorithm:
        for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
            uint64_t a = it->second.first;
            uint64_t b = it->second.second;
            bool internal=true;
            if (a >= ccenters_size) { a = ccenters_size; internal=false;}
            if (b >= ccenters_size) { b = ccenters_size; internal=false;} // vertices of extra has the same color
            if (!colours.connected(a, b)) {
                colours.merge(a, b);
                if (internal) { length+= std::sqrt(it->first); }
            }
        }
        return 0.5 * length;
    }

    pattern diagonalise(pattern inp) {
        /*
        * Create a diagonal line longer than the diameter of a pattern.
        */

        int64_t bbox[4] = {0};
        inp.getrect(bbox);

        pattern diagonal(inp.getlab(), "o$bo$2bo$3bo$4bo$5bo$6bo$7bo$8bo$9bo$10bo$11bo$12bo$13bo$14bo$15bo!", inp.getrule());
        for (uint64_t i = 4; i < 64; i++) {
            if (((1 << i) >= bbox[2]) && ((1 << i) >= bbox[3])) { break; }
            diagonal += diagonal(1 << i, 1 << i);
        }

        return diagonal;
    }

    pattern get_exclusions(pattern inpat, classifier &cfier) {

        auto lab = inpat.getlab();

        pattern excl(lab, "", "b3s23");

        bitworld live = inpat.flatlayer(0);
        std::vector<bitworld> clusters = cfier.getclusters(live, live, true);
        for (uint64_t i = 0; i < clusters.size(); i++) {
            if (clusters[i].population() >= MIN_EXCLUSION_SIZE) {
                excl += pattern(lab, lab->demorton(clusters[i], 1), "b3s23");
            }
        }

        return excl;
    }

    uint64_t excluded_popcount(pattern inpat, classifier &cfier) {
        pattern excl = get_exclusions(inpat, cfier);
        return (inpat - excl).popcount((1 << 30) + 3);
    }

    pattern cell_lowerright(pattern inpat) {
        // Get a reasonably lower-right cell of an input pattern.
        bitworld bw = inpat.flatlayer(0);
        bw = bw.br1cell();
        auto lab = inpat.getlab();
        pattern brcell(lab, lab->demorton(bw, 1), inpat.getrule());
        return brcell;
    }

    pattern bounding_hull(pattern x) {

        int64_t bbox[4] = {0};
        x.getrect(bbox);

        pattern y(x.getlab(), "bbo$bo$o!", "b3s23");
        y = y.shift(-1, -1);

        uint64_t p = bbox[2] + bbox[3];
        while (p > 0) {
            p = p >> 1;
            y = y.convolve(y);
        }

        return x.convolve(y).subrect(bbox);
    }

    pattern get_isolated_block(pattern inpat) {

        auto lab = inpat.getlab();
        pattern convrect(lab, lab->rectangle(-20, -20, 41, 41), "b3s23");
        return inpat & cell_lowerright(inpat).convolve(convrect);

    }

    bool has_isolated_block(pattern inpat) {

        pattern x = get_isolated_block(inpat);
        int64_t bbox[4] = {0};
        x.getrect(bbox);
        return (bbox[2] == 2) && (bbox[3] == 2);

    }

    pattern get_lowerright(pattern inpat, pattern inpat2, uint64_t minpop, int radius, classifier &cfier, pattern extra) {
        // Get a lower-right chunk of an input pattern.
        auto lab = inpat.getlab();
        pattern convrect(lab, lab->rectangle(-radius, -radius, 2 * radius + 1, 2 * radius + 1), "b3s23");
        pattern icell = cell_lowerright(inpat2);
        pattern diag = diagonalise(inpat);
        pattern sword(lab, "o$bo$2bo$3b2o$3b2o!", "b3s23");

        for (int i = 0; i < 6; i++) {
            sword = sword.convolve(sword);
        }

        sword = sword.convolve(diag).convolve(convrect);
        pattern chunk(lab, "", "b3s23");

        for (;;) {
            pattern newchunk = chunk + icell;

            while (chunk != newchunk) {
                chunk = newchunk & inpat;
                newchunk = bounding_hull(chunk).convolve(sword) & inpat;
            }

            pattern rempat = inpat - chunk;

            if (rempat.empty()) { break; }

            uint64_t epc = excluded_popcount(chunk, cfier);
            if (epc < minpop) {
                icell = icell.convolve(convrect);
            } else {
                std::cout << "\033[36;1mFound " << epc;
                std::cout << "-cell chunk; " << excluded_popcount(rempat, cfier);
                std::cout << " + " << extra.popcount((1 << 30) + 3);
                std::cout << " cells remain.\033[0m" << std::endl;
                break;
            }
        }
        return chunk;
    }

    std::vector<std::pair<int64_t,pattern>> dimerise(pattern stell, classifier &cfier, int64_t maxDepth) {
        /*
        * Find promising still-life pairs and individual still-lifes
        * which the compiler has a good chance of being able to build.
        */

        /*
          dimers have monomer center distances at most 25\sqrt{2} so Manhattan distance at most 50
          for a monomer A center of the most distance dimmer containing A is from center of A in Manhattan distance at most 25
          We want A to be processed after last dimmer containing A so we should penalise A by Manhattan distance > 25
          We use 2 points coordinates so the penalty should be > 50
          (After that we try to reduce a tub or beehive to block, and other monomer than tub, beehive or block to tub, beehive or block)

          Monomer blocks are processed differently, we transform them to blocks, but we should try to improve a
          chance it will create dimmer to be processed.
          The heuristic trying to shorten (global) minimum spanning forest looks well as the most distant blocks tend to move
          towards the rest.

          moving block to Manhattan distance 36 is rather cheap, it seems to me they should be
          "invited to follow monomers" from around such a distance.

        */

        const int64_t diPenalty = 0;
        // bespokePenalty=-80 < -50 to prefere be broken to clusters before the clusters could become part of active dimers

        const int64_t monoPenalty = 51;

        const int64_t blockPenalty = 120;

        std::vector<std::pair<int64_t,pattern>> sorted_dimers;
        //std::vector<std::vector<pattern> > sdimers;

        bitworld env = to_env(stell).flatlayer(0);
        bitworld live = stell.flatlayer(0);

        std::vector<bitworld> clusters = cfier.getclusters(live, env, true);
        std::vector<coords64> ccenters;

        std::set<std::pair<int64_t, edge64>> edgelist_sorted;

        for (uint64_t i = 0; i < clusters.size(); i++) {
            // Get bounding box:
            int64_t bbox[4] = {0};
            clusters[i].getbbox(bbox);
            int64_t mx2 = bbox[0] * 2 + bbox[2] - 1;
            int64_t my2 = bbox[1] * 2 + bbox[3] - 1;
            coords64 m2(mx2, my2);
            ccenters.push_back(m2);

            // penalty to ensure that singleton blocks appear after everything else:
            int64_t pen = ((bbox[2] == 2) && (bbox[3] == 2)) ? blockPenalty : monoPenalty;

            edgelist_sorted.emplace(pen - 2*(mx2 + my2), edge64(i, i));
        }

        std::vector<edge64> edgelist_unsorted = spanning_graph(ccenters);

        for (uint64_t i = 0; i < edgelist_unsorted.size(); i++) {
            edge64 edge = edgelist_unsorted[i];

            int64_t x1 = ccenters[edge.first].first;
            int64_t y1 = ccenters[edge.first].second;
            int64_t x2 = ccenters[edge.second].first;
            int64_t y2 = ccenters[edge.second].second;

            if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > 5000) { continue; }
            edgelist_sorted.emplace(diPenalty - (x1 + y1 + x2 + y2), edge);
        }

        lifetree_abstract<uint32_t>* lab = stell.getlab();
        std::set<edge64> edgedump;

        int64_t priorityLimit = edgelist_sorted.begin()->first + maxDepth;
        uint64_t ActiveMonomers=0;

        for (auto it = edgelist_sorted.begin(); it != edgelist_sorted.end(); ++it) {
            if (it->first >= priorityLimit) {break;}
            edge64 edge = it->second;
            if (edge.first == edge.second) {
                // Include monomers:
                ActiveMonomers++;
                uint64_t i = edge.first;
                pattern monomer(lab, lab->demorton(clusters[i], 1), stell.getrule());
                sorted_dimers.push_back(std::make_pair(it->first,monomer));
            } else {
                // Include dimers:
                uint64_t idLo = (edge.first < edge.second) ? edge.first : edge.second;
                uint64_t idHi = (edge.first > edge.second) ? edge.first : edge.second;
                bitworld bw = clusters[edge.first];
                bw += clusters[edge.second];
                pattern dimer(lab, lab->demorton(bw, 1), stell.getrule());
                edge64 newedge(idLo, idHi);
                if (edgedump.count(newedge) == 0) {
                    sorted_dimers.push_back(std::make_pair(it->first,dimer));
                    edgedump.insert(newedge);
                }
            }
        }
        std::cout << "considered clusters/dimers/monomers/bespokes/active bespokes/early targets/later targets/early maxDepth/later maxdepth: " <<
             clusters.size() << "/"<< sorted_dimers.size()-ActiveMonomers << "/" << ActiveMonomers << "/";
        return sorted_dimers;
    }

    template <typename T> struct cgsalvo {

        std::vector<std::pair<T, char> > gliders;
        T dx;
        T dy;
        bool age;
        bool transpose;

        void glidermatch(pattern pat) {

            std::map<std::pair<int64_t, int64_t>, uint8_t> gmap;
            std::vector<pattern> matches;

            pattern a_glider(pat.getlab(), "3o$o$bo!", pat.getrule());

            int64_t bbox[4] = {0};

            for (uint64_t i = 0; i < 4; i++) {
                bitworld bw = pat.match(a_glider).flatlayer(0);
                a_glider = a_glider[1];

                while (bw.population()) {
                    bitworld onecell = bw.get1cell();
                    bw -= onecell;
                    onecell.getbbox(bbox);
                    int64_t lane = (bbox[0] - bbox[1]);
                    uint8_t phase = (uint8_t) (((bbox[1] & 1) << 2) + i);
                    gmap[std::pair<int64_t, int64_t>(bbox[0] + bbox[1], lane)] = phase;
                }
            }

            for (auto it = gmap.begin(); it != gmap.end(); ++it) {
                T lane = it->first.second;
                uint8_t phase = it->second;
                gliders.emplace_back(lane, phase);
            }
        }

        std::pair<pattern, pattern> frompattern(pattern pat) {

            int64_t bbox[4] = {0};
            pattern fin = pat[1 << 20];
            
            to_env(fin).getrect(bbox);
            pattern centred = pat(-bbox[0], -bbox[1]);
            fin = fin(-bbox[0], -bbox[1]);

            pattern target = centred & centred[8];
            pattern gliders = centred - target;

            glidermatch(gliders);

            dx = 0;
            dy = 0;
            age = 0;
            transpose = 0;

            return std::pair<pattern, pattern>(target, fin);
        }

        void fromline(std::string line) {
            std::vector<std::string> colons = string_split(line, ':');
            if (colons.size() >= 3) {

                std::string t = colons[colons.size() - 2];
                std::vector<std::string> gstrings = string_split(t, ' ');

                for (uint64_t i = 0; i < gstrings.size(); i++) {
                    std::string g = gstrings[i];
                    if (g != "") {
                        char lastchar = g[g.length() - 1];
                        if ((lastchar == 'E') || (lastchar == 'O')) {
                            T j = (std::stoll(g.substr(0, g.length() - 1)) - 1) / 2;
                            gliders.emplace_back(j, ((lastchar == 'E') ? 0 : 1));
                        }
                    }
                }

                std::string s = colons[colons.size() - 3];
                std::replace(s.begin(), s.end(), '(', ' ');
                std::replace(s.begin(), s.end(), ')', ' ');
                std::replace(s.begin(), s.end(), ',', ' ');
                std::vector<std::string> hstrings = string_split(s, ' ');
                std::vector<std::string> hstrings2;

                for (uint64_t i = 0; i < hstrings.size(); i++) {
                    std::string h = hstrings[i];
                    if (h != "") { hstrings2.push_back(h); }
                }
                if (hstrings2.size() == 3) {
                    dx = std::stoll(hstrings2[0]);
                    dy = std::stoll(hstrings2[1]);
                    transpose = (hstrings2[2] == "T");
                }

                std::string r = colons[colons.size() - 1];
                if (r.find("o") != std::string::npos) { age = 1; }
                if (r.find("e") != std::string::npos) { age = 0; }

            }
        }

    };

    struct cgfile {
        std::map<std::string, std::vector<std::vector<std::pair<std::string, cgsalvo<int16_t>>>>> sdata;
        std::set<std::string> btargets;

        void digestmc(std::string filename, lifetree_abstract<uint32_t> *lab) {

            pattern x(lab, filename);
            uint64_t period = x[1 << 20].ascertain_period();
            std::cout << " -- " << filename << " has period " << period << "." << std::endl;

            for (uint64_t i = 0; i < period; i++) {
                for (uint64_t j = 0; j < 2; j++) {
                    cgsalvo<int16_t> cg;
                    std::pair<pattern, pattern> respair = cg.frompattern(x);
                    std::string source = respair.first._string32();
                    std::string target = respair.second._string32();
                    btargets.insert(target);

                    if (sdata[target].size() <= cg.gliders.size()) {
                        sdata[target].resize(cg.gliders.size() + 1);
                    }
                    sdata[target][cg.gliders.size()].emplace_back(source, cg);

                    x = x.transpose();
                }
                x = x[1];
            }
        }

        void readfile(std::string filename, lifetree_abstract<uint32_t> *lab, std::string rule) {
            std::ifstream f(filename);
            std::string line;
            std::string rlesofar;
            std::string target;
            std::string source;
            bool readingsrc = false;

            if (!f.good()) { return; }

            std::cout << "Reading file " << filename << "..." << std::endl;
            while (std::getline(f, line)) {
                if (line.empty()) { continue; }
                char c = line[0];

                if ((c == ' ') || (c == '*')) {
                    if (rlesofar != "") { rlesofar += "$"; }
                    rlesofar += line;
                } else if (rlesofar != "") {
                    std::replace( rlesofar.begin(), rlesofar.end(), ' ', 'b');
                    std::replace( rlesofar.begin(), rlesofar.end(), '*', 'o');
                    rlesofar += "!";
                    // std::cout << rlesofar << std::endl;
                    pattern p(lab, rlesofar, rule);
                    if (readingsrc) { source = p._string32(); } else { target = p._string32(); }
                    rlesofar = "";
                }

                if (c == '-') {
                    if (line.find("Source") != std::string::npos) {
                        readingsrc = true;
                    } else if (line.find("Target") != std::string::npos) {
                        readingsrc = false;
                    }
                } else if (rlesofar == "") {
                    cgsalvo<int16_t> cg;
                    cg.fromline(line);
                    if (cg.gliders.size() != 0) {
                        if (sdata[target].size() <= cg.gliders.size()) {
                            sdata[target].resize(cg.gliders.size() + 1);
                        }
                        sdata[target][cg.gliders.size()].emplace_back(source, cg);
                    }
                }
            }
            std::cout << "..." << filename << " successfully read." << std::endl;
        }
    };

    struct cghq {
        /*
        * Collection of slow-salvo recipes
        */

        std::map<std::string, cgfile> cgfiles;
        std::string rule;
        std::string datadir;
        lifetree_abstract<uint32_t> *lab;
        uint64_t stat_bespoke=0, stat_deep=0, stat_tree=0, stat_split=0, stat_reduce=0;

        uint64_t assertfile(std::string filename) {
            if (cgfiles.count(filename) == 0) {
                cgfiles[filename].readfile(filename, lab, rule);
            }
            return cgfiles[filename].sdata.size();
        }

        void assertbespoke(std::string dirname) {
            if (cgfiles.count(dirname) == 0) {
                std::ifstream f(dirname + "/filelist.txt");
                std::string line;
                while (std::getline(f, line)) {
                    std::string filename = dirname + "/" + line;
                    cgfiles[dirname].digestmc(filename, lab);
                }
            }
        }
        std::vector<pattern> depthExtras;
        std::vector<std::vector<coords64>> depthECCBoundary;
        std::vector<std::vector<std::pair<int64_t,pattern>>> depthSDimers;
        std::vector<pattern> depthavoid;
        std::vector<std::uint64_t> depthnbespoke;
        std::vector<std::uint64_t> depthnblocks;
        std::vector<std::int64_t> depthEarlyPriorityLimit;
        std::vector<std::double_t> depthStLen;
        std::vector<std::double_t> depthNextSaveTime;
        std::vector<pattern> depthBestDonkey;
        std::vector<std::int64_t> depthBestDonkeyEval;


        void preparedepth(uint64_t depth, pattern extra, pattern work, classifier &cfier) {
            if (depthSDimers.size()==depth) {
                std::vector<std::pair<int64_t,pattern>> empty;
                depthSDimers.push_back(empty);
                pattern dummy(lab,"",rule);
                depthExtras.push_back(dummy);
                std::vector<coords64> noccoords;
                depthECCBoundary.push_back(noccoords);
                depthavoid.push_back(dummy);
                depthnbespoke.push_back(0);
                depthnblocks.push_back(0);
                depthEarlyPriorityLimit.push_back(0);
                depthStLen.push_back(1.1);
                depthNextSaveTime.push_back(0.0);
                depthBestDonkey.push_back(dummy);
                depthBestDonkeyEval.push_back(-1);
            }
            depthExtras[depth]=extra;
            depthStLen[depth]=1.1;
            depthBestDonkeyEval[depth]=-1;
            std::vector<coords64> noccoords;
            depthECCBoundary[depth] = noccoords;
            pattern stell = work & work[8];

            bitworld env = to_env(stell).flatlayer(0), live = stell.flatlayer(0);
            std::vector<bitworld> sclusters = cfier.getclusters(live, env, false);

            env = to_env(extra).flatlayer(0); live = extra.flatlayer(0);
            std::vector<bitworld> eclusters = cfier.getclusters(live, env, false);

            std::vector<coords64> sccenters,eccenters,accenters;
            sccenters=getccenters(sclusters);
            eccenters=getccenters(eclusters);

            for (uint64_t i = 0; i < sccenters.size(); i++) {
                accenters.push_back(sccenters[i]);
            }

            for (uint64_t i = 0; i < eccenters.size(); i++) {
                accenters.push_back(eccenters[i]);
            }

            dsds colours(sccenters.size()+1);
            std::vector<edge64> sgraph = spanning_graph(accenters);

            // Compute squared length of edges with an endpoint in stell:
            std::vector<std::pair<int64_t, edge64> > sedges;
            for (std::vector<edge64>::iterator it = sgraph.begin(); it != sgraph.end(); ++it) {
                if ((it->first < sccenters.size())||(it->second < sccenters.size())) {
                    coords64 a = accenters[it->first];
                    coords64 b = accenters[it->second];
                    int64_t xdiff = a.first - b.first;
                    int64_t ydiff = a.second - b.second;
                    int64_t sqlength = (xdiff * xdiff) + (ydiff * ydiff);
                    sedges.push_back(std::make_pair(sqlength, *it));
                }
            }

            // Sort edges into ascending order:
            std::sort(sedges.begin(), sedges.end());

            // Apply Kruskal's algorithm:
            std::set<uint64_t> eboundary;
            for (std::vector<std::pair<int64_t, edge64> >::iterator it = sedges.begin(); it != sedges.end(); ++it) {
                uint64_t a = it->second.first;
                uint64_t b = it->second.second;
                uint64_t e = 0;
                if (a >= sccenters.size()) { e = a - sccenters.size(); a = sccenters.size(); }
                if (b >= sccenters.size()) { e = b - sccenters.size(); b = sccenters.size(); } // vertices of extra has the same color
                if (!colours.connected(a, b)) {
                    colours.merge(a, b);
                    if (e != 0) { eboundary.emplace(e); }
                }
            }

            for(std::set<uint64_t> :: iterator it = eboundary.begin(); it != eboundary.end(); ++it) {
               depthECCBoundary[depth].push_back(eccenters[*it]);
            }

        }

        pattern precurse(pattern orig, classifier &cfier, int state, int initbail, int maxbail, uint32_t lastbail, int64_t *ideal, uint64_t depth, uint64_t shallowFlags = 0) {

            std::cout << "precurse " << state << " " << initbail << " " << maxbail << " " << lastbail << " " << ((ideal==0)?"0":"i") << " " << depthExtras[depth].popcount((1 << 30) + 3) << " " << depth << std::endl;

            assertbespoke(datadir+"bespoke");

            pattern stell = orig & orig[8];
            pattern exsalvo = orig - stell;
            pattern diagonal = diagonalise(stell);
            pattern smallblock(lab, "2o$2o!", rule);
            pattern bigblock(lab, "4o$4o$4o$4o!", rule);
            pattern utilhash(lab,"o5bo5bo5bo5$o5bo5bo5bo5$o5bo5bo5bo5$o5bo5bo5bo5!",rule);

            pattern sword = diagonal.convolve(bigblock);
            /*pattern swordExtender(lab,"o12o12$o!",rule);
            pattern extSword = sword.convolve(swordExtender);*/

            uint64_t deep_bailout = initbail - 1, last_deep_bailout = lastbail - 1;
            uint64_t tree_bailout = initbail * 100, last_tree_bailout = lastbail * 100;
            const int64_t earlyMaxDepth = 150;
            int64_t laterMaxDepth = 220; if (shallowFlags>0) {laterMaxDepth = earlyMaxDepth;}

            std::vector<std::pair<int64_t,pattern>> sorted_dimers;
            uint64_t nbespoke, nblocks=0;
            pattern avoid(lab, "", rule);
            int64_t earlyPriorityLimit;

            if ((state==0)) {
                const int64_t bespokePenalty = -80;
                std::vector<std::pair<int64_t,pattern>> sorted_xdimers = dimerise(stell, cfier, laterMaxDepth);

                earlyPriorityLimit = sorted_xdimers .begin()->first + earlyMaxDepth;
                uint64_t nAllBespokes=0;
                depthBestDonkeyEval[depth]=-1;

                for (auto it = cgfiles[datadir+"bespoke"].btargets.begin(); it != cgfiles[datadir+"bespoke"].btargets.end(); ++it) {
                    pattern sterm(lab, lab->_string32(*it), rule);
                    bitworld bw = stell.match(sterm).flatlayer(0);
                    while (bw.population()) {
                        int64_t bbox[4] = {0};
                        bitworld onecell = bw.get1cell();
                        bw -= onecell;
                        onecell.getbbox(bbox);
                        pattern subset = sterm(bbox[0], bbox[1]);
                        avoid += subset;
                        bitworld fsubset = subset.flatlayer(0);
                        fsubset.getbbox(bbox);
                        int64_t priority = bespokePenalty - 2*(bbox[0] * 2 + bbox[2] + bbox[1] * 2 + bbox[3] - 2);
                        nAllBespokes++;
                        if (priority<earlyPriorityLimit) {
                            sorted_dimers.push_back(std::make_pair(priority,subset));
                        }
                    }
                }
                nbespoke = sorted_dimers.size(); //bespokes are filtered by priority, but unsorted at front of others
                std::cout << nAllBespokes << "/" << nbespoke << "/";
                if (nbespoke==0) { laterMaxDepth = earlyMaxDepth;}
                int64_t laterPriorityLimit = earlyPriorityLimit - earlyMaxDepth + laterMaxDepth;

                uint64_t nEarlyTargets=nbespoke;
                for (uint64_t i = 0; i < sorted_xdimers.size(); i++) {
                    if (sorted_xdimers[i].first>=laterPriorityLimit) {break;}
                    if ((sorted_xdimers[i].second & avoid).empty()) {
                        if (sorted_xdimers[i].first<earlyPriorityLimit) {nEarlyTargets++;}
                        sorted_dimers.push_back(sorted_xdimers[i]);
                    }
                }
                std::cout << nEarlyTargets << "/" << sorted_dimers.size() << "/" << earlyMaxDepth << "/" << laterMaxDepth << std::endl;
                depthStLen[depth] = 1.1; //dummy ... not 0.0 yet
            } else {
                sorted_dimers=depthSDimers[depth];
                avoid=depthavoid[depth];
                nbespoke=depthnbespoke[depth];
                nblocks=depthnblocks[depth];
                earlyPriorityLimit=depthEarlyPriorityLimit[depth];
            }

            double stelllength = depthStLen[depth];

            if ((depth>0)&&(stelllength<0.5)) {
                std::cout << "\033[36;1mAlgorithm terminated with an opimal ST.\033[0m" << std::endl;
                return orig;
            }

            pattern eglider(lab, "3o$o$bo!", rule);

            int budget = (nbespoke>20) ? nbespoke + 10 : 30; // budget rarely stops the state==0 phase
            bool promissingBlockExists = false;
            /*if (state != 0) {

                std::cout << "Obtained " << (freshDimers?"fresh ":"") << depthDimers[depth].size() << " dimers/monomers";

                if (nbespoke) { std::cout << " (including " << nbespoke << " bespoke objects)"; }
                std::cout << "." << std::endl;

                bitworld env = to_env(stell).flatlayer(0);
                bitworld live = stell.flatlayer(0);

                std::vector<bitworld> clusters = cfier.getclusters(live, env, true);
                for (uint64_t i = 0; i < clusters.size(); i++) {
                    if (clusters[i].population() < MIN_EXCLUSION_SIZE) { smallobj += 1; }
                }
                std::cout << smallobj << " objects of < " << MIN_EXCLUSION_SIZE << " cells." << std::endl;
            }

            if (state == 0) {
                std::cout << "Obtained " << (freshDimers?"fresh ":"") << depthDimers[depth].size() << " dimers/monomers";
                if (nbespoke) { std::cout << " (including " << nbespoke << " bespoke objects)"; }
                std::cout << " budget " << budget << "." << std::endl;
            }*/

            // Display dimers:
            for (uint64_t i = 0; i < sorted_dimers.size(); i++) {
                int64_t priority = sorted_dimers[i].first; pattern dimer = sorted_dimers[i].second;
                bitworld live = dimer.flatlayer(0);
                bitworld env = to_env(dimer).flatlayer(0);
                int64_t envbbox[4] = {0};
                env.getbbox(envbbox);

                pattern remainder = stell - dimer;
                pattern dcs = dimer.convolve(sword);

                if ((dcs & remainder).nonempty() && (dcs(12, 0) & remainder).nonempty() && (dcs(0, 12) & remainder).nonempty()) { continue; }
                // ^ one of the 3 must be empty ?! I would expect pattern dcs = dimer.convolve(extSword) ... (dcs & remainder).nonempty()

                std::map<std::string, int64_t> counts = cfier.census(live, env);
                std::ostringstream ss;
                uint64_t totobj = 0;

                for (auto it = counts.begin(); it != counts.end(); ++it) {
                    if (totobj != 0) { ss << "__"; }
                    if (it->second != 0) {
                        ss << it->first;
                        if (it->second != 1) { ss << "(" << it->second << ")"; }
                    }
                    totobj += it->second;
                }

                std::string lexrepr = ss.str();
                std::vector<std::string> prefices;

                if (i < nbespoke) {prefices.push_back("bespoke");}
                bool oneblock = false;
                if (lexrepr == "xs4_33") {
                    if (state == 0) { nblocks++;continue; }
                    if ((sorted_dimers.size() == 1) && (stell.popcount((1 << 30) + 3)==4)) { oneblock = true; }
                    prefices.push_back("longmove");
                } else if (initbail>5) {continue; // initbail 1->1->3->9->27...}
                } else if ((lexrepr == "xs6_696") || (lexrepr == "xs4_252")) {
                    if ((initbail==1) == (priority >= earlyPriorityLimit)) { continue; }
                    if (initbail==1) {
                        budget--;
                        if ((state == 0) && (budget == 0)) { break; }
                        if ((state != 0) && (budget>0)) {continue; } //processed in budget last time
                    }
                    prefices.push_back("edgy/xs4_33");
                } else {
                    if ((initbail==1) == (priority >= earlyPriorityLimit)) { continue; }
                    if (initbail==1) {
                        budget--;
                        if ((state == 0) && (budget == 0)) { break; }
                        if ((state != 0) && (budget>0)) {continue; } //processed in budget last time
                    }
                    prefices.push_back("edgy/xs4_33");
                    if (totobj == 1) { //monomer
                        prefices.push_back("edgy/xs6_696");
                        prefices.push_back("edgy/xs4_252");
                    }
                }

                if (lexrepr == "xs4_33") { // test the block is promissing
                    if (initbail>2) {promissingBlockExists = true;}
                    if (promissingBlockExists) {
                        if (initbail>5) {//there is big enough chance we will try several deep attempts with no chance
                            if ((nbespoke == 0) && (nblocks>1)) {
                                double altstelllength = stlength(remainder - avoid, depthECCBoundary[depth], cfier);
                                if (altstelllength > stelllength - 15.0) {
                                    pattern empty(lab,"",rule);
                                    preparedepth(depth+1,empty,remainder,cfier); // no need to prepare spanning tree for shallow search
                                    std::cout << "\033[33;1m";
                                    pattern newpatt = precurse(remainder, cfier, 0, 1, 1, 0, ideal, depth+1, 2);
                                    std::cout << "\033[0m";
                                    if (newpatt == remainder) {
                                       continue;
                                    }
                                }
                            }
                        }
                    } else {
                        if ((nbespoke > 0)||oneblock) { promissingBlockExists = true;}
                        else {
                            double altstelllength = stlength(remainder - avoid, depthECCBoundary[depth], cfier);
                            if (altstelllength <= stelllength - 15.0) {
                                promissingBlockExists = true;
                            } else {
                                pattern empty(lab,"",rule);
                                preparedepth(depth+1,empty,remainder,cfier); // no need to prepare spanning tree for shallow search
                                std::cout << "\033[35;1m";
                                pattern newpatt = precurse(remainder, cfier, 0, 1, 1, 0, ideal, depth+1, 2);
                                std::cout << "\033[0m";
                                if (newpatt != remainder) {
                                     promissingBlockExists = true;
                                } else {
                                   continue;
                                }
                            }
                        }
                    }
                }

                uint64_t bdiff = 0;
                if (oneblock) {
                    if (ideal != 0) {
                        int64_t bbox2[4] = {0};
                        dimer.getrect(bbox2);
                        bbox2[0] -= ideal[0];
                        bbox2[1] -= ideal[1];
                        bdiff = (bbox2[0] * bbox2[0]) + (bbox2[1] * bbox2[1]);
                    }
                    if (bdiff == 0) {
                        std::cout << "\033[36;1mAlgorithm terminated with single block.\033[0m" << std::endl;
                        return orig;
                    }
                }

                for (uint64_t z = 0; z < prefices.size(); z++) {
                   bool is_bespoke = (prefices[z] == "bespoke");
                   std::string filename = datadir + prefices[z];
                   if (!is_bespoke) {
                       filename += ("/" + lexrepr);
                   }
                   assertfile(filename);

                   for (uint64_t j = 0; j < (is_bespoke ? 1 : 4); j++) {
                      pattern tlt = dimer.shift(-envbbox[0], -envbbox[1]);
                      if (j & 1) {
                          if (tlt == tlt[1]) { continue; }
                          tlt = tlt[1];
                      }
                      if (j & 2) {
                          if (lexrepr == "xs4_33") { continue; }
                          //if (lexrepr == "xs4_252") { continue; } ??
                          tlt = tlt.transpose();
                      }

                      //std::cout << i << lexrepr << " z " << z << " j " << j << std::endl;

                      auto it = cgfiles[filename].sdata.find(tlt._string32());
                      if (it != cgfiles[filename].sdata.end()) {
                          uint64_t trycount = 0;
                          for (uint64_t k = 1; k < it->second.size(); k++) {
                              uint64_t n_recipes = it->second[k].size();
                              double tree_req_progress = 4*k-initbail/10;
                              if (tree_req_progress<15.0) {
                                  tree_req_progress=15.0;
                              }
                              if (lexrepr == "xs4_33") {
                                  if (trycount > tree_bailout) { break; }
                                    //std::cout << "Attempting to construct xs4_33 with " << k << " gliders (" << n_recipes << " recipes)" << std::endl;
                              }
                              //std::cout << i << lexrepr << " z " << z << " j " << j <<  " k " << k<< std::endl;

                              for (uint64_t l = 0; l < n_recipes; l++) {
                                  cgsalvo<int16_t> cs = it->second[k][l].second;
                                  std::string srcstr = it->second[k][l].first;
                                  pattern source(lab, lab->_string32(srcstr), rule);
                                  // Determine whether it is worth proceeding:
                                  bool trythis = false;
                                  pattern xlt = source.shift(-cs.dx, -cs.dy);
                                  pattern altstell = remainder + xlt.shift(envbbox[0], envbbox[1]);
                                  double altstelllength = stelllength;
                                  uint64_t altbdiff = 0;
                                  bool oneblock_improvement = false;
                                  //std::cout << i << lexrepr << " z " << z << " j " << j << " k " << k << " l " << l << std::endl;

                                  if (oneblock) {
                                      int64_t bbox2[4] = {0};
                                      xlt.shift(envbbox[0], envbbox[1]).getrect(bbox2);
                                      bbox2[0] -= ideal[0];
                                      bbox2[1] -= ideal[1];
                                      altbdiff = (bbox2[0] * bbox2[0]) + (bbox2[1] * bbox2[1]);
                                      oneblock_improvement = (std::sqrt((double) altbdiff) <= std::sqrt((double) bdiff) - 25.0 + 0.1 * initbail);
                                      trythis = (altbdiff == 0) || oneblock_improvement;
                                  } else if (lexrepr == "xs4_33") {
                                      /*if (trycount == deep_bailout) {
                                          //std::cout << "Reached bailout " << deep_bailout << " for strategy 'deep'" << std::endl;
                                      } else if (trycount == tree_bailout) {
                                          //std::cout << "Reached bailout " << tree_bailout << " for strategy 'tree'" << std::endl;
                                      } else*/
                                      if (trycount > tree_bailout) { break; }
                                      altstelllength = stlength(altstell - avoid, depthECCBoundary[depth], cfier);
                                      trythis = (altstelllength <= stelllength - tree_req_progress) || (trycount < deep_bailout) || (nbespoke >= 1);
                                  } else {
                                      trythis = true;
                                  }

                                  if (trythis) {
                                     pattern slt = source;
                                     for (uint64_t m = 0; m < cs.gliders.size(); m++) {
                                         std::pair<int16_t, uint8_t> ng = cs.gliders[m];
                                         int64_t posback = (m + 1) * 128;
                                         slt += eglider(posback + ng.first, posback)[ng.second];
                                     }
                                     uint64_t j2 = (cs.transpose ? 2 : 0) + cs.age;
                                     j2 ^= j;
                                     slt = slt.shift(-cs.dx, -cs.dy);
                                     pattern xlt = source.shift(-cs.dx, -cs.dy);
                                     if (j2 & 1) { slt = slt[1]; xlt = xlt[1]; }
                                     if (j2 & 2) { slt = slt.transpose(); xlt = xlt.transpose(); }

                                     pattern sltshift = slt.shift(envbbox[0], envbbox[1]);
                                     pattern newpat = sltshift + remainder;

                                     //std::cout << i << lexrepr << " z " << z << " j " << j << " k " << k << " l " << l << " trying" << std::endl;

                                     if (newpat[512 * (cs.gliders.size() + 1)] == stell) {
                                        if ((sltshift.convolve(sword) & remainder).empty()) {
                                            // std::cout << "Good match!" << std::endl;
                                        } else {
                                            // std::cout << "Inaccessible from infinity" << std::endl;
                                            continue;
                                        }
                                        int64_t posback = (cs.gliders.size() + 2) * 128;
                                        newpat += exsalvo(posback, posback);
                                        pattern altstell = remainder + xlt.shift(envbbox[0], envbbox[1]);

                                        if (oneblock) {
                                            if (altbdiff == 0) {
                                                std::cout << "\033[32;1mInitial block correctly emplaced\033[0m" << std::endl;
                                                return newpat;
                                            } else if (oneblock_improvement) {
                                                std::cout << "\033[32;1mInitial block moved towards target\033[0m" << std::endl;
                                                return newpat;
                                            }
                                        } else if (lexrepr == "xs4_33") {
                                            pattern newpat2 = newpat;
                                            if ((trycount >= last_deep_bailout) && (trycount < deep_bailout)) {
                                                // We now create a really large sword to ensure only the BR-most
                                                // block is moved by the 'deep' strategy:
                                                pattern qlt = sltshift.convolve(bigblock).convolve(bigblock);
                                                qlt += qlt(8, 8).convolve(bigblock).convolve(bigblock);
                                                if ((qlt.convolve(sword) & remainder).empty()) {
                                                    pattern empty(lab,"",rule);
                                                    preparedepth(depth+1,empty,newpat2,cfier); // no need to prepare spanning tree for shallow search
                                                    std::cout << "\033[36;1m";
                                                    newpat2 = precurse(newpat, cfier, 0, 1, 1, 0, ideal, depth+1, 1);
                                                    std::cout << "\033[0m";
                                                    std::cout << "back to precurse A" << std::endl;
                                                }
                                            }
                                            if (newpat != newpat2) {
                                                stat_deep++;
                                                std::cout << (double) clock() << " \033[32;1mdeep   \033[0m" ;
                                                std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                                std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                                std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
				  			                    std::cout << " predeep population " << stell.popcount((1 << 30) + 3) << " --> ";
                                                std::cout << altstell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr << std::endl;
                                                return newpat2;
                                            }

                                            if (trycount >= last_tree_bailout) {
                                                double nutil_old = stelllength;
                                                double nutil_new = altstelllength;
                                                if (nbespoke > 0) {
                                                    pattern barrier = avoid.convolve(sword);
                                                    pattern altrem = altstell - avoid, rem = stell - avoid;
                                                    altrem = altrem.convolve(utilhash), rem = rem.convolve(utilhash);
                                                    /*altrem += altrem(0, 6); altrem += altrem(6, 0);
                                                    altrem += altrem(0, 12); altrem += altrem(12, 0);
                                                    rem += rem(0, 6); rem += rem(6, 0); // ?? why not convolve?
                                                    rem += rem(0, 12); rem += rem(12, 0); */
                                                    uint64_t pop1 = 0;
                                                    uint64_t pop2 = 0;
                                                    for (uint64_t zz = 0; zz < 4; zz++) {
                                                        pop2 += (barrier & altrem).popcount((1 << 30) + 3);
                                                        pop1 += (barrier & rem).popcount((1 << 30) + 3);
                                                        barrier = barrier.convolve(sword).convolve(sword);
                                                    }
                                                    nutil_old += 0.1 * pop1;
                                                    nutil_new += 0.1 * pop2;
                                                }
                                                if (nutil_new <= nutil_old - tree_req_progress) {
                                                    stat_tree++;
                                                    std::cout << (double) clock() << " \033[32;1mtree   \033[0m: ";
                                                    std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                                    std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                                    std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
				  			                        std::cout << " population " << stell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr;
                                                    std::cout << " loss " << nutil_old << " --> " << nutil_new << std::endl;
                                                    depthStLen[depth] = altstelllength;
                                                    return newpat;
                                                }
                                                if (depthECCBoundary[depth].size()==2) {
                                                    int64_t bbox2[4] = {0};
                                                    xlt.shift(envbbox[0], envbbox[1]).getrect(bbox2);
                                                    int64_t donkeyEval = (120-4*(bbox2[0]+bbox2[1])-priority);
                                                    if (donkeyEval>0) {
                                                        donkeyEval *= (laterMaxDepth-earlyMaxDepth+earlyPriorityLimit-priority)/cs.gliders.size();
                                                    }
                                                    if (donkeyEval>depthBestDonkeyEval[depth]) {
                                                        std::cout << "\033[37;1mDonkeyEvalCalc "<< donkeyEval << " ("<< bbox2[0] << "," << bbox2[1] << ") "
                                                            << earlyPriorityLimit << "-" << priority << "/" << cs.gliders.size() << "\033[0m" << std::endl;
                                                        depthBestDonkeyEval[depth]=donkeyEval;
                                                        depthBestDonkey[depth]=newpat;
                                                        std::cout << "\033[37;1mDonkeyEvalImprovement "<< donkeyEval << "\033[0m" << std::endl;
                                                    }
                                                }
                                            }
                                        } else {
                                            if (shallowFlags<2) {
                                                if (is_bespoke) {
                                                    stat_bespoke++;
                                                    std::cout << (double) clock() << " \033[32;1mbespoke\033[0m: ";
                                                } else if (totobj == 1) {
                                                    stat_reduce++;
                                                    std::cout << (double) clock() << " \033[32;1mreduce \033[0m: ";
                                                } else {
                                                    stat_split++;
                                                    std::cout << (double) clock() << " \033[32;1msplit  \033[0m: ";
                                                }
                                            } else {
                                                std::cout << (double) clock() << " promise ";
                                            }
                                            std::cout << i << " " << z << " " << j << " " << k << " " << l << " + " << cs.gliders.size() ;
                                            std::cout << " " << stat_bespoke << " " << stat_deep << " " << stat_tree << " " << stat_split << " " << stat_reduce;
                                            std::cout << " " << sorted_dimers.size() << "(" << nbespoke << ")";
                                            std::cout << " population " << stell.popcount((1 << 30) + 3) << " --> ";
                                            std::cout << altstell.popcount((1 << 30) + 3) << " + " << depthExtras[depth].popcount((1 << 30) + 3) << " " << lexrepr << std::endl;

                                            return newpat;
                                        }
                                     }
                                  }
                                  trycount += 1;
                              }
                          }
                      }
                   }
                }
            }
            // std::cout << "--------------------------------" << std::endl;
            depthSDimers[depth]=sorted_dimers;
            depthavoid[depth]=avoid;
            depthnbespoke[depth]=nbespoke;
            depthnblocks[depth]=nblocks;
            depthEarlyPriorityLimit[depth]=earlyPriorityLimit;
            if ((state == 0) && (shallowFlags==0)) {
                depthStLen[depth] = stlength(stell - avoid, depthECCBoundary[depth], cfier);
                std::cout << "stelllength " << depthStLen[depth] << std::endl;
            }

            if ((initbail>5) && (depthBestDonkeyEval[depth]>0)) {//testing
                std::cout << "\033[37;1mno improvement found, early using best donkey move\033[0m" << std::endl;
                return depthBestDonkey[depth];
            }
            if ((maxbail != 0) && (initbail < maxbail)) {
                if (promissingBlockExists) {
                    std::cout << "Increasing bailout to " << (initbail * 3) << std::endl;
                    pattern ret=precurse(orig, cfier, state, initbail * 3, maxbail, initbail, ideal, depth);
                    std::cout << "back to precurse B" << std::endl;
                    return ret;
                } else {
                    std::cout << "no promissing block ... ";
                }
            }
            if (depthBestDonkeyEval[depth]>0) {
                std::cout << "\033[37;1mno improvement found, using best donkey move\033[0m" << std::endl;
                return depthBestDonkey[depth];
            }
            if (shallowFlags) {
                std::cout << "no shallow improvement found" << std::endl;
                return orig;
            }
            std::cout << "no improvement found" << std::endl;
            return orig;
        }

        pattern preiterate(pattern initial, classifier &cfier, int64_t *ideal, uint64_t depth, pattern extra) {
            std::cout << "preiterate " << ((ideal==0)?"0":"i") << " " << depth << " " << extra.popcount((1 << 30) + 3) << std::endl;
            pattern pcend = initial;
            pattern pcstart(lab, "", rule);
            pattern gliders(lab, "", rule);
            preparedepth(depth,extra,initial,cfier);

            //uint64_t chunk1minpop = 8, chunk2minpop = 8;
            while (pcstart != pcend) {

                pcstart = pcend & pcend[8];
                pattern salvo = pcend - pcstart;
                if (salvo.nonempty() && gliders.nonempty()) {

                    int64_t diag = 0;
                    int64_t gliders_bbox[4] = {0};
                    int64_t pcend_bbox[4] = {0};

                    gliders.getrect(gliders_bbox);
                    pcend.getrect(pcend_bbox);

                    int64_t horizontal = (pcend_bbox[0] + pcend_bbox[2] - gliders_bbox[0]);
                    int64_t vertical   = (pcend_bbox[1] + pcend_bbox[3] - gliders_bbox[1]);

                    if (diag < horizontal) { diag = horizontal; }
                    if (diag < vertical) { diag = vertical; }

                    diag += 512;
                    diag -= (diag & 255);

                    gliders = gliders(diag, diag);
                }

                gliders += salvo;
                if (clock()>=depthNextSaveTime[depth]) {
                    depthNextSaveTime[depth] = clock()+30*CLOCKS_PER_SEC;
                    // save progress
                    std::ostringstream ss;
                    ss << "current" << depth << ".mc";
                    std::ofstream out(ss.str());
                    (pcstart + gliders).write_macrocell(out);
                }

                if (depth>0) {
                    if (depthStLen[depth]<0.5) {
                        std::cout << "\033[36;1m Reached optimal ST -> break.\033[0m" << std::endl;
                        break;
                    }
                }

                pattern inpat2 = pcstart;

                if (has_isolated_block(pcstart)) {
                    pattern chunk = get_lowerright(pcstart, inpat2, 8, 32, cfier, extra); //
                    if (chunk != pcstart) {
                        pattern remainder = pcstart - chunk;
                        pcend = remainder + preiterate(chunk, cfier, 0, depth+1, extra + remainder);
                        std::cout << "back to preiterate A " << depth << " " << depthStLen[depth+1];
                        if (pcend == pcstart) {
                            std::cout << " with no progress" << std::endl;
                        } else {
                            std::cout << " with a progress" << std::endl;
                            continue;
                        }
                    }
                    inpat2 -= get_isolated_block(inpat2);
                }

                pcend = precurse(pcstart, cfier, 0, 1, 1, 0, ideal, depth);
                std::cout << "back to preiterate B " << depth << " " << depthStLen[depth];
                if (pcend != pcstart) {
                    std::cout << " with a progress" << std::endl;
                    continue;
                }

                if (depth>0) {
                    if (depthStLen[depth]<0.5) {
                        std::cout << "\033[36;1m with optimal ST -> break.\033[0m" << std::endl;
                        break;
                    }
                }

                std::cout << " with no progress" << std::endl;

                if (inpat2.nonempty()) {
                    pattern chunk = get_lowerright(pcstart, inpat2, 8, 25, cfier, extra);
                    if (chunk != pcstart) {
                        pattern remainder = pcstart - chunk;
                        pcend = remainder + preiterate(chunk, cfier, 0, depth+1, extra + remainder);
                        std::cout << "back to preiterate C " << depth << " " << depthStLen[depth+1];
                        if (pcend == pcstart) {
                            std::cout << " with no progress" << std::endl;
                        } else {
                            std::cout << " with a progress" << std::endl;
                            continue;
                        }
                    }
                }

                // attempt deeper constructions:
                pcend = precurse(pcstart, cfier, 1, 1, 4096, 0, ideal, depth);
                std::cout << "back to preiterate D " << depth << " " << depthStLen[depth];

                if (pcend != pcstart) {
                    std::cout << " with a progress" << std::endl;
                    continue;
                }

                if (depth>0) {
                    if (depthStLen[depth]<0.5) {
                        std::cout << "\033[36;1m with optimal ST -> break.\033[0m" << std::endl;
                        break;
                    }
                }

                std::cout << " ending with no progress" << std::endl;
            }

            return (pcstart + gliders);
        }

        pattern preiterate(pattern initial, classifier &cfier, int64_t *ideal, uint64_t depth = 0) {
            std::cout << "preiterate0 " << ((ideal==0)?"0":"i") << " " << depth  << std::endl;
            lab = initial.getlab();
            rule = initial.getrule();

            pattern extra(lab, "", rule);
            return preiterate(initial, cfier, ideal, depth, extra);
        }

        pattern preiterate(pattern initial, classifier &cfier) {
            pattern findme(initial.getlab(), "3b2o$2bo2bo$bob2obo$obo2bobo$obo2bobo$bob2obo$2bo2bo$3b2o!", initial.getrule());
            pattern m = initial.match(findme);
            pattern im = initial - m.convolve(findme);

            if (m.empty()) {
                return preiterate(im, cfier, 0, 0);
            } else {
                int64_t ideal[4] = {0};
                m(3, 3).getrect(ideal);
                return preiterate(im, cfier, ideal, 0);
            }
        }

    };

}
defragmented_donkey_vx_7864.mc
(258.26 KiB) Downloaded 17 times
Watch the 7864 salvo generations 592000-620000. I would be much more happy if we use split move rather to move (destroying one of the blocks) and later creating block in nearby location due to another move ... . This is what "more source blocks" would be able to do if we would find a way to work with it. Hmm, I wander why it happened ... has isolated block chunk strategy caused the problem separating SW block, to solve middle one?
Whould the preiterate stop working without this chunking? ... No with isolated block chunking commented out, the recipe (finished in 16 minutes) consists of 7917 gliders.

If the "anti donkey" strategy is so efficient (and usually it resullts in easy followup), it could be helpfull to have donkeylong moves ordered by maximizing ratio of x+y/salvo size. Using this order for deep moves (may be alternating with the original order for safety) could improve both time and salvo size.
Last edited by Hippo.69 on June 16th, 2023, 10:45 am, edited 1 time in total.

User avatar
Hippo.69
Posts: 271
Joined: July 14th, 2020, 7:35 pm

Re: slmake

Post by Hippo.69 » May 17th, 2023, 5:21 pm

I have to look to the code again ... for an unknown reason the slmake is called (rather consistently) with the pattern rotated 180 degrees ... even when there is a mango with block defining the orientation. Oh seems I have the block on the wrong side ... .

Hmm and now when working from the correct side I got incorrect salvos. Chunk with isolated block? Deep move problem? ... I should definitely check what is going on.

I got regularly this ... here is the problem shown in one file ... we should add extra to avoid!!!
red NW portion was cut as a chunk on previous level. This is so frustratig the LifeHistory has only one dead marked color ... oh state 2? Bad, but better than nothing ... (I use in my version several pairs with 4-5 states behaviour, what is much better for such documentation.)

Code: Select all

x = 13478, y = 13507, rule = LifeHistory
44.2D$24.D19.2D$24.3D$27.D$26.D.D$27.D28$2.2B$3.B$3.B.B10.2A$4.2B10.
2A3$8.2B$7.B2.B$8.B.B$9.B3$6.2B$6.3B$2B2.B2.B.B$2B2.2B2.2B$4.2B109$
144.2A$143.2A$145.A136$272.2A$271.2A$273.A121$400.2A$399.2A$401.A121$
528.2A$527.2A$529.A143$656.2A$655.2A$657.A119$784.2A$783.2A$785.A128$
911.3A$911.A$912.A504$1417.3A$1417.A$1418.A126$1556.3A$1556.A$1557.A
126$1674.3A$1674.A$1675.A485$2199.2A$2199.A.A$2199.A117$2327.2A$2327.
A.A$2327.A131$2455.2A$2455.A.A$2455.A125$2583.2A$2583.A.A$2583.A124$
2711.2A$2711.A.A$2711.A134$2839.2A$2839.A.A$2839.A389$3203.3A$3203.A$
3204.A126$3331.3A$3331.A$3332.A125$3466.A$3465.2A$3465.A.A126$3600.A$
3599.2A$3599.A.A127$3717.3A$3717.A$3718.A125$3853.A$3852.2A$3852.A.A
126$3973.A$3972.2A$3972.A.A127$4099.3A$4099.A$4100.A405$4482.2A$4482.
A.A$4482.A123$4610.2A$4610.A.A$4610.A125$4738.2A$4738.A.A$4738.A136$
4866.2A$4866.A.A$4866.A132$4994.2A$4994.A.A$4994.A505$5514.A$5513.2A$
5513.A.A126$5642.A$5641.2A$5641.A.A126$5777.2A$5777.A.A$5777.A126$
5888.2A$5888.A.A$5888.A126$6026.A$6025.2A$6025.A.A126$6158.2A$6158.A.
A$6158.A126$6268.2A$6268.A.A$6268.A258$6535.2A$6534.2A$6536.A117$
6663.2A$6662.2A$6664.A133$6791.2A$6790.2A$6792.A126$6918.3A$6918.A$
6919.A122$7047.2A$7046.2A$7048.A133$7174.3A$7174.A$7175.A380$7562.A$
7561.2A$7561.A.A126$7694.A$7693.2A$7693.A.A126$7805.2A$7805.A.A$7805.
A126$7957.2A$7957.A.A$7957.A126$8083.2A$8083.A.A$8083.A511$8591.2A$
8590.2A$8592.A129$8719.2A$8718.2A$8720.A126$8847.2A$8846.2A$8848.A
112$8974.3A$8974.A$8975.A123$9102.3A$9102.A$9103.A263$9364.2A$9363.2A
$9365.A127$9492.2A$9491.2A$9493.A111$9620.2A$9619.2A$9621.A127$9747.
3A$9747.A$9748.A140$9876.2A$9875.2A$9877.A132$10003.3A$10003.A$10004.
A128$10131.3A$10131.A$10132.A108$10259.3A$10259.A$10260.A387$10636.A$
10635.2A$10635.A.A482$11165.3A$11165.A$11166.A126$11290.3A$11290.A$
11291.A125$11412.A$11411.2A$11411.A.A127$11538.3A$11538.A$11539.A126$
11669.3A$11669.A$11670.A125$11764.A$11763.2A$11763.A.A126$11916.A$
11915.2A$11915.A.A126$12040.A$12039.2A$12039.A.A126$12151.A$12150.2A$
12150.A.A292$12449.3A$12449.A$12450.A126$12581.3A$12581.A$12582.A125$
12707.A$12706.2A$12706.A.A523$13218.3A$13218.A$13219.A126$13350.3A$
13350.A$13351.A125$13476.A$13475.2A$13475.A.A!
I will be fixing it at night ... I have increased the required chunk distance raher to adding extra to avoid.

AlbertArmStain
Posts: 1233
Joined: January 28th, 2022, 7:18 pm
Location: Planet Z

Re: slmake

Post by AlbertArmStain » May 30th, 2023, 10:13 am

Does slmake have traveling salesman problem like optimization? Slmake is just a traveling salesman problem in principle. The slmake optimization algorithm will probably be something like ant colony optimization or a greedy algorithm.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: slmake

Post by calcyman » May 30th, 2023, 10:50 am

AlbertArmStain wrote:
May 30th, 2023, 10:13 am
Does slmake have traveling salesman problem like optimization? Slmake is just a traveling salesman problem in principle. The slmake optimization algorithm will probably be something like ant colony optimization or a greedy algorithm.
The old slmake is a greedy algorithm, and the new version that I'm currently writing (pslmake) is a parallelised beam search with heuristic similar to that in the A* search algorithm, where the cost estimate is based on a linear model fitted on diagnostics produced by the old slmake.

I don't see how it's related to travelling salesman? The problem is to find a short path from a single block to the desired pattern in the (infinite) directed graph where each node is a constellation and each directed edge corresponds to a glider collision; this is a pathfinding problem. On the other hand, travelling salesman is about finding a minimum-length path/cycle that visits all nodes of a finite graph with weighted edges, which is a very different sort of problem (NP-complete as a function of the size of the graph, whereas pathfinding is polynomial-time in the size of the graph).
What do you do with ill crystallographers? Take them to the mono-clinic!

Post Reply