//{{{  Banner                                   

//============================================================================
//
//      infer.cxx
//
//      Inference for common conflicts.
//
//============================================================================
// ####ECOSHOSTGPLCOPYRIGHTBEGIN####                                        
// -------------------------------------------                              
// This file is part of the eCos host tools.                                
// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.            
//
// This program is free software; you can redistribute it and/or modify     
// it under the terms of the GNU General Public License as published by     
// the Free Software Foundation; either version 2 or (at your option) any   
// later version.                                                           
//
// This program is distributed in the hope that it will be useful, but      
// WITHOUT ANY WARRANTY; without even the implied warranty of               
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        
// General Public License for more details.                                 
//
// You should have received a copy of the GNU General Public License        
// along with this program; if not, write to the                            
// Free Software Foundation, Inc., 51 Franklin Street,                      
// Fifth Floor, Boston, MA  02110-1301, USA.                                
// -------------------------------------------                              
// ####ECOSHOSTGPLCOPYRIGHTEND####                                          
//============================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):   bartv
// Contact(s):  bartv
// Date:        1999/11/1
// Version:     0.01
//
//####DESCRIPTIONEND####
//============================================================================

//}}}
//{{{  #include's                               

// ----------------------------------------------------------------------------
#include "cdlconfig.h"

// Get the infrastructure types, assertions, tracing and similar
// facilities.
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/cyg_trac.h>

// <cdlcore.hxx> defines everything implemented in this module.
// It implicitly supplies <string>, <vector> and <map> because
// the class definitions rely on these headers.
#include <cdlcore.hxx>

//}}}

//{{{  CdlInfer class                           

//{{{  Description                              

// ----------------------------------------------------------------------------
// The following functions provide the main entry points for inference.
//
// 1) bool CdlInfer::make_active(CdlTransaction, CdlValuable, level)
//    Do whatever it takes to make the valuable active in
//    a clean sub-transaction, if possible.
// 2) bool CdlInfer::make_inactive(CdlTransaction, CdlValuable, level)
//    Do whatever it takes to make the valuable inactive.
// 3) bool CdlInfer::set_valuable_value(CdlTransaction, CdlValuable, CdlSimpleValue&, level)
//    Try to set the valuable to the specified value, taking into
//    account the different flavors.
// 4) bool CdlInfer::set_valuable_bool(CdlTransaction, CdlValuable, bool, level)
//    Similar to (3) but deals with the boolean aspect of the valuable
//    rather than the data part.
// 5) bool CdlInfer::subexpr(CdlTransaction, CdlExpression, int index, CdlSimpleValue& goal, level)
//    Process a sub-expression and try to make it evaluate to the
//    goal.
// 6) bool CdlInfer::subexpr_bool(CdlTransaction, CdlExpression, int index, bool goal, level)
//    Ditto but only deal with boolean goals. If the expression starts to
//    involve arithmetic etc. then we need to move to CdlInfer::subexpr()
//
//    As might be expected, the sub-expression handlers contain a big
//    switch statement and calls into various auxiliary functions when
//    necessary.
//
// For convenience the various entry points check whether or not the
// desired condition is already satisfied.

//}}}
//{{{  Forward declarations                     

// ----------------------------------------------------------------------------

static bool infer_handle_interface_value(CdlTransaction, CdlInterface, CdlSimpleValue&, int);
static bool infer_handle_reference_bool(CdlTransaction, CdlValuable, bool, int);

//}}}
//{{{  CdlInfer::make_active()                  

// ----------------------------------------------------------------------------
// Making a node active. This requires the following conditions to be
// satisfied:
// 1) the parent must be made active
// 2) if the parent has flavor bool or booldata, it must be enabled
// 3) any active_if properties 

bool
CdlInfer::make_active(CdlTransaction transaction, CdlNode node, int level)
{
    CYG_REPORT_FUNCNAMETYPE("CdlInfer::make_active", "result %d");
    CYG_REPORT_FUNCARG3XV(transaction, node, level);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(node);

    bool result = false;
    if (transaction->is_active(node)) {
        result = true;
        CYG_REPORT_RETVAL(result);
        return result;
    }
    
    CdlContainer parent = node->get_parent();
    CYG_ASSERT_CLASSC(parent);
    if (!transaction->is_active(parent)) {
        if (!CdlInfer::make_active(transaction, parent, level)) {
            CYG_REPORT_RETVAL(result);
            return result;
        }
    }
    // The parent is now active. Does it have to be enabled as well?
    CdlValuable parent_valuable = dynamic_cast<CdlValuable>(static_cast<CdlNode>(parent));
    if (0 != parent_valuable) {
        CdlValueFlavor flavor = parent_valuable->get_flavor();
        if (((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) &&
            !parent_valuable->is_enabled(transaction)) {
            if (!CdlInfer::set_valuable_bool(transaction, parent_valuable, true, level)) {
                CYG_REPORT_RETVAL(result);
                return result;
            }
        }
    }
    // The parent is now active and enabled. Are there any active_if properties to worry about?
    CdlValuable valuable = dynamic_cast<CdlValuable>(node);
    if (0 != valuable) {
        std::vector<CdlProperty_GoalExpression> active_if_goals;
        std::vector<CdlProperty_GoalExpression>::iterator goal_i;
        valuable->get_active_if_conditions(active_if_goals);
        for (goal_i = active_if_goals.begin(); goal_i != active_if_goals.end(); goal_i++) {
            
            CdlEvalContext context(transaction, valuable, *goal_i);
            try {
                if (!(*goal_i)->eval(context)) {
                    CdlExpression expr = (*goal_i)->get_expression();
                    if (!CdlInfer::subexpr_bool(transaction, expr, expr->first_subexpression, true, level)) {
                        CYG_REPORT_RETVAL(result);
                        return result;
                    }
                }
            } catch(...) {
                CYG_REPORT_RETVAL(result);
                return result;
            }
        }
        
    }
    
    result = transaction->is_active(node);
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  CdlInfer::make_inactive()                

// ----------------------------------------------------------------------------
// Making a node inactive can be done in three ways:
// 1) if the parent is boolean, try disabling it
// 2) otherwise if the parent can be made inactive, that will do
//    fine.
// 3) if there are any active_if properties, they could be considered
//    as well. For now this possibility is ignored.

bool
CdlInfer::make_inactive(CdlTransaction transaction, CdlNode node, int level)
{
    CYG_REPORT_FUNCNAMETYPE("CdlInfer::make_inactive", "result %d");
    CYG_REPORT_FUNCARG3XV(transaction, node, level);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(node);

    bool result = false;
    if (!transaction->is_active(node)) {
        result = true;
        CYG_REPORT_RETVAL(result);
        return result;
    }

    CdlContainer parent = node->get_parent();
    if (0 == parent) {
        // No point in trying to disable the entire configuration.
        CYG_REPORT_RETVAL(result);
        return result;
    }
    
    CdlValuable  parent_valuable = dynamic_cast<CdlValuable>(parent);
    if (0 != parent_valuable) {
        CdlValueFlavor flavor = parent_valuable->get_flavor();
        if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
            // Since the current node is active the parent must currently be enabled.
            // A sub-transaction is needed because an alternative approach is
            // possible later on.
            CdlTransaction subtransaction = transaction->make(transaction->get_conflict());
            if (CdlInfer::set_valuable_bool(subtransaction, parent_valuable, false, level)) {
                subtransaction->commit();
                delete subtransaction;
                result = true;
                CYG_REPORT_RETVAL(result);
                return result;
            } else {
                subtransaction->cancel();
                delete subtransaction;
            }
        }
    }

    // It is not possible to disable the parent. How about making it inactive?
    if (CdlInfer::make_inactive(transaction, parent, level)) {
        result = true;
        CYG_REPORT_RETVAL(result);
        return result;
    }

    // For now do not try to mess about with active_if conditions.
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  CdlInfer::set_valuable_value()           

// ----------------------------------------------------------------------------
// Deal with the value part of a valuable. The valuable is known to exist
// and be active, so this code only deals with the actual value part.

bool
CdlInfer::set_valuable_value(CdlTransaction transaction, CdlValuable valuable, CdlSimpleValue& goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("CdlInfer::set_valuable_value", "result %d");
    CYG_REPORT_FUNCARG3XV(transaction, valuable, level);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(valuable);
    CYG_PRECONDITIONC(transaction->is_active(valuable));

    bool result = false;

    const CdlValue& current_value = transaction->get_whole_value(valuable);
    CdlValueFlavor  flavor        = current_value.get_flavor();
    bool bool_goal                = goal.get_bool_value();

    switch(flavor) {
      default                  :
      case CdlValueFlavor_None :
          break;

      case CdlValueFlavor_Bool :
          if (bool_goal == current_value.is_enabled()) {
              result = true;
          } else {
              if (valuable->is_modifiable() &&
                  (0 == dynamic_cast<CdlLoadable>(valuable)) &&
                  !transaction->changed_by_user(valuable)) {

                  valuable->set_enabled(transaction, bool_goal, CdlValueSource_Inferred);
                  valuable->set_source(transaction, CdlValueSource_Inferred);
                  result = transaction->resolve_recursion(level);
              }

          }
          break;

      case CdlValueFlavor_BoolData :
          if (!bool_goal && !current_value.is_enabled()) {
              result = true;
          } else if (bool_goal && current_value.is_enabled() && (goal == current_value.get_simple_value())) {
              result = true;
          } else {
              if (valuable->is_modifiable() &&
                  (0 == dynamic_cast<CdlLoadable>(valuable)) &&
                  !transaction->changed_by_user(valuable)) {
                  
                  if (!bool_goal) {
                      valuable->disable(transaction, CdlValueSource_Inferred);
                  } else {
                      valuable->enable_and_set_value(transaction, goal, CdlValueSource_Inferred);
                  }
                  valuable->set_source(transaction, CdlValueSource_Inferred);
                  result = transaction->resolve_recursion(level);
              } else if (0 != dynamic_cast<CdlInterface>(valuable)) {
                  // Interfaces are not directly modifiable, but their implementors are.
                  result = infer_handle_interface_value(transaction, dynamic_cast<CdlInterface>(valuable), goal, level);
              }
          }
          break;

      case CdlValueFlavor_Data:
        // Now check whether or not the valuable already has the desired value
        if (goal == current_value.get_simple_value()) {
            result = true;
        } else {
            if (valuable->is_modifiable() &&
                (0 == dynamic_cast<CdlLoadable>(valuable)) &&
                !transaction->changed_by_user(valuable)) {
    
                // Make the change, propagate, and perform further resolution.
                valuable->set_value(transaction, goal, CdlValueSource_Inferred);
                valuable->set_source(transaction, CdlValueSource_Inferred);
                result = transaction->resolve_recursion(level);
            } else if (0 != dynamic_cast<CdlInterface>(valuable)) {
                // Interfaces are not directly modifiable, but their implementors are.
                result = infer_handle_interface_value(transaction, dynamic_cast<CdlInterface>(valuable), goal, level);
            }
        }
        break;
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  CdlInfer::set_valuable_bool()            

// ----------------------------------------------------------------------------
// Deal with the boolean part of a valuable. It is assumed that active vs.
// inactive is dealt with elsewhere so this code only needs to worry
// about the valuable itself.

bool
CdlInfer::set_valuable_bool(CdlTransaction transaction, CdlValuable valuable, bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("CdlInfer::set_valuable_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, valuable, goal, level);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(valuable);

    bool result = false;

    // Examine the current flavor. If None or Data then the valuable
    // is always enabled. If BoolData or Boolean then the condition
    // may be satisfied already, otherwise an attempt must be made
    // to change the value and see what happens.
    CdlValueFlavor flavor = valuable->get_flavor();
    if (CdlValueFlavor_None == flavor) {
        if (goal) {
            result = true;
        }
        CYG_REPORT_RETVAL(result);
        return result;
    }

    if (CdlValueFlavor_Data == flavor) {
        std::string value = valuable->get_value(transaction);
        if (goal) {
            if (("" != value) && ("0" != value)) {
                result = true;
            }
        } else {
            if (("" == value) || ("0" == value)) {
                result = true;
            }
        }
        CYG_REPORT_RETVAL(result);
        return result;
    }
    
    CYG_ASSERTC((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor));
    bool enabled = valuable->is_enabled(transaction);
    if (enabled == goal) {
        result = true;
        CYG_REPORT_RETVAL(result);
        return result;
    }

    // enabled != goal, and we have a boolean or booldata item.
    // Before we actually try making any changes, is this sensible?
    if (!valuable->is_modifiable() ||
        (0 != dynamic_cast<CdlLoadable>(valuable)) ||
        transaction->changed_by_user(valuable)) {

        CYG_REPORT_RETVAL(result);
        return result;
    }
    // If we are about to disable a container, better check that this would
    // not annoy the user either
    if (!goal) {
        CdlContainer container = dynamic_cast<CdlContainer>(valuable);
        if ((0 != container) && transaction->subnode_changed_by_user(container)) {
            CYG_REPORT_RETVAL(result);
            return result;
        }
    }
    
    // Try to change the state, propagate, and perform further resolution.
    valuable->set_enabled(transaction, goal, CdlValueSource_Inferred);
    valuable->set_source(transaction, CdlValueSource_Inferred);
    result = transaction->resolve_recursion(level);
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  infer_choose()                           

// ----------------------------------------------------------------------------
// Given two sub-transactions which may or may not have succeeded, pick the
// preferred one. This happens for many binary operators.

static bool
infer_lhs_preferable(CdlTransaction lhs_transaction, bool lhs_result, CdlTransaction rhs_transaction, bool rhs_result)
{
    CYG_REPORT_FUNCNAMETYPE("infer_choose2", "result %d");
    CYG_REPORT_FUNCARG4XV(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
    CYG_PRECONDITIONC(lhs_result || rhs_result);
    
    bool result = true;
    
    if (lhs_result && !rhs_result) {
        // Only the lhs succeeded.
        result = true;
    } else if (!lhs_result && rhs_result) {
        // Only the rhs succeeded.
        result = false;
    } else if (lhs_result && rhs_result) {
        // Both sides succeeded. Next check for user_confirmation.
        bool lhs_confirm_needed = lhs_transaction->user_confirmation_required();
        bool rhs_confirm_needed = rhs_transaction->user_confirmation_required();
        if (lhs_confirm_needed && !rhs_confirm_needed) {
            result = false;
        } else if (!lhs_confirm_needed && rhs_confirm_needed) {
            result = true;
        } else {
            // Neither or both of the two sides need user confirmation, so they
            // are equal in that respect
            if (lhs_transaction->is_preferable_to(rhs_transaction)) {
                result = true;
            } else {
                result = false;
            }
        }
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

// A variant which will actually do the commits and cancels. This is
// commonly required when doing inferences of binary operators.
static bool
infer_choose2(CdlTransaction lhs_transaction, bool lhs_result, CdlTransaction rhs_transaction, bool rhs_result)
{
    CYG_REPORT_FUNCNAMETYPE("infer_choose2", "result %d");
    CYG_REPORT_FUNCARG4XV(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
    bool result = false;

    if (lhs_result || rhs_result) {
        bool lhs_preferable = infer_lhs_preferable(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
        if (lhs_preferable) {
            rhs_transaction->cancel();
            lhs_transaction->commit();
        } else {
            lhs_transaction->cancel();
            rhs_transaction->commit();
        }
        result = true;
    } else {
        // Neither side succeeded.
        lhs_transaction->cancel();
        rhs_transaction->cancel();
    }
    
    // Zero or one of these transactions will have been committed,
    // neither is still necessary.
    delete lhs_transaction;
    delete rhs_transaction;

    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  infer_handle_interface()                 

// ----------------------------------------------------------------------------
// Set an interface to a specific value, which should be some number n.
// If (n == 0) then all implementers must be disabled or made inactive.
// If (n == 1) then exactly one of the implementers must be active and enabled.
// Other combinations are not considered here, they could lead to an
// exponential explosion.

static bool
infer_handle_interface_value(CdlTransaction transaction, CdlInterface interface, CdlSimpleValue& goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_reference_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, interface, &goal, level);
    bool result = false;

    if (goal.has_integer_value()) {
        cdl_int real_goal = goal.get_integer_value();
        if (real_goal == interface->get_integer_value(transaction)) {
            result = true;
        } else if (0 == real_goal) {
            // All implementers must be disabled or made inactive. This
            // can be achieved by creating a sub-transaction and calling
            // infer_handle_reference_bool() on all of the implementers.
            //
            // However there are no guarantees that the result is what
            // is intended. Updating a later implementer may as a side
            // effect cause an earlier one to become active again. Also
            // there may be confusion with valuables with the data
            // flavor being given a value of 0. Hence a final check is
            // needed that the new interface value really is the desired goal.
            CdlTransaction sub_transaction;
            std::vector<CdlValuable> implementers;
            std::vector<CdlValuable>::const_iterator impl_i;

            sub_transaction = transaction->make(transaction->get_conflict());
            try {
                interface->get_implementers(implementers);
                for (impl_i = implementers.begin(); impl_i != implementers.end(); impl_i++) {
                    (void) infer_handle_reference_bool(sub_transaction, *impl_i, false, level);
                }
                if (0 == interface->get_integer_value(sub_transaction)) {
                    sub_transaction->commit();
                    result = true;
                } else {
                    sub_transaction->cancel();
                }
            } catch (...) {
                delete sub_transaction;
                throw;
            }
            delete sub_transaction;
            sub_transaction = 0;
            
        } else if (1 == real_goal) {
            // This is a bit trickier than the above. We need n
            // sub-transactions, one per implementer. In each
            // sub-transaction we try to set exactly one of the
            // implementers to enabled and the rest to disabled.
            std::vector<CdlValuable>    implementers;
            unsigned int                impl_count;
            unsigned int                i, j;
            
            interface->get_implementers(implementers);
            impl_count = implementers.size();
            std::vector<CdlTransaction> sub_transactions;
            std::vector<bool>           results;

            try {
                for (i = 0; i < impl_count; i++) {
                    CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
                    sub_transactions.push_back(sub_transaction);
                    results.push_back(false);
                    results[i]          = false;
                }
                for (i = 0; i < impl_count; i++) {
                    for (j = 0; j < impl_count; j++) {
                        (void) infer_handle_reference_bool(sub_transactions[i], implementers[j], (i == j), level);
                    }
                    if (1 == interface->get_integer_value(sub_transactions[i])) {
                        results[i] = true;
                    }
                }
                
                // At this point we may have some combination of successful and unsucessful
                // sub-transactions, and it is time to choose the best one.
                CdlTransaction  preferred = 0;
                for (i = 0; i < impl_count; i++) {
                    if (results[i]) {
                        preferred = sub_transactions[i];
                        break;
                    }
                }

                for (j = i + 1; j < impl_count; j++) {
                    if (results[j]) {
                        if (!infer_lhs_preferable(preferred, true, sub_transactions[j], true)) {
                            preferred = sub_transactions[j];
                        }
                    }
                }

                // Now either preferred == 0, i.e. all
                // sub-transactions failed and we want to cancel them
                // all. Or we have a viable sub-transaction.
                for (i = 0; i < impl_count; i++) {
                    if (preferred == sub_transactions[i]) {
                        sub_transactions[i]->commit();
                        result = true;
                    } else {
                        sub_transactions[i]->cancel();
                    }
                    delete sub_transactions[i];
                    sub_transactions[i] = 0;
                }
                
            } catch(...) {
                for (i = 0; i < sub_transactions.size(); i++) {
                    if (0 != sub_transactions[i]) {
                        sub_transactions[i]->cancel();
                        delete sub_transactions[i];
                        sub_transactions[i] = 0;
                    }
                }
            }
        }
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  infer_handle_reference()                 

// ----------------------------------------------------------------------------
// We are processing an expression and have reached a point where we
// need <reference>, !<reference> or <reference>==<value>. The
// reference may currently be unbound, in which case 0 is the only
// goal that can be satisfied. If the reference is bound then it may
// be possible to satisfy the goal by setting the value. In addition
// it is necessary to worry about active vs. inactive state.

static bool
infer_handle_reference_bool(CdlTransaction transaction, CdlValuable valuable, bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_reference_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, valuable, goal, level);

    bool result = false;
    if (0 == valuable) {
        if (!goal) {
            result = true;
        }
        CYG_REPORT_RETVAL(result);
        return result;
    }

    // If the valuable should evaluate to true then it must be both active
    // and be either enabled or have a non-zero value.
    if (goal) {
        if (!transaction->is_active(valuable)) {
            if (!CdlInfer::make_active(transaction, valuable, level)) {
                CYG_REPORT_RETVAL(result);
                return result;
            }
        }
        if (CdlInfer::set_valuable_bool(transaction, valuable, true, level)) {
            result = true;
        }

    } else {
        // If the valuable should evaluate to false then it must be either
        // inactive or it must be disabled or have a zero value.
        if (!transaction->is_active(valuable)) {
            // The goal is already satisfied, no need to proceed
            result = true;
            CYG_REPORT_RETVAL(result);
            return result;
        }
        
        // There is a choice to be made so two sub-transactions are
        // needed. Disabling is generally preferred to making inactive.
        CdlTransaction value_transaction    = transaction->make(transaction->get_conflict());
        CdlTransaction inactive_transaction = 0;
        bool value_result = CdlInfer::set_valuable_bool(value_transaction, valuable, false, level);
        if (value_result && !value_transaction->user_confirmation_required()) {
            value_transaction->commit();
            delete value_transaction;
            value_transaction = 0;
            result = true;
            CYG_REPORT_RETVAL(result);
            return result;
        }

        inactive_transaction = transaction->make(transaction->get_conflict());
        bool inactive_result = CdlInfer::make_inactive(inactive_transaction, valuable, level);
        if (!inactive_result) {
            if (value_result) {
                // Changing the value is the only solution.
                inactive_transaction->cancel();
                value_transaction->commit();
                result = true;
            } else {
                inactive_transaction->cancel();
                value_transaction->cancel();
                result = false;
            }
        } else {
            if (!value_result) {
                // Making the valuable inactive is the only solution.
                value_transaction->cancel();
                inactive_transaction->commit();
                result = true;
            } else if (!inactive_transaction->user_confirmation_required()) {
                // Disabling the valuable would require user confirmation, making it inactive does not
                value_transaction->cancel();
                inactive_transaction->commit();
                result = true;
            } else {
                // Both approaches are valid but would require user confirmation.
                // Pick the preferred one.
                if (value_transaction->is_preferable_to(inactive_transaction)) {
                    inactive_transaction->cancel();
                    value_transaction->commit();
                    result = true;
                } else {
                    value_transaction->cancel();
                    inactive_transaction->commit();
                    result = true;
                }
            }
        }

        delete value_transaction;
        delete inactive_transaction;
        value_transaction = 0;
        inactive_transaction = 0;
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------
// Try to set a valuable to a particular value. Of course the reference
// may not be bound yet.
//
// First check whether or not the valuable is currently active. If it is
// inactive and the goal is 0 then we have succeeded. If it is active and
// the goal is 0 then we could try to make the valuable inactive, but
// this possibility is ignored for now in case it leads to unexpected
// behaviour. If it is active then we try to set the value, using
// CdlInfer::set_valuable_value().

static bool
infer_handle_reference_value(CdlTransaction transaction, CdlValuable valuable, CdlSimpleValue& goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_reference", "result %d");
    CYG_REPORT_FUNCARG3XV(transaction, valuable, level);

    bool result = false;

    if (0 == valuable) {
        if (goal == (cdl_int) 0) {
            result = true;
        }
    } else {
    
        bool active = transaction->is_active(valuable);
        if (!active) {
            if (goal == (cdl_int) 0) {
                result = true;
            }
        } else {
            result = CdlInfer::set_valuable_value(transaction, valuable, goal, level);
        }
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  infer_handle_xxx_constant()              

// ----------------------------------------------------------------------------
// Somewhere in the expression processing we have encountered a string
// constant. The expression cannot be changed, so either the goal matches
// the constant or it does not.
static bool
infer_handle_string_constant_bool(CdlSimpleValue& constant, bool goal)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_string_constant_bool", "result %d");
    bool result = false;

    if (goal) {
        if (("" != constant.get_value()) && ("0" != constant.get_value())) {
            result = true;
        }
    } else {
        if (("" == constant.get_value()) || ("0" == constant.get_value())) {
            result = true;
        }
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

static bool
infer_handle_string_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_string_constant_value", "result %d");
    bool result = false;

    if (constant.get_value() == goal.get_value()) {
        result = true;
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------
// Integers are also fairly straightforward.
static bool
infer_handle_integer_constant_bool(CdlSimpleValue& constant, bool goal)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_integer_constant_bool", "result %d");
    CYG_PRECONDITIONC(constant.has_integer_value());
    bool result = false;

    if (goal) {
        if (0 != constant.get_integer_value()) {
            result = true;
        }
    } else {
        if (0 == constant.get_integer_value()) {
            result = true;
        }
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

static bool
infer_handle_integer_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_integer_constant_value", "result %d");
    CYG_PRECONDITIONC(constant.has_integer_value());
    bool result = false;

    if (goal.has_integer_value() && (constant.get_integer_value() == goal.get_integer_value())) {
        result = true;
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------
// Doubles are also straightforward, except than an exact comparision may
// be too strict. There is not a lot that can be done about this right now.
// Future enhancements to CDL may support tolerances.
static bool
infer_handle_double_constant_bool(CdlSimpleValue& constant, bool goal)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_double_constant_bool", "result %d");
    CYG_PRECONDITIONC(constant.has_double_value());
    bool result = false;

    if (goal) {
        if (0.0 != constant.get_double_value()) {
            result = true;
        }
    } else {
        if (0.0 == constant.get_double_value()) {
            result = true;
        }
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

static bool
infer_handle_double_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_double_constant_value", "result %d");
    CYG_PRECONDITIONC(constant.has_double_value());
    bool result = false;

    if (goal.has_double_value() && (constant.get_double_value() == goal.get_double_value())) {
        result = true;
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  infer_handle_logical_xxx()               

// ----------------------------------------------------------------------------
// Logical not simply involves inverting the goal and then trying to infer
// the rest of the sub-expression. There is little point in touching
// the other arguments.
static bool
infer_handle_logical_NOT_bool(CdlTransaction transaction, CdlExpression expr, unsigned int index, bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_logical_NOT_bool", "result %d");

    bool result = CdlInfer::subexpr_bool(transaction, expr, index, !goal, level);
    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------
// Depending on the goal, we want either both sides of the AND to evaluate to
// true, or we want one of the sides to evaluate to false.
static bool
infer_handle_AND_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
                      bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_AND_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC(lhs != rhs);

    bool result = false;
    
    if (goal) {
        // Both sides must be true in the same transaction, in case
        // the solutions overlap in conflicting ways. A sub-transaction
        // is still used to avoid polluting current values if the lhs
        // can be inferred but not the rhs.
        CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
        if (CdlInfer::subexpr_bool(sub_transaction, expr, lhs, true, level) &&
            CdlInfer::subexpr_bool(sub_transaction, expr, rhs, true, level)) {
            sub_transaction->commit();
            result = true;
        } else {
            sub_transaction->cancel();
        }
        delete sub_transaction;
    } else {
        // We need to try out both sides of the OR and see which one is preferable.
        // An optimization would be to only try the LHS, but trying both allows
        // for a more informed choice.
        CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
        CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
        bool lhs_result = CdlInfer::subexpr_bool(lhs_transaction, expr, lhs, false, level);
        bool rhs_result = CdlInfer::subexpr_bool(rhs_transaction, expr, rhs, false, level);

        result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------
// The support for the other logical operations involves basically minor
// variants of the above.

static bool
infer_handle_OR_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
                     bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_OR_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC(lhs != rhs);


    bool result = false;
    
    if (goal) {
        // We need to try out both sides of the OR and see which one is preferable.
        // An optimization would be to only try the LHS, but trying both allows
        // for a more informed choice.
        CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
        CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
        bool lhs_result = CdlInfer::subexpr_bool(lhs_transaction, expr, lhs, true, level);
        bool rhs_result = CdlInfer::subexpr_bool(rhs_transaction, expr, rhs, true, level);

        result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
    } else {
        
        // !(A || B) -> !A && !B
        CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
        if (CdlInfer::subexpr_bool(sub_transaction, expr, lhs, false, level) &&
            CdlInfer::subexpr_bool(sub_transaction, expr, rhs, false, level)) {
            sub_transaction->commit();
            result = true;
        } else {
            sub_transaction->cancel();
        }
        delete sub_transaction;
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------

static bool
infer_handle_IMPLIES_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
                     bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_implies_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC(lhs != rhs);


    bool result = false;
    
    if (goal) {
        // A implies B -> !A || B
        // Given a choice between !A or B, arguably the "implies"
        // operator has the connotation that B is preferred. All other
        // things being equal, infer_choose2() will prefer the rhs
        // over the lhs so this is achieved automagically.
        
        CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
        CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
        bool lhs_result = CdlInfer::subexpr_bool(lhs_transaction, expr, lhs, false, level);
        bool rhs_result = CdlInfer::subexpr_bool(rhs_transaction, expr, rhs, true, level);

        result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
        
    } else {
        
        // !(A implies B) -> !(!A || B) -> (A && !B)
        CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
        if (CdlInfer::subexpr_bool(sub_transaction, expr, lhs, true, level) &&
            CdlInfer::subexpr_bool(sub_transaction, expr, rhs, false, level)) {
            sub_transaction->commit();
            result = true;
        } else {
            sub_transaction->cancel();
        }
        delete sub_transaction;
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------

static bool
infer_handle_XOR_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
                     bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_XOR_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC(lhs != rhs);


    bool result = false;
    
    if (goal) {
        // (A xor B) -> (A && !B) || (!A && B)

        CdlTransaction sub1 = transaction->make(transaction->get_conflict());
        CdlTransaction sub2 = transaction->make(transaction->get_conflict());
        bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, true, level) &&
                        CdlInfer::subexpr_bool(sub1, expr, rhs, false, level));
        bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, false, level) &&
                        CdlInfer::subexpr_bool(sub2, expr, rhs, true, level));
                           
        result = infer_choose2(sub1, result1, sub2, result2);
        
    } else {
        
        // !(A xor B) -> (!A && !B) || (A && B)
        CdlTransaction sub1 = transaction->make(transaction->get_conflict());
        CdlTransaction sub2 = transaction->make(transaction->get_conflict());
        bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, false, level) &&
                        CdlInfer::subexpr_bool(sub1, expr, rhs, false, level));
        bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, true, level) &&
                        CdlInfer::subexpr_bool(sub2, expr, rhs, true, level));
                           
        result = infer_choose2(sub1, result1, sub2, result2);
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

// ----------------------------------------------------------------------------

static bool
infer_handle_EQV_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
                     bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_EQV_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC(lhs != rhs);


    bool result = false;
    
    if (goal) {
        // (A eqv B) -> (A && B) || (!A && !B)

        CdlTransaction sub1 = transaction->make(transaction->get_conflict());
        CdlTransaction sub2 = transaction->make(transaction->get_conflict());
        bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, true, level) &&
                        CdlInfer::subexpr_bool(sub1, expr, rhs, true, level));
        bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, false, level) &&
                        CdlInfer::subexpr_bool(sub2, expr, rhs, false, level));
                           
        result = infer_choose2(sub1, result1, sub2, result2);
    } else {
        // !(A eqv B) -> (A && !B) || (!A && B)

        CdlTransaction sub1 = transaction->make(transaction->get_conflict());
        CdlTransaction sub2 = transaction->make(transaction->get_conflict());
        bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, true, level) &&
                        CdlInfer::subexpr_bool(sub1, expr, rhs, false, level));
        bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, false, level) &&
                        CdlInfer::subexpr_bool(sub2, expr, rhs, true, level));
                           
        result = infer_choose2(sub1, result1, sub2, result2);
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  infer_handle_Equal()                     

// ----------------------------------------------------------------------------
// Handle expressions of the form A == B. This can be achieved either by
// evaluating B and trying to assign the result to A, or vice versa. There
// is a problem if assigning to one side has a side effect on the other, e.g.
//
//   requires { xyzzy == (xyzzy + 3) }
//
// This has to be guarded against by reevaluating the expression.
//
// At present this code only copes with equality, not inequality.

static bool
infer_handle_equal_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs, bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_equal_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC(lhs != rhs);

    bool result = false;
    if (goal) {
        
        // We need two sub-transactions, The lhs_transaction is for evaluating the lhs
        // and trying to update the rhs. 
        CdlTransaction  lhs_transaction = transaction->make(transaction->get_conflict());
        bool            lhs_result = false;
        try {
            CdlSimpleValue  lhs_value;
            CdlEvalContext  lhs_context(lhs_transaction);
            expr->eval_subexpression(lhs_context, lhs, lhs_value);
            lhs_result = CdlInfer::subexpr_value(lhs_transaction, expr, rhs, lhs_value, level);
            if (lhs_result) {
                CdlSimpleValue check;
                expr->eval_subexpression(lhs_context, lhs, check);
                if (lhs_value != check) {
                    lhs_result = false;
                }
            }
        } catch (...) {
            lhs_result = false;
        }
        
        CdlTransaction  rhs_transaction = transaction->make(transaction->get_conflict());
        bool            rhs_result = false;
        try {
            CdlSimpleValue  rhs_value;
            CdlEvalContext  rhs_context(rhs_transaction);
            expr->eval_subexpression(rhs_context, rhs, rhs_value);
            rhs_result = CdlInfer::subexpr_value(rhs_transaction, expr, lhs, rhs_value, level);
            if (rhs_result) {
                CdlSimpleValue check;
                expr->eval_subexpression(rhs_context, rhs, check);
                if (rhs_value != check) {
                    rhs_result = false;
                }
            }
        } catch (...) {
            rhs_result = false;
        }

        result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  infer_handle_numerical_equal()           

// ----------------------------------------------------------------------------
// Handle expressions of the form A == B, where the comparison has to be
// numerical in basis. This is used primarily for operators like <=
// and >.

static bool
infer_handle_numerical_equal_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs, bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("infer_handle_numerical_equal_bool", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC(lhs != rhs);

    bool result = false;
    if (goal) {
        
        // We need two sub-transactions, The lhs_transaction is for evaluating the lhs
        // and trying to update the rhs. 
        CdlTransaction  lhs_transaction = transaction->make(transaction->get_conflict());
        bool            lhs_result = false;
        try {
            CdlSimpleValue  lhs_value;
            CdlEvalContext  lhs_context(lhs_transaction);
            expr->eval_subexpression(lhs_context, lhs, lhs_value);
            if (lhs_value.has_integer_value() || lhs_value.has_double_value()) {
                lhs_result = CdlInfer::subexpr_value(lhs_transaction, expr, rhs, lhs_value, level);
                if (lhs_result) {
                    CdlSimpleValue check;
                    expr->eval_subexpression(lhs_context, lhs, check);
                    if (lhs_value != check) {
                        lhs_result = false;
                    }
                }
            }
        } catch (...) {
            lhs_result = false;
        }
        
        CdlTransaction  rhs_transaction = transaction->make(transaction->get_conflict());
        bool            rhs_result = false;
        try {
            CdlSimpleValue  rhs_value;
            CdlEvalContext  rhs_context(rhs_transaction);
            expr->eval_subexpression(rhs_context, rhs, rhs_value);
            if (rhs_value.has_integer_value() || rhs_value.has_double_value()) {
                rhs_result = CdlInfer::subexpr_value(rhs_transaction, expr, lhs, rhs_value, level);
                if (rhs_result) {
                    CdlSimpleValue check;
                    expr->eval_subexpression(rhs_context, rhs, check);
                    if (rhs_value != check) {
                        rhs_result = false;
                    }
                }
            }
        } catch (...) {
            rhs_result = false;
        }

        result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  CdlInfer::subexpr_bool()                 

// ----------------------------------------------------------------------------
bool
CdlInfer::subexpr_bool(CdlTransaction transaction, CdlExpression expr, unsigned int index, bool goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("CdlInfer::subexpr_bool", "result %d");
    CYG_REPORT_FUNCARG5XV(transaction, expr, index, goal, level);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC((0 <= index) && (index < expr->sub_expressions.size()));
    
    bool result = false;
    CdlSubexpression& subexpr = expr->sub_expressions[index];

    switch(subexpr.op) {
        
      case CdlExprOp_Reference :
          // The most common case. Follow the reference, and call the appropriate function.
          // Note that the reference may be unbound.
          {
              CdlNode node = expr->references[subexpr.reference_index].get_destination();
              CdlValuable valuable = 0;
              if (0 != node) {
                  valuable = dynamic_cast<CdlValuable>(node);
              }
              result = infer_handle_reference_bool(transaction, valuable, goal, level);
              break;
          }
          
      case CdlExprOp_StringConstant :
          result = infer_handle_string_constant_bool(subexpr.constants, goal);
          break;
                                                
      case CdlExprOp_IntegerConstant :
          result = infer_handle_integer_constant_bool(subexpr.constants, goal);
          break;
          
      case CdlExprOp_DoubleConstant :
          result = infer_handle_double_constant_bool(subexpr.constants, goal);
          break;

      case CdlExprOp_LogicalNot :
          result = infer_handle_logical_NOT_bool(transaction, expr, subexpr.lhs_index, goal, level);
          break;
          
      case CdlExprOp_And :
          result = infer_handle_AND_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
          break;
          
      case CdlExprOp_Or :
          result = infer_handle_OR_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
          break;

      case CdlExprOp_Implies :
          result = infer_handle_IMPLIES_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
          break;
        
      case CdlExprOp_Xor :
          result = infer_handle_XOR_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
          break;
        
      case CdlExprOp_Eqv :
          result = infer_handle_EQV_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
          break;
        
      case CdlExprOp_Equal :
          result = infer_handle_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
          break;

      case CdlExprOp_NotEqual :
          result = infer_handle_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, !goal, level);
          break;

          // <= is satisfied by a numerical equality. However the inverse relation > cannot be handled that way
          // The other comparison operators are much the same.
      case CdlExprOp_LessEqual :
      case CdlExprOp_GreaterEqual :
          if (goal) {
              result = infer_handle_numerical_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, true, level);
          }
          break;

      case CdlExprOp_LessThan :
      case CdlExprOp_GreaterThan :
          if (!goal) {
              result = infer_handle_numerical_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, true, level);
          }
          break;
          
      case CdlExprOp_Function :
          result = CdlFunction::infer_bool(transaction, expr, index, goal, level);
          break;
              
      default:
          // No other inferences are implemented at this stage.
          break;
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
//{{{  CdlInfer::subexpr_value()                

bool
CdlInfer::subexpr_value(CdlTransaction transaction, CdlExpression expr, unsigned int index, CdlSimpleValue& goal, int level)
{
    CYG_REPORT_FUNCNAMETYPE("CdlInfer::subexpr_value", "result %d");
    CYG_REPORT_FUNCARG4XV(transaction, expr, index, level);
    CYG_PRECONDITION_CLASSC(transaction);
    CYG_PRECONDITION_CLASSC(expr);
    CYG_PRECONDITIONC((0 <= index) && (index < expr->sub_expressions.size()));
    
    bool result = false;
    CdlSubexpression& subexpr = expr->sub_expressions[index];

    switch(subexpr.op) {
        
      case CdlExprOp_Reference          :
          // The most common case. Follow the reference, and call the appropriate function.
          // Note that the reference may be unbound.
          {
              CdlNode node = expr->references[subexpr.reference_index].get_destination();
              CdlValuable valuable = 0;
              if (0 != node) {
                  valuable = dynamic_cast<CdlValuable>(node);
              }
              result = infer_handle_reference_value(transaction, valuable, goal, level);
              break;
          }
          
      case CdlExprOp_StringConstant     :
          result = infer_handle_string_constant_value(subexpr.constants, goal);
          break;
                                                
      case CdlExprOp_IntegerConstant    :
          result = infer_handle_integer_constant_value(subexpr.constants, goal);
          break;
          
      case CdlExprOp_DoubleConstant     :
          result = infer_handle_double_constant_value(subexpr.constants, goal);
          break;

      case CdlExprOp_LogicalNot         :
      case CdlExprOp_And                :
      case CdlExprOp_Or                 :
      case CdlExprOp_Implies            :
      case CdlExprOp_Xor                :
      case CdlExprOp_Eqv                :
      {
          bool  new_goal = true;
          if (("0" == goal.get_value()) || ("" == goal.get_value())) {
              new_goal = false;
          }
          result = CdlInfer::subexpr_bool(transaction, expr, index, new_goal, level);
          break;
      }
          
      case CdlExprOp_Function :
          result = CdlFunction::infer_value(transaction, expr, index, goal, level);
          break;
        
      default:
          // No other inferences are implemented at this stage.
          break;
    }

    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}

//}}}
//{{{  Illegal value resolution                 

// ----------------------------------------------------------------------------
// This is not yet implemented.

bool
CdlConflict_IllegalValueBody::inner_resolve(CdlTransaction transaction, int level)
{
    CYG_REPORT_FUNCNAMETYPE("CdlConflict_IllegalValue::inner_resolve", "result %d");
    CYG_REPORT_FUNCARG3XV(this, transaction, level);
    CYG_PRECONDITION_THISC();
    CYG_PRECONDITION_CLASSC(transaction);

    CYG_UNUSED_PARAM(CdlTransaction, transaction);
    
    CYG_REPORT_RETVAL(false);
    return false;
}

//}}}
//{{{  Requires resolution                      

// ----------------------------------------------------------------------------
// The entry point for this code is
// CdlConflict_RequiresBody::resolve(). "this" is a requires conflict
// that needs to be resolved, if possible. There are twos argument: a
// sub-transaction, which should be filled in with the solution if
// possible; and a recursion level indicator, 0 if this is a top-level
// inference engine invocation rather than a recursive one. There are
// additional static parameters inference_recursion_limit and
// inference_override which control details of the inference process.
//
// As an example of what is involved in an inference, consider the
// simple case of a "requires XXX" property. This constraint may not
// be satisfied because XXX is disabled,  because XXX is inactive,
// or both.
//
// Assume for simplicity that XXX is already active. The inference
// engine can now figure out that XXX must be enabled (it must be
// of type bool or booldata, or else the conflict would not have
// arisen). This is achieved by creating a sub-transaction,
// enabling XXX in that sub-transaction, propagating the
// sub-transaction and performing further inference. The inference
// is successfull if no new conflicts are introduced.
//
// However, even if a solution is found it is not necessarily
// acceptable without user confirmation, subject to
// inference_override. This is handled in part by the transaction
// class itself, in the resolve() and user_confirmation_required()
// members. In cases where the inference engine can choose between
// several alternatives it needs to consider this issue for each one.
// Resolving a requires conflict. There are three ways of tackling
// this problem, in order of preference:
//
// 1) change the terms in the expression to make it evaluate to
//    true.
// 2) disable the source so that the requires property is no longer
//    relevant.
// 3) make the source inactive, with the same effect.
//
// The first one should always be tried. If it is entirely successful
// then there is no point in looking any further. If user confirmation
// is required then the second approach should be tried. If that is
// entirely successful then there is no point in looking further.
// If user confirmation is required then the third approach should
// be tried.

bool
CdlConflict_RequiresBody::inner_resolve(CdlTransaction transaction, int level)
{
    CYG_REPORT_FUNCNAME("CdlConflict_Requires::inner_resolve");
    CYG_REPORT_FUNCARG3XV(this, transaction, level);
    CYG_PRECONDITION_THISC();
    CYG_PRECONDITION_CLASSC(transaction);

    bool result = false;

    CdlProperty_GoalExpression  gexpr   = dynamic_cast<CdlProperty_GoalExpression>(this->get_property());
    CdlExpression               expr    = gexpr->get_expression();
    
    // Only create the sub-transactions when needed.
    CdlTransaction expr_transaction     = 0;
    CdlTransaction disable_transaction  = 0;
    CdlTransaction inactive_transaction = 0;

    // Keep track of the preferred solution found to date.
    CdlTransaction preferred_transaction = 0;

    expr_transaction = transaction->make(this);
    if (!CdlInfer::subexpr_bool(expr_transaction, expr, expr->first_subexpression, true, level)) {
        // No luck here.
        expr_transaction->cancel();
        delete expr_transaction;
        expr_transaction = 0;
    } else {
        // We have a possible solution. How acceptable is it?
        if (!expr_transaction->user_confirmation_required()) {
            // Whoopee.
            expr_transaction->commit();
            delete expr_transaction;
            result = true;
            CYG_REPORT_RETVAL(result);
            return result;
        } else {
            // Maybe we can do better.
            preferred_transaction = expr_transaction;
            expr_transaction = 0;
        }
    }

    // Disabling the source only makes sense if we have a bool or booldata item.
    CdlValuable valuable = dynamic_cast<CdlValuable>(this->get_node());
    CYG_ASSERT_CLASSC(valuable);

    if ((CdlValueFlavor_Bool == valuable->get_flavor()) || (CdlValueFlavor_BoolData == valuable->get_flavor())) {
        disable_transaction = transaction->make(this);
        if (!CdlInfer::set_valuable_bool(disable_transaction, valuable, false, level)) {
            // No luck here either.
            disable_transaction->cancel();
            delete disable_transaction;
            disable_transaction = 0;
        } else {
            if (!disable_transaction->user_confirmation_required()) {
                disable_transaction->commit();
                delete disable_transaction;
                if (0 != preferred_transaction) {
                    preferred_transaction->cancel();
                    delete preferred_transaction;
                    preferred_transaction = 0;
                }
                result = true;
                CYG_REPORT_RETVAL(result);
                return result;
            } else if (0 == preferred_transaction) {
                preferred_transaction = disable_transaction;
            } else if (!preferred_transaction->is_preferable_to(disable_transaction)) {
                preferred_transaction->cancel(); 
                delete preferred_transaction;
                preferred_transaction = disable_transaction;
                disable_transaction = 0;
            } else {
                disable_transaction->cancel();
                delete disable_transaction;
                disable_transaction = 0;
            }
        }
    }

    // Now try for the inactive approach. This may work in cases where the disable
    // approach does not if e.g. there are dependencies between two nodes in the
    // same container, or if the source of the conflict is not boolean.
    inactive_transaction = transaction->make(this);
    if (!CdlInfer::make_inactive(inactive_transaction, valuable, level)) {
        inactive_transaction->cancel();
        delete inactive_transaction;
        inactive_transaction = 0;
    } else {
        if (!inactive_transaction->user_confirmation_required()) {
            inactive_transaction->commit();
            delete inactive_transaction;
            if (0 != preferred_transaction) {
                preferred_transaction->cancel();
                delete preferred_transaction;
                preferred_transaction = 0;
            }
            result = true;
            CYG_REPORT_RETVAL(result);
            return result;
        } else if (0 == preferred_transaction) {
            preferred_transaction = inactive_transaction;
        } else if (!preferred_transaction->is_preferable_to(inactive_transaction)) {
            preferred_transaction->cancel(); 
            delete preferred_transaction;
            preferred_transaction = inactive_transaction;
            inactive_transaction = 0;
        } else {
            inactive_transaction->cancel();
            delete inactive_transaction;
            inactive_transaction = 0;
        }
    }

    // Is there any solution at all? If so then use the currently-preferred one.
    if (0 != preferred_transaction) {
        preferred_transaction->commit();
        delete preferred_transaction;
        preferred_transaction = 0;
        result = true;
    }
    
    CYG_REPORT_RETVAL(result);
    return result;
}

//}}}
