friend closure_value 
 operator! (closure_value const &rhs)
 {
     switch (rhs.type) {
     case is_int:    return closure_value(!as_long(rhs), rhs.valid);
     case is_bool:   return closure_value(!as_bool(rhs), rhs.valid); 
     case is_uint:   break;
     }
     return closure_value(!as_ulong(rhs), rhs.valid);
 }
 friend closure_value 
 operator< (closure_value const &lhs, closure_value const &rhs)
 {
     bool cmp = false;
     switch (lhs.type) {
     case is_int:
         switch(rhs.type) {
         case is_bool:   cmp = lhs.value.i < as_long(rhs); break;
         case is_int:    cmp = lhs.value.i < rhs.value.i; break;
         case is_uint:   cmp = lhs.value.ui < rhs.value.ui; break;
         }
         break;
         
     case is_uint:   cmp = lhs.value.ui < as_ulong(rhs); break;
     case is_bool:   cmp = as_bool(lhs) < as_bool(rhs); break;
     }
     return closure_value(cmp, (value_error)(lhs.valid | rhs.valid));
 }
    friend closure_value 
    operator- (closure_value const &rhs)
    {
        switch (rhs.type) {
        case is_int:
            {
                long value = as_long(rhs);
                if (value != 0 && value == -value)
                    return closure_value(-value, error_integer_overflow);
                return closure_value(-value, rhs.valid);
            }
            
        case is_bool:   return closure_value(-as_long(rhs), rhs.valid); 
        case is_uint:   break;
        }

        long value = as_ulong(rhs);
        if (value != 0 && value == -value)
            return closure_value(-value, error_integer_overflow);
        return closure_value(-value, rhs.valid);
    }
    closure_value &
    operator<<= (closure_value const &rhs)
    {
        switch (type) {
        case is_bool:
        case is_int:
            switch (rhs.type) {
            case is_bool:
            case is_int:
                {
                long shift_by = as_long(rhs);
                    
                    if (shift_by > 64) 
                        shift_by = 64;
                    else if (shift_by < -64)
                        shift_by = -64;
                    value.i <<= shift_by; 
                }
                break;
                
            case is_uint:
                {
                unsigned long shift_by = as_ulong(rhs);
                    
                    if (shift_by > 64) 
                        shift_by = 64;
                    value.ui <<= shift_by; 
                
                // Note: The usual arithmetic conversions are not performed on 
                //       bit shift operations.
                }
                break;
            }
            break;

        case is_uint:
            switch (rhs.type) {
            case is_bool:
            case is_int:
                {
                long shift_by = as_long(rhs);
                    
                    if (shift_by > 64) 
                        shift_by = 64;
                    else if (shift_by < -64)
                        shift_by = -64;
                    value.ui <<= shift_by; 
                }
                break;
                
            case is_uint:
                {
                unsigned long shift_by = as_ulong(rhs);
                    
                    if (shift_by > 64) 
                        shift_by = 64;
                    value.ui <<= shift_by; 
                }
                break;
            }
        }
        valid = (value_error)(valid | rhs.valid);
        return *this;
    }
    closure_value &operator%= (closure_value const &rhs)
    {
        switch (type) {
        case is_int:    
            switch(rhs.type) {
            case is_bool:   
            case is_int:
                if (as_long(rhs) != 0) {
                    if (value.i == -value.i && -1 == rhs.value.i) {
                    // LONG_MIN % -1 on two's complement
                        valid = error_integer_overflow;
                    }
                    else {
                        value.i %= as_long(rhs); 
                    }
                }
                else {
                    valid = error_division_by_zero;      // division by zero
                }
                break;
                
            case is_uint:
                if (rhs.value.ui != 0) {
                    value.ui %= rhs.value.ui; 
                    type = is_uint; 
                }
                else {
                    valid = error_division_by_zero;      // division by zero
                }
                break;
            }
            break;
            
        case is_uint: 
            if (as_ulong(rhs) != 0) 
                value.ui %= as_ulong(rhs); 
            else
                valid = error_division_by_zero;      // division by zero
            break;

        case is_bool:  
            if (as_bool(rhs)) {
                switch(rhs.type) {
                case is_int:
                    value.i = (value.b ? 1 : 0) % rhs.value.i;
                    type = is_int;
                    break;
                    
                case is_uint:
                    value.i = (value.b ? 1 : 0) % rhs.value.ui;
                    type = is_int;
                    break;
                    
                case is_bool:
                    break;
                }                    
            }
            else {
                valid = error_division_by_zero;      // division by zero
            }
        }
        return *this;
    }
 closure_value &operator*= (closure_value const &rhs)
 {
     switch (type) {
     case is_int:    
         switch(rhs.type) {
         case is_bool:   value.i *= as_long(rhs); break;
         case is_int:
             {
                 long result = value.i * rhs.value.i; 
                 if (0 != value.i && 0 != rhs.value.i &&
                     (result / value.i != rhs.value.i ||
                      result / rhs.value.i != value.i)
                    )
                 {
                     valid = error_integer_overflow;
                 }
                 else {
                     value.i = result;
                 }
             }
             break;
             
         case is_uint:
             {
                 unsigned long result = value.ui * rhs.value.ui; 
                 if (0 != value.ui && 0 != rhs.value.ui &&
                     (result / value.ui != rhs.value.ui ||
                      result / rhs.value.ui != value.ui)
                    )
                 {
                     valid = error_integer_overflow;
                 }
                 else {
                     value.ui = result;
                     type = is_uint; 
                 }
             }
             break;
         }
         break;
         
     case is_uint:
         {
             unsigned long rhs_val = as_ulong(rhs);
             unsigned long result = value.ui * rhs_val; 
             if (0 != value.ui && 0 != rhs_val &&
                 (result / value.ui != rhs_val ||
                   result / rhs_val != value.ui)
                 )
             {
                 valid = error_integer_overflow;
             }
             else {
                 value.ui = result;
                 type = is_uint; 
             }
         }
         break;
         
     case is_bool:
         switch (rhs.type) {
         case is_int:
             value.i = (value.b ? 1 : 0) * rhs.value.i; 
             type = is_int; 
             break;
             
         case is_uint:
             value.ui = (value.b ? 1 : 0) * rhs.value.ui; 
             type = is_uint; 
             break;
             
         case is_bool:
             value.b = 0 != ((value.b ? 1 : 0) * (rhs.value.b ? 1 : 0));
             break;
         }
     }
     valid = (value_error)(valid | rhs.valid);
     return *this;
 }
    closure_value &operator-= (closure_value const &rhs)
    {
        switch (type) {
        case is_int:
            switch(rhs.type) {
            case is_bool:
                {
                    long result = value.i - as_long(rhs); 
                    if (rhs.value.i > 0L && result > value.i || 
                        rhs.value.i < 0L && result < value.i)
                    {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.i = result;
                    }
                }
                break;

            case is_int:
                {
                    long result = value.i - rhs.value.i;
                    if (rhs.value.i > 0L && result > value.i || 
                        rhs.value.i < 0L && result < value.i)
                    {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.i = result;
                    }
                }
                break;
                
            case is_uint:
                {
                    unsigned long result = value.ui - rhs.value.ui; 
                    if (result > value.ui) {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.ui = result;
                        type = is_uint; 
                    }
                }
                break;
            }
            break;
            
        case is_uint:
            switch(rhs.type) {
            case is_bool:
                {
                    unsigned long result = value.ui - as_ulong(rhs); 
                    if (result > value.ui)
                    {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.ui = result;
                    }
                }
                break;

            case is_int:
                {
                    unsigned long result = value.ui - rhs.value.i;
                    if (rhs.value.i > 0L && result > value.ui || 
                        rhs.value.i < 0L && result < value.ui)
                    {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.ui = result;
                    }
                }
                break;
                
            case is_uint:
                {
                    unsigned long result = value.ui - rhs.value.ui; 
                    if (result > value.ui) {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.ui = result;
                    }
                }
                break;
            }
            break;

        case is_bool:   
            value.i = value.b - as_bool(rhs);
            type = is_int;
        }
        valid = (value_error)(valid | rhs.valid);
        return *this;
    }
// arithmetics
    closure_value &operator+= (closure_value const &rhs)
    {
        switch (type) {
        case is_int:    
            switch(rhs.type) {
            case is_bool:
                {
                    int_literal_type result = value.i + as_long(rhs); 
                    if ((rhs.value.i > 0L && value.i > result) || 
                        (rhs.value.i < 0L && value.i < result))
                    {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.i = result;
                    }
                }
                break;
                
            case is_int:
                {
                    int_literal_type result = value.i + rhs.value.i;
                    if ((rhs.value.i > 0L && value.i > result) || 
                        (rhs.value.i < 0L && value.i < result))
                    {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.i = result;
                    }
                }
                break;
                
            case is_uint:
                {
                    uint_literal_type result = value.ui + rhs.value.ui; 
                    if (result < value.ui) {
                        valid = error_integer_overflow;
                    }
                    else {
                        value.ui = result;
                        type = is_uint; 
                    }
                }
                break;
            }
            break;
            
        case is_uint:
            {
                uint_literal_type result = value.ui + as_ulong(rhs); 
                if (result < value.ui) {
                    valid = error_integer_overflow;
                }
                else {
                    value.ui = result;
                }
            }
            break;
            
        case is_bool:   
            value.i = value.b + as_bool(rhs);
            type = is_int;
        }
        valid = (value_error)(valid | rhs.valid);
        return *this;
    }