void GoSafetySolver::GenBlocksRegions()
{
    if (UpToDate())
        /* */ return; /* */
        
    GoStaticSafetySolver::GenBlocksRegions();
    
    Regions()->GenChains();
    
    // merge blocks adjacent to 1-vital with 2 conn. points
    for (SgBWIterator it; it; ++it)
    {
        SgBlackWhite color(*it);
        for (SgVectorIteratorOf<GoRegion> 
             it(Regions()->AllRegions(color)); it; ++it)
        {
            GoRegion* r = *it;
            r->ComputeFlag(GO_REGION_STATIC_1VITAL);
        }

        bool changed = true;
        while (changed)
        {   changed = false;
            for (SgVectorIteratorOf<GoRegion> 
                 it(Regions()->AllRegions(color)); it; ++it)
            {   GoRegion* r = *it;
                if (  r->GetFlag(GO_REGION_STATIC_1VC)
                   && r->Chains().IsLength(2)
                   && r->Has2Conn() 
                        //  || r->Safe2Cuts(Board()) changed from && to ||
                        // @todo does not work if blocks are already chains???
                        // must explicitly keep chain libs info.
                   )
                // easy case of only 2 chains
                {
                    GoChain* c1 = r->Chains().Front();
                    GoChain* c2 = r->Chains().Back();
                    Merge(c1, c2, r, false); // false = not by search
                    changed = true;
                    break; // to leave iteration
                }
                else if (   r->GetFlag(GO_REGION_STATIC_1VITAL)
                         && r->GetFlag(GO_REGION_CORRIDOR)
                         && ! r->GetFlag(GO_REGION_USED_FOR_MERGE)
                        ) 
                {
                    GoChain* c1 = 0;
                    GoChain* c2 = 0;
                    if (r->Find2Mergable(&c1, &c2))
                    {
                        Merge(c1, c2, r, false);
                        changed = true;
                        break; // to leave iteration
    }   }   }   }   }
    
    m_code = Board().GetHashCode();
}
void GoSafetySolver::Merge(GoChain* c1, GoChain* c2,
                           GoRegion* r, bool bySearch)
{
    SG_ASSERT(! r->GetFlag(GO_REGION_USED_FOR_MERGE));
    r->SetFlag(GO_REGION_USED_FOR_MERGE, true);
    
    GoChainCondition* c = 0;
    if (bySearch)
        c = new GoChainCondition(GO_CHAIN_BY_SEARCH);
    else
    {
        SgPoint lib1, lib2;
        r->Find2FreeLibs(c1, c2, &lib1, &lib2);
        c = new GoChainCondition(GO_CHAIN_TWO_LIBERTIES_IN_REGION,
                                 lib1, lib2);
    }
    
    GoChain* m = new GoChain(c1, c2, c);

    SgBlackWhite color = c1->Color();
    bool found = Regions()->AllChains(color).Exclude(c1);
    SG_DEBUG_ONLY(found);
    SG_ASSERT(found);
    found = Regions()->AllChains(color).Exclude(c2);
    SG_ASSERT(found);
    Regions()->AllChains(color).Include(m);
    SG_ASSERT(Regions()->AllChains(color).UniqueElements());

    for (SgVectorIteratorOf<GoRegion>
         it(Regions()->AllRegions(color)); it; ++it)
    {
        GoRegion* r = *it;
        bool replace1 = r->ReplaceChain(c1, m);
        bool replace2 = r->ReplaceChain(c2, m);
        if (replace1 || replace2)
        {
            r->ReInitialize();
            r->ComputeFlag(GO_REGION_STATIC_1VITAL);
        }
    }

    if (DEBUG_MERGE_CHAINS)
    {
        SgDebug() << "\nmerge:";
        c1->WriteID(SgDebug());
        SgDebug() << " + ";
        c2->WriteID(SgDebug());
        SgDebug() << " = ";
        m->WriteID(SgDebug());
        SgDebug() << '\n';
    }

    delete c1;
    delete c2;
}