/* call-seq:
  * BigDecimal.mode(mode, value)
  *
  * Controls handling of arithmetic exceptions and rounding. If no value
  * is supplied, the current value is returned.
  *
  * Six values of the mode parameter control the handling of arithmetic
  * exceptions:
  *
  * BigDecimal::EXCEPTION_NaN
  * BigDecimal::EXCEPTION_INFINITY
  * BigDecimal::EXCEPTION_UNDERFLOW
  * BigDecimal::EXCEPTION_OVERFLOW
  * BigDecimal::EXCEPTION_ZERODIVIDE
  * BigDecimal::EXCEPTION_ALL
  *
  * For each mode parameter above, if the value set is false, computation 
  * continues after an arithmetic exception of the appropriate type. 
  * When computation continues, results are as follows:
  *
  * EXCEPTION_NaN:: NaN
  * EXCEPTION_INFINITY:: +infinity or -infinity
  * EXCEPTION_UNDERFLOW:: 0
  * EXCEPTION_OVERFLOW:: +infinity or -infinity
  * EXCEPTION_ZERODIVIDE:: +infinity or -infinity
  *
  * One value of the mode parameter controls the rounding of numeric values:
  * BigDecimal::ROUND_MODE. The values it can take are:
  *
  * ROUND_UP:: round away from zero
  * ROUND_DOWN:: round towards zero (truncate)
  * ROUND_HALF_UP:: round up if the appropriate digit >= 5, otherwise truncate (default)
  * ROUND_HALF_DOWN:: round up if the appropriate digit >= 6, otherwise truncate
  * ROUND_HALF_EVEN:: round towards the even neighbor (Banker's rounding)
  * ROUND_CEILING:: round towards positive infinity (ceil)
  * ROUND_FLOOR:: round towards negative infinity (floor)
  *
  */
static VALUE
BigDecimal_mode(int argc, VALUE *argv, VALUE self)
{
    VALUE which;
    VALUE val;
    unsigned long f,fo;
 
    if(rb_scan_args(argc,argv,"11",&which,&val)==1) val = Qnil;

    Check_Type(which, T_FIXNUM);
    f = (unsigned long)FIX2INT(which);

    if(f&VP_EXCEPTION_ALL) {
        /* Exception mode setting */
        fo = VpGetException();
        if(val==Qnil) return INT2FIX(fo);
        if(val!=Qfalse && val!=Qtrue) {
            rb_raise(rb_eTypeError, "second argument must be true or false");
            return Qnil; /* Not reached */
        }
        if(f&VP_EXCEPTION_INFINITY) {
            VpSetException((unsigned short)((val==Qtrue)?(fo|VP_EXCEPTION_INFINITY):
                           (fo&(~VP_EXCEPTION_INFINITY))));
        }
        if(f&VP_EXCEPTION_NaN) {
            VpSetException((unsigned short)((val==Qtrue)?(fo|VP_EXCEPTION_NaN):
                           (fo&(~VP_EXCEPTION_NaN))));
        }
        fo = VpGetException();
        return INT2FIX(fo);
    }
    if(VP_ROUND_MODE==f) {
        /* Rounding mode setting */
        fo = VpGetRoundMode();
        if(val==Qnil) return INT2FIX(fo);
        Check_Type(val, T_FIXNUM);
        if(!VpIsRoundMode(FIX2INT(val))) {
            rb_raise(rb_eTypeError, "invalid rounding mode");
            return Qnil;
        }
        fo = VpSetRoundMode((unsigned long)FIX2INT(val));
        return INT2FIX(fo);
    }
    rb_raise(rb_eTypeError, "first argument for BigDecimal#mode invalid");
    return Qnil;
}