
        // sort bounds
        
        final int [] varindex = new int[variables.length*2-2];
        final boolean [] upperb = new boolean[variables.length*2-2]; // true if it's the upperbound.
        
        for(int i=0; i<variables.length; i++)
        {
            if(i!=xi)
            {
                varindex[2*i]=i;
                varindex[2*i+1]=i;
                upperb[2*i]=true;
                upperb[2*i+1]=false;
            }
        }
        
        
        { // now sort
        Swapper swapper = new Swapper()
        {
            public void swap(int a, int b) 
            {
                int t1; boolean t2;
                t1=varindex[a];
                varindex[a]=varindex[b];
                varindex[b]=t1;
                
                t2=upperb[a];
                upperb[a]=upperb[b];
                upperb[b]=t2;
            }
        };
        
        IntComparator comp = new IntComparator()
        {
            public int compare(int a, int b)
            {
                IntNum abs1=(IntNum) (upperb[a]?variables[varindex[a]].upperbound_big().abs():variables[varindex[a]].lowerbound_big().abs());
                IntNum abs2=(IntNum) (upperb[b]?variables[varindex[b]].upperbound_big().abs():variables[varindex[b]].lowerbound_big().abs());
                return abs1.compare(abs2);
            }
        };
        
        GenericSorting.mergeSort(0, varindex.length, comp, swapper);
        }
        
        // now check there is at least one x_j != i that spans 0.
        int numspanzero=0;
        for(int i=0; i<variables.length; i++)
        {
            if(i!=xi)
            {
                if(spanzero[i]) numspanzero++;
            }
        }
        
        if(numspanzero>0)
        {
            // in this case, we have to use the last freely settable spanzero variable to 
            // set the appropriate sign.
            
            // don't forget we do not have free access to inner universals!!
        }*/
        
        /*boolean anyspanzero=false;
        
        for(int i=0; i<xi; i++)
        {   // for variables x_1 to x_i-1
            if(spanzero[i])
            {
                anyspanzero=true;
                break;
            }
        }




Old resolution stuff.


/*
    final void combine_ee_max(IntNum lb2, IntNum ub2, boolean spanszero2)
    {
        // Use the EE combine rule to replace the two variables
        // represented by: newupperbound, newlowerbound, spanzero
        // and: ub2, lb2, spanzero2
        // with one existential variable, whose parameters are
        // stored in newupperbound, newlowerbound and spanszero.
        // Whatever the codepath, these three should be set.
        
        if(emptydomain)
        {
            // If the domain of the inner variable (new...) is empty, 
            // then whether the replacement domain is empty or not 
            // depends on whether the outer variable spans 0.
            if(spanszero2)
            {
                // The outer variable does span 0, and it's existential, so
                // there is the possibility of the outer variable being set to 0
                // so the replacement variable is a 0.
                newupperbound=zero;
                newlowerbound=zero;
                spanszero=true;
                emptydomain=false;
                // at this point we could stop the sweep and set x_i to 0.
            }
            else
            {   // still empty
                emptydomain=true;
            }
            return;
        }
        else if(spanszero)
        {
            if(spanszero2)
            {
                // if they both span 0
                // using the EE rewrite rule.
                IntNum lb1ub2=IntNum.times(newlowerbound, ub2);
                IntNum ub1lb2=IntNum.times(newupperbound, lb2);
                IntNum lb1lb2=IntNum.times(newlowerbound, lb2);
                IntNum ub1ub2=IntNum.times(newupperbound, ub2);
                newlowerbound=min(lb1ub2, ub1lb2);
                newupperbound=max(lb1lb2, ub1ub2);
                spanszero=true;  //just for clarity
            }
            else
            {
                // second variable does not span 0 so just use its greater (absolute) bound
                if(ub2.compare(zero)<0)
                {   // both negative so use the lowerbound, lb2
                    // and swap over the new bounds.
                    IntNum temp=newupperbound;
                    newupperbound=IntNum.times(newlowerbound, lb2);
                    newlowerbound=IntNum.times(temp, lb2);
                }
                else
                {   // both positive so use the upperbound
                    newupperbound=IntNum.times(newupperbound, ub2);
                    newlowerbound=IntNum.times(newlowerbound, ub2);
                }
                spanszero=true;
            }
        }
        else
        {
            if(spanszero2)
            {
                // Take the greater (absolute) bound from new..bound
                if(newupperbound.compare(zero)<0)
                {   // negative, so use newlowerbound and swap over the bounds
                    newupperbound=IntNum.times(lb2, newlowerbound);
                    newlowerbound=IntNum.times(ub2, newlowerbound);
                }
                else
                {   //positive so use newupperbound
                    newlowerbound=IntNum.times(lb2, newupperbound);
                    newupperbound=IntNum.times(ub2, newupperbound);
                }
                spanszero=true;
            }
            else
            {   // both non-span-zero so take the absolute biggest together, and abs smallest x'ed together.
                boolean onegtzero=newupperbound.compare(zero)>0;
                boolean twogtzero=ub2.compare(zero)>0;
                if((onegtzero && twogtzero) || (!onegtzero && !twogtzero))
                {   // both either positive or negative.
                    newupperbound=IntNum.times(newupperbound, ub2);
                    newlowerbound=IntNum.times(newlowerbound, lb2);
                    spanszero=false;
                }
                else if(onegtzero && !twogtzero)
                {   // one is positive, two is negative
                    IntNum temp=newlowerbound;
                    newlowerbound=IntNum.times(newupperbound, lb2); // absolute largest ones
                    newupperbound=IntNum.times(temp, ub2);          // absolute smallest ones.
                    spanszero=false;
                }
                else
                {
                    assert !onegtzero && twogtzero;
                    // one is negative, two is positive
                    newlowerbound=IntNum.times(newlowerbound, ub2);  // absolute largest ones
                    newupperbound=IntNum.times(newupperbound, lb2);  // absolute smallest ones.
                    spanszero=false;
                }
            }
        }
        
    }*/
    
    
    /*
    final void combine_ae_max(IntNum lb2, IntNum ub2, boolean spanszero2)
    {
        // combine an outermost AE combination into one E variable.
        // _max because it maximizes the (absolute) bounds. i.e. universal
        // player minimizes the bounds and existential maximizes.
        
        // The universal is the parameters passed in, existential is the
        // ones stored in newlowerbound, newupperbound, spanszero, emptydomain
        
        if(emptydomain)
        {
            // If the domain of the inner variable (new...) is empty, 
            // then the replacement variable will be 0 if the outer universal is set
            // to 0, otherwise the replacement variable will be empty.
            if(lb2.compare(zero)==0 && ub2.compare(zero)==0)
            {
                // The outer universal is 0.
                newupperbound=zero;
                newlowerbound=zero;
                spanszero=true;
                emptydomain=false;
                // at this point we could stop the sweep and set x_i to 0.
            }
            else
            {
                // else still empty
                emptydomain=true;  // not really required.
            }
            return;
        }
        else if(spanszero)
        {
            if(spanszero2)
            {
                // They both span 0 so use the appropriate rule for universal forcing existential's hand
                // The order actually doesn't matter.
                IntNum lb1ub2=IntNum.times(newlowerbound, ub2);
                IntNum ub1lb2=IntNum.times(newupperbound, lb2);
                IntNum lb1lb2=IntNum.times(newlowerbound, lb2);
                IntNum ub1ub2=IntNum.times(newupperbound, ub2);
                newlowerbound=max(ub1lb2, lb1ub2);   // max (absolute minimum) of both possible negative products
                newupperbound=min(lb1lb2, ub1ub2);   // min (absolute minimum) of both possible positive products.
                spanszero=true;  //just for clarity
                emptydomain=false;
            }
            else
            {
                // The universal does not span zero, but the inner existential does.
                // Just use the universal's smaller (abs) bound.
                if(ub2.compare(zero)<0)
                {   // negative, so use lb2 and swap over the bounds
                    newupperbound=IntNum.times(lb2, newlowerbound);
                    newlowerbound=IntNum.times(lb2, newlowerbound);
                }
                else
                {   //positive so use ub2
                    newupperbound=IntNum.times(ub2, newupperbound);
                    newlowerbound=IntNum.times(ub2, newlowerbound);
                }
                spanszero=true;
                emptydomain=false;
            }
        }
        else
        {
            if(spanszero2)
            {
                // The existential does not span 0 but the universal does.
                // Should this return an empty interval? Yes I think so.
                emptydomain=true;
            }
            else
            {
                // Neither variable spans 0. 
                // Therefore just use the worst case for the universal (abs smaller bound)
                boolean onegtzero=newupperbound.compare(zero)>0;
                boolean twogtzero=ub2.compare(zero)>0;
                if(onegtzero && twogtzero)
                {   // both positive
                    newupperbound=IntNum.times(newupperbound, lb2);
                    newlowerbound=IntNum.times(newlowerbound, lb2);
                    spanszero=false;
                    emptydomain=false;
                }
                else if(!twogtzero)
                {   // universal is negative
                    // therefore use the upperbound of the universal
                    // and swap the bounds over.
                    IntNum temp=newlowerbound;
                    newlowerbound=IntNum.times(newupperbound, ub2); // absolute largest ones
                    newupperbound=IntNum.times(temp, ub2);          // absolute smallest ones.
                    spanszero=false;
                    emptydomain=false;
                }
                else if(!onegtzero && twogtzero)
                {
                    // one is negative, two is positive
                    newlowerbound=IntNum.times(newlowerbound, lb2);  // absolute largest ones
                    newupperbound=IntNum.times(newupperbound, lb2);  // absolute smallest ones.
                    spanszero=false;
                    emptydomain=false;
                }
            }
        }
    }*/


/*void combine_ae(IntNum lb1, IntNum ub1, IntNum denom1, boolean emptydomain1, 
                        IntNum lb2, IntNum ub2, IntNum denom2, boolean emptydomain2)
    {
        // ub1 lb1 etc represent the outer universal.
        if(emptydomain || emptydomain2)
        {
            if(emptydomain && lb1.isZero() && ub1.isZero())
            {   // the universal is set to zero.
                newupperbound=zero;
                newlowerbound=zero;
                denominator=one;
                emptydomain=false;
                return;
            }
            else
            {   
                denominator=one;
                newupperbound=one;
                newlowerbound=one;
                emptydomain=true;
                return;  // still empty
            }
        }
        IntNum lb1ub2=IntNum.times(lb1, ub2);
        IntNum ub1lb2=IntNum.times(ub1, lb2);
        IntNum lb1lb2=IntNum.times(lb1, lb2);
        IntNum ub1ub2=IntNum.times(ub1, ub2);
        // remember l/ub2 represents the outer one, and should really be numbered '1'
        newlowerbound=max(min(lb1lb2, lb1ub2), min(ub1lb2, ub1ub2));
        newupperbound=min(max(lb1lb2, lb1ub2), max(ub1lb2, ub1ub2));
        denominator=IntNum.times(denom1, denom2);
        emptydomain=newlowerbound.compare(newupperbound)>0;
    }*/
    
/*void combine_ee(IntNum lb1, IntNum ub1, IntNum denom1, boolean emptydomain1, 
                        IntNum lb2, IntNum ub2, IntNum denom2, boolean emptydomain2)
    {
        // what if one or other has a zero denominator??
        denominator=IntNum.times(denom1, denom2);
        
        if(denominator.isZero())
        {
            // This indicates that xi is zero. Therefore be propagate through the
            // zero denominator, and the bounds may as well be 1.
            newupperbound=one;
            newlowerbound=one;
            emptydomain=false;
            return;
        }
        
        if(emptydomain1 || emptydomain2)
        {
            if((emptydomain1 && !(lb2.compare(zero)<=0 && ub2.compare(zero)>=0))
                || (emptydomain2 && !(lb1.compare(zero)<=0 && ub1.compare(zero)>=0)))
            {
                denominator=one;
                newupperbound=one;
                newlowerbound=one;
                emptydomain=true;
                return;  // still empty
            }
            else
            {   // otherwise zero.
                newupperbound=zero;
                newlowerbound=zero;
                denominator=one;
                emptydomain=false;
                return;
            }
        }
        
        IntNum lb1ub2=IntNum.times(lb1, ub2);
        IntNum ub1lb2=IntNum.times(ub1, lb2);
        IntNum lb1lb2=IntNum.times(lb1, lb2);
        IntNum ub1ub2=IntNum.times(ub1, ub2);
        newlowerbound=min(lb1lb2, min(lb1ub2, min(ub1lb2, ub1ub2)));  // min of all bound products
        newupperbound=max(lb1lb2, max(lb1ub2, max(ub1lb2, ub1ub2)));  // max of all bound products
        denominator=IntNum.times(denom1, denom2);
        emptydomain=false;  // no way of it becoming empty.
    }*/
    
    
     
        
        // old res_leftward stuff
        /*
                if(haszero1)
                {
                    // if the universal contains zero,
                    // split into two groups:
                    // 1. {0}, which is supported if the inner resolvent spans 0
                    // 2. all other values, which can be supported by the outer resolvent as well
                    //    if it contains the appropriate non-zero value or values
                    if(!lb1.isZero() || !ub1.isZero())
                    {
                        // contains zero and other values.
                        if(lb1.isNegative() && !ub1.isZero() && !ub1.isNegative())
                        {
                            // contains and spans zero
                            // The inner resolvent must also contain and span zero.
                            if(!emptydomain2 && lb2.isNegative() && !ub2.isZero() && !ub2.isNegative())
                            {
                                // resolve as normal.
                                
                                // use the inner bounds to minimize the quotient.
                                lb1=variables[i].negative_smallest_big();
                                ub1=variables[i].positive_smallest_big();
                                
                                combine_axi(lb1, ub1, one, false,       // assumes it's not empty
                                            lb2, ub2, d2, emptydomain2);
                            }
                            else
                            {
                                // the universal is able to cause failure.
                                newlowerbound=one;
                                newupperbound=one;
                                denominator=one;
                                emptydomain=true;
                            }
                        }
                        else
                        {
                            // xi contains zero and other values but does not span zero.
                            // Check that the inner resolvent also contains zero and other
                            // values
                            if(!emptydomain2 && (!lb2.isZero() || !ub2.isZero()))
                            {
                                // resolve as normal not using the zero.
                                if(lb1.isZero())
                                {
                                    lb1=variables[i].positive_smallest_big();
                                }
                                
                                if(ub1.isZero())
                                {
                                    ub1=variables[i].negative_smallest_big();
                                }
                                
                                combine_axi(lb1, ub1, one, false,       // assumes it's not empty
                                            lb2, ub2, d2, emptydomain2);
                            }
                            else
                            {
                                // the universal is able to cause failure.
                                newlowerbound=one;
                                newupperbound=one;
                                denominator=one;
                                emptydomain=true;
                            }
                        }
                    }
                    else
                    {
                        // xi contains only zero.
                        // if we set the denominator to 0 then that may cause unsound pruning
                        // We should set the denominator to 0 and trust that a variable
                        // will be listed to be pruned only if necessary.
                        if(!emptydomain2)
                        {
                            newlowerbound=one;
                            newupperbound=one;
                            denominator=zero;
                            emptydomain=false;
                        }
                        else
                        {
                            newlowerbound=one;
                            newupperbound=one;
                            denominator=one;
                            emptydomain=true;
                        }
                    }
                }
                else
                {
                    // xi does not contain zero.
                    // may still span zero
                    
                    if(lb1.isNegative())
                    {
                        lb1=variables[xi].negative_smallest_big();
                    }

                    if(!ub1.isZero() && !ub1.isNegative())
                    {
                        ub1=variables[xi].positive_smallest_big();
                    }
                    combine_axi(lb1, ub1, one, false,       // assumes it's not empty
                                lb2, ub2, d2, emptydomain2);
                }
                
                // strip down combine_axi to the minimum now.
                
                // waht about doing the resolution with the smallest bounds, then
                // seeing about containsZero?
                
                // Since we are not pruning xi, 0 is good unless we can get an empty resolvent.
                / *
                if(lb1.isNegative() && !ub1.isNegative() && !ub1.isZero())
                {
                    lb1=variables[i].negative_smallest_big();
                    ub1=variables[i].positive_smallest_big();
                }
                
                combine_axi(lb1, ub1, one, false,       // assumes it's not empty
                    lb2, ub2, d2, emptydomain2);
                
                // if the resolvent spans zero and v1 contains zero, we can use that.
                if(variables[i].containsZero() && !newupperbound.isNegative() && !newupperbound.isZero() && newlowerbound.isNegative() && !emptydomain)  
                {
                    // should we not be zeroing the denominator??
                    // problem is, this means zeroing *all* the outer variables! doh.
                    // Trust that a variable will only be listed to be pruned if
                    // it should be zeroed.
                    denominator=zero;
                    //newupperbound=zero;
                    //newlowerbound=zero;
                }* /
            }
            else
            {
                IntNum lb1=variables[i].lowerbound_big();
                IntNum ub1=variables[i].upperbound_big();
                combine_exi(lb1, ub1, one, false,       // assumes it's not empty
                            lb2, ub2, d2, emptydomain2);
            }
        }
        else// Not the xi variable.
        {
            // Treat the variable data stored in lb2, ub2 as an
            // outer existential variable. Then use the combine_ee and combine_ae rules to resolve leftward
            if(variables[i].quant())
            {
                IntNum lb1=variables[i].lowerbound_big();
                IntNum ub1=variables[i].upperbound_big();  // assuming not empty.
                
                // waht about doing the resolution with the smallest bounds, then
                // seeing about containsZero?
                
                // Since we are pruning xi, 0 is good unless we can get an empty resolvent.
                // If we are not pruning xi, 0 is as good as a spanzero resolvent, because both indicate no pruning.
                
                if(lb1.isNegative() && !ub1.isNegative() && !ub1.isZero())
                {
                    lb1=variables[i].negative_smallest_big();
                    ub1=variables[i].positive_smallest_big();
                }
                
                combine_ae(lb1, ub1, one, false,       // assumes it's not empty
                    lb2, ub2, d2, emptydomain2);
                
                // if the resolvent spans zero and v1 contains zero, we can use that.
                if(variables[i].containsZero() && !newupperbound.isNegative() && !newupperbound.isZero() && newlowerbound.isNegative() && !emptydomain)
                {
                    newupperbound=zero;
                    newlowerbound=zero;
                }
            }
            else  // existential.
            {
                IntNum lb1=variables[i].lowerbound_big();
                IntNum ub1=variables[i].upperbound_big();
                combine_ee(lb1, ub1, one, false,       // assumes it's not empty
                            lb2, ub2, d2, emptydomain2);
            }
        }
        // fill in cache entry.
        lxupperbound[i]=newupperbound;
        lxlowerbound[i]=newlowerbound;
        lxdenominator[i]=denominator;
        lxemptydomain[i]=emptydomain;
        
        assert lxdoneto==i+1;
        lxdoneto=i;
        System.out.println("Resolving leftward for variable "+variables[i]+": ["+(emptydomain?"":newlowerbound+", "+newupperbound)+"], den "+denominator);
    }
    */
    
    old combine_exi
    {
        // The first set of arguments is the xi variable's bounds etc.
        // multiply by c2 (which is positive) then combine with 
        // multiply the bounds by c2
        
        assert denom1.isOne() && denom2.isOne();
        
        IntNum lb1ub2=IntNum.times(lb1, ub2);
        IntNum ub1lb2=IntNum.times(ub1, lb2);
        IntNum lb1lb2=IntNum.times(lb1, lb2);
        IntNum ub1ub2=IntNum.times(ub1, ub2);
        
        denominator=IntNum.times(c2, IntNum.times(lb1, ub1));
        if(denominator.isZero())
        {
            // some or all of the fractions in the min/max are not defined, so
            // we cannot compute the min and max.
            // The bounds may as well be set to one.
            newlowerbound=one;
            newupperbound=one;
            emptydomain=false;
        }
        else if(emptydomain2)
        {
            denominator=one;
            newlowerbound=one;
            newupperbound=one;
            emptydomain=true;
        }
        else
        {
            newlowerbound=min(min(lb1lb2, ub1lb2), min(lb1ub2, ub1ub2));
            newupperbound=max(max(lb1lb2, ub1lb2), max(lb1ub2, ub1ub2));
            emptydomain=newlowerbound.compare(newupperbound)>0;
            if(denominator.isNegative())
            {
                IntNum temp=newlowerbound;
                newlowerbound=IntNum.neg(newupperbound);
                newupperbound=IntNum.neg(temp);
                denominator=IntNum.neg(denominator);
            }
        }
    }



    //////////////////////////////////////////////////////////////////////////////////////////////
    // all methods with xi after the name are used only when pruning xi. No longer used.
    
    boolean prune_variable_xi()
    {
        System.out.println("Pruning variable "+variables[xi]);
        for(int i=variables.length-1; i>xi; i--)
        {   // compute the minimum number of resolvents.
            res_leftward_xi(i);
        }
        IntNum lxlb=(xi==variables.length-1?c1:newlowerbound);
        IntNum lxub=(xi==variables.length-1?c1:newupperbound); 
        boolean lxempty=(xi==variables.length-1?false:emptydomain);
        
        for(int i=0; i<xi; i++)
        {
            res_rightward_xi(i);
        }
        IntNum rxlb=(xi==0?one:newlowerbound);
        IntNum rxub=(xi==0?one:newupperbound);
        boolean rxempty=(xi==0?false:emptydomain);
        
        combine_ee_xi(lxub, lxlb, lxempty, rxlb, rxub, rxempty);
        
        if(emptydomain)
        {   // e1 will be empty, so
            return false;
        }
        
        return revise_bounds_xi(newlowerbound, newupperbound, c2);
    }
    
    void res_leftward_xi(int i)
    {
        // i is the index
        // compute the minimum number of resolvents.
        // remember to treat xi differently.
        
        // treat newupperbound, newlowerbound and emptydomain as the previous
        // resolvent.
        
        IntNum lb2;
        IntNum ub2;
        boolean emptydomain2;
        
        if(i<variables.length-1)
        {
            lb2=newlowerbound;
            ub2=newupperbound;
            emptydomain2=emptydomain;
        }
        else
        {
            assert i==variables.length-1;
            lb2=c1;
            ub2=c1;
            emptydomain2=false;
        }
        
        // Treat the variable data stored in lb2, ub2 as an
        // outer existential variable. Then use the combine_ee and combine_ae rules to resolve leftward
        if(variables[i].quant())
        {
            IntNum lb1=variables[i].lowerbound_big();
            IntNum ub1=variables[i].upperbound_big();  // assuming not empty, as ever.
            
            // waht about doing the resolution with the smallest bounds, then
            // seeing about containsZero?
            
            // Since we are pruning xi, 0 is good unless we can get an empty resolvent.
            // If we are not pruning xi, 0 is as good as a spanzero resolvent, because both indicate no pruning.
            
            if(lb1.isNegative() && !ub1.isNegative() && !ub1.isZero())
            {
                lb1=variables[i].negative_smallest_big();
                ub1=variables[i].positive_smallest_big();
            }
            
            combine_ae_xi(lb1, ub1, false,       // assumes it's not empty
                lb2, ub2, emptydomain2);

            // if the resolvent spans zero and v1 contains zero, we can use that.
            if(variables[i].containsZero() && !newupperbound.isNegative() && !newupperbound.isZero() && newlowerbound.isNegative() && !emptydomain)
            {
                newupperbound=zero;
                newlowerbound=zero;
            }
        }
        else  // existential.
        {
            IntNum lb1=variables[i].lowerbound_big();
            IntNum ub1=variables[i].upperbound_big();
            combine_ee_xi(lb1, ub1, false,       // assumes it's not empty
                        lb2, ub2, emptydomain2);
        }
        System.out.println("Resolving leftward for variable "+variables[i]+": ["+(emptydomain?"":newlowerbound+", "+newupperbound)+"]");
        
        // leaves resolvent in newlowerbound, newupperbound and emptydomain.
    }
    
    void res_rightward_xi(int i)
    {
        IntNum lb2;
        IntNum ub2;
        boolean emptydomain2;
        
        if(i!=0)
        {
            lb2=newlowerbound;
            ub2=newupperbound;
            emptydomain2=emptydomain;
        }
        else
        {
            lb2=one;
            ub2=one;
            emptydomain2=false;
        }
        
        IntNum ub1=variables[i].upperbound_big();
        IntNum lb1=variables[i].lowerbound_big();
        combine_ee_xi(lb1, ub1, false,
                    lb2, ub2, emptydomain2);   // assumes it's not empty
        
        System.out.println("Resolving rightward for variable "+variables[i]+": ["+(emptydomain?"":newlowerbound+", "+newupperbound)+"]");
        // leaves the result in newlowerbound, newupperbound and emptydomain.
    }
    
    void combine_ee_xi(IntNum lb1, IntNum ub1, boolean emptydomain1, 
                        IntNum lb2, IntNum ub2, boolean emptydomain2)
    {
        
        if(emptydomain1 || emptydomain2)
        {
            if((emptydomain1 && !(lb2.compare(zero)<=0 && ub2.compare(zero)>=0))
                || (emptydomain2 && !(lb1.compare(zero)<=0 && ub1.compare(zero)>=0)))
            {
                newupperbound=one;
                newlowerbound=one;
                emptydomain=true;
                return;  // still empty
            }
            else
            {   // otherwise zero.
                newupperbound=zero;
                newlowerbound=zero;
                emptydomain=false;
                return;
            }
        }
        
        IntNum lb1ub2=IntNum.times(lb1, ub2);
        IntNum ub1lb2=IntNum.times(ub1, lb2);
        IntNum lb1lb2=IntNum.times(lb1, lb2);
        IntNum ub1ub2=IntNum.times(ub1, ub2);
        
        newlowerbound=min(lb1lb2, min(lb1ub2, min(ub1lb2, ub1ub2)));  // min of all bound products
        newupperbound=max(lb1lb2, max(lb1ub2, max(ub1lb2, ub1ub2)));  // max of all bound products
        emptydomain=false;  // no way of it becoming empty.
    }
    
    void combine_ae_xi(IntNum lb1, IntNum ub1, boolean emptydomain1, 
                        IntNum lb2, IntNum ub2, boolean emptydomain2)
    {
        // ub1 lb1 etc represent the outer universal.
        if(emptydomain || emptydomain2)
        {
            if(emptydomain && lb1.isZero() && ub1.isZero())
            {   // the universal is set to zero.
                newupperbound=zero;
                newlowerbound=zero;
                emptydomain=false;
                return;
            }
            else
            {   
                newupperbound=one;
                newlowerbound=one;
                emptydomain=true;
                return;  // still empty
            }
        }
        IntNum lb1ub2=IntNum.times(lb1, ub2);
        IntNum ub1lb2=IntNum.times(ub1, lb2);
        IntNum lb1lb2=IntNum.times(lb1, lb2);
        IntNum ub1ub2=IntNum.times(ub1, ub2);
        // remember l/ub2 represents the outer one, and should really be numbered '1'
        newlowerbound=max(min(lb1lb2, lb1ub2), min(ub1lb2, ub1ub2));
        newupperbound=min(max(lb1lb2, lb1ub2), max(ub1lb2, ub1ub2));
        emptydomain=newlowerbound.compare(newupperbound)>0;
    }
    
    boolean revise_bounds_xi(IntNum lb, IntNum ub, IntNum denominator)
    {
        // This one divides through by denominator, which is c2.
        assert denominator.compare(zero)>0;
        
        // These need to be rounded inwards (i.e. towards the other bound, not towards 0.)
        ub=IntNum.quotient(ub, denominator, IntNum.FLOOR);
        lb=IntNum.quotient(lb, denominator, IntNum.CEILING);
        
        System.out.println("Setting xi to ["+lb+", "+ub+"]");
        
        intnum_bounds var=variables[xi];
        
        if(var.upperbound_big().compare(ub)>0)
        {
            System.out.println("Prunning upperbound");
            boolean flag=var.exclude_upper(ub, null);  // requeue this constraint
            if(!flag) return false;
        }
        
        if(var.lowerbound_big().compare(lb)<0)
        {
            System.out.println("Pruning lowerbound");
            boolean flag=var.exclude_lower(lb, null);  // requeue
            if(!flag) return false;
        }
        
        //System.out.println("New bounds: ["+var.lowerbound_big()+", "+var.upperbound_big()+"]");
        return true;
    }


/*
    // newer version, pre 5-value.
    void combine_axi(IntNum lb1, IntNum ub1, IntNum denom1, boolean emptydomain1, 
                        IntNum lb2, IntNum ub2, IntNum denom2, boolean emptydomain2)
    {
        if(emptydomain2)
        {
            // the resolvent is also empty.
            denominator=one;
            newlowerbound=one;
            newupperbound=one;
            emptydomain=true;
            return; 
        }
        
        // remember ub1 etc refer to the universal xi.
        assert denom1.isOne() && denom2.isOne();
        
        IntNum lb1ub2=IntNum.times(lb1, ub2);
        IntNum ub1lb2=IntNum.times(ub1, lb2);
        IntNum lb1lb2=IntNum.times(lb1, lb2);
        IntNum ub1ub2=IntNum.times(ub1, ub2);
        
        denominator=IntNum.times(c2, IntNum.times(lb1, ub1));   // Changed from lb2, ub2
        
        assert !denominator.isZero();  // given the context of where it is called, the den
                                        // should never be zero.
        
        newlowerbound=max(min(lb1lb2, lb1ub2), min(ub1lb2, ub1ub2));
        newupperbound=min(max(lb1lb2, lb1ub2), max(ub1lb2, ub1ub2));
        emptydomain=newlowerbound.compare(newupperbound)>0;
        
        assert !emptydomain;
        
        if(denominator.isNegative())
        {
            IntNum temp=newlowerbound;
            newlowerbound=IntNum.neg(newupperbound);
            newupperbound=IntNum.neg(temp);
            denominator=IntNum.neg(denominator);
        }
    }
    */
    
    /*void combine_axi(IntNum lb1, IntNum ub1, IntNum denom1, boolean emptydomain1, 
                        IntNum lb2, IntNum ub2, IntNum denom2, boolean emptydomain2)
    {
        // remember the passed-in parameters refer to the universal xi.
        assert denom1.isOne() && denom2.isOne();
        
        IntNum lb1ub2=IntNum.times(lb1, ub2);
        IntNum ub1lb2=IntNum.times(ub1, lb2);
        IntNum lb1lb2=IntNum.times(lb1, lb2);
        IntNum ub1ub2=IntNum.times(ub1, ub2);
        
        denominator=IntNum.times(c2, IntNum.times(lb1, ub1));   // Changed from lb2, ub2
        
        // may need to be altered in a similar way to above.
        if(denominator.isZero())
        {
            // some or all of the fractions in the min/max are not defined, so
            // we cannot compute the min and max.
            // The bounds may as well be set to one.
            newlowerbound=one;
            newupperbound=one;
            emptydomain=false;
        }
        else if(emptydomain2)
        {
            // the resolvent is also empty.
            denominator=one;
            newlowerbound=one;
            newupperbound=one;
            emptydomain=true; 
        }
        else
        {
            newlowerbound=max(min(lb1lb2, lb1ub2), min(ub1lb2, ub1ub2));
            newupperbound=min(max(lb1lb2, lb1ub2), max(ub1lb2, ub1ub2));
            emptydomain=newlowerbound.compare(newupperbound)>0;
            if(denominator.isNegative())
            {
                IntNum temp=newlowerbound;
                newlowerbound=IntNum.neg(newupperbound);
                newupperbound=IntNum.neg(temp);
                denominator=IntNum.neg(denominator);
            }
        }
    }*/
