Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: EPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Eclipse Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.eclipse.org/legal/epl-v10.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright (C) 2006 Ola Bini <ola@ologix.com> Copyright (C) 2009 Joseph LaFata <joe@quibb.org> Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the EPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the EPL, the GPL or the LGPL. END LICENSE BLOCK ***
  
  package org.jruby.ext.bigdecimal;
  
  import java.util.List;
  import org.jruby.Ruby;
  

Author(s):
Ola Bini
  
  @JRubyClass(name="BigDecimal", parent="Numeric")
  public class RubyBigDecimal extends RubyNumeric {
      private static final ObjectAllocator BIGDECIMAL_ALLOCATOR = new ObjectAllocator() {
          public IRubyObject allocate(Ruby runtimeRubyClass klass) {
              return new RubyBigDecimal(runtimeklass);
          }
      };
  
      @JRubyConstant
      public final static int ROUND_DOWN = .;
      @JRubyConstant
      public final static int ROUND_CEILING = .;
      @JRubyConstant
      public final static int ROUND_UP = .;
      @JRubyConstant
      public final static int ROUND_HALF_DOWN = .;
      @JRubyConstant
      public final static int ROUND_HALF_EVEN = .;
      @JRubyConstant
      public final static int ROUND_HALF_UP = .;
      @JRubyConstant
      public final static int ROUND_FLOOR = .;
  
      @JRubyConstant
      public final static int SIGN_POSITIVE_INFINITE = 3;
      @JRubyConstant
      public final static int EXCEPTION_OVERFLOW = 8;
      @JRubyConstant
      public final static int SIGN_POSITIVE_ZERO = 1;
      @JRubyConstant
      public final static int EXCEPTION_ALL = 255;
     @JRubyConstant
     public final static int SIGN_NEGATIVE_FINITE = -2;
     @JRubyConstant
     public final static int EXCEPTION_UNDERFLOW = 4;
     @JRubyConstant
     public final static int SIGN_NaN = 0;
     @JRubyConstant
     public final static int BASE = 10000;
     @JRubyConstant
     public final static int ROUND_MODE = 256;
     @JRubyConstant
     public final static int SIGN_POSITIVE_FINITE = 2;
     @JRubyConstant
     public final static int EXCEPTION_INFINITY = 1;
     @JRubyConstant
     public final static int SIGN_NEGATIVE_INFINITE = -3;
     @JRubyConstant
     public final static int EXCEPTION_ZERODIVIDE = 1;
     @JRubyConstant
     public final static int SIGN_NEGATIVE_ZERO = -1;
     @JRubyConstant
     public final static int EXCEPTION_NaN = 2;
     
     // Static constants
     private static final BigDecimal TWO = new BigDecimal(2);
     private static final double SQRT_10 = 3.162277660168379332;
     
     public static RubyClass createBigDecimal(Ruby runtime) {
         RubyClass bigDecimal = runtime.defineClass("BigDecimal"runtime.getNumeric(), );
 
         runtime.getKernel().defineAnnotatedMethods(BigDecimalKernelMethods.class);
 
         bigDecimal.setInternalModuleVariable("vpPrecLimit", RubyFixnum.zero(runtime));
         bigDecimal.setInternalModuleVariable("vpExceptionMode", RubyFixnum.zero(runtime));
         bigDecimal.setInternalModuleVariable("vpRoundingMode"runtime.newFixnum());
         
         bigDecimal.defineAnnotatedMethods(RubyBigDecimal.class);
         bigDecimal.defineAnnotatedConstants(RubyBigDecimal.class);
 
         if (runtime.is1_9()) {
             RubyModule bigMath = runtime.defineModule("BigMath");
             // TODO: BigMath.exp and BigMath.pow in native code
 
             bigDecimal.defineConstant("NAN"newNaN(runtime));
             bigDecimal.defineConstant("INFINITY"newInfinity(runtime, 1));
         }
 
         return bigDecimal;
     }
 
     private final boolean isNaN;
     private final int infinitySign;
     private final int zeroSign;
     private BigDecimal value;
 
     public BigDecimal getValue() {
         return ;
     }
 
     public RubyBigDecimal(Ruby runtimeRubyClass klass) {
         super(runtimeklass);
         this. = false;
         this. = 0;
         this. = 0;
     }
 
     public RubyBigDecimal(Ruby runtimeBigDecimal value) {
         super(runtimeruntime.getClass("BigDecimal"));
         this. = false;
         this. = 0;
         this. = 0;
         this. = value;
     }
 
     public RubyBigDecimal(Ruby runtimeRubyClass klassBigDecimal value) {
         super(runtimeklass);
         this. = false;
         this. = 0;
         this. = 0;
         this. = value;
     }
 
     public RubyBigDecimal(Ruby runtimeBigDecimal valueint infinitySign) {
         super(runtimeruntime.getClass("BigDecimal"));
         this. = false;
         this. = infinitySign;
         this. = 0;
         this. = value;
     }
 
     public RubyBigDecimal(Ruby runtimeBigDecimal valueint infinitySignint zeroSign) {
         super(runtimeruntime.getClass("BigDecimal"));
         this. = false;
         this. = infinitySign;
         this. = zeroSign;
         this. = value;
     }
 
     public RubyBigDecimal(Ruby runtimeBigDecimal valueboolean isNan) {
         super(runtimeruntime.getClass("BigDecimal"));
         this. = isNan;
         this. = 0;
         this. = 0;
         this. = value;
     }
 
     public RubyBigDecimal(Ruby runtimeRubyBigDecimal rbd) {
         this(runtimeruntime.getClass("BigDecimal"), rbd);
     }
 
     public RubyBigDecimal(Ruby runtimeRubyClass klassRubyBigDecimal rbd) {
         super(runtimeklass);
         this. = rbd.isNaN;
         this. = rbd.infinitySign;
         this. = rbd.zeroSign;
         this. = rbd.value;
     }
     
     public static class BigDecimalKernelMethods {
         @JRubyMethod(name = "BigDecimal", rest = true, module = true, visibility = .)
         public static IRubyObject newBigDecimal(IRubyObject recvIRubyObject[] args) {
             return RubyBigDecimal.newBigDecimal(recvargs.);
         }
     }
 
     public static RubyBigDecimal newBigDecimal(IRubyObject recvIRubyObject[] argsBlock unusedBlock) {
         return newInstance(recv.getRuntime().getClass("BigDecimal"), args);
     }
 
     @JRubyMethod(name = "ver", meta = true)
     public static IRubyObject ver(IRubyObject recv) {
         return recv.getRuntime().newString("1.0.1");
     }
 
     @JRubyMethod(name = "_dump", optional = 1)
     public IRubyObject dump(IRubyObject[] argsBlock unusedBlock) {
         RubyString precision = RubyString.newUnicodeString(getRuntime(), "0:");
 
         return precision.append(asString());
     }
         
     @JRubyMethod(name = "_load", required = 1, meta = true)
     public static RubyBigDecimal load(IRubyObject recvIRubyObject fromBlock block) {
         RubyBigDecimal rubyBigDecimal = (RubyBigDecimal) (((RubyClass)recv).allocate());
         String precisionAndValue = from.convertToString().asJavaString();
         String value = precisionAndValue.substring(precisionAndValue.indexOf(":")+1);
         rubyBigDecimal.value = new BigDecimal(value);
         return rubyBigDecimal;
     }
 
     @JRubyMethod(name = "double_fig", meta = true)
     public static IRubyObject double_fig(IRubyObject recv) {
         return recv.getRuntime().newFixnum(20);
     }
     
     @JRubyMethod(name = "limit", optional = 1, meta = true)
     public static IRubyObject limit(IRubyObject recvIRubyObject[] args) {
         Ruby runtime = recv.getRuntime();
         RubyModule c = (RubyModule)recv;
         IRubyObject nCur = c.searchInternalModuleVariable("vpPrecLimit");
 
         if (args.length > 0) {
             IRubyObject arg = args[0];
             if (!arg.isNil()) {
                 if (!(arg instanceof RubyFixnum)) {
                     throw runtime.newTypeError(argruntime.getFixnum());
                 }
                 if (0 > ((RubyFixnum)arg).getLongValue()) {
                     throw runtime.newArgumentError("argument must be positive");
                 }
                 c.setInternalModuleVariable("vpPrecLimit"arg);
             }
         }
 
         return nCur;
     }
 
     @JRubyMethod(name = "save_limit", meta = true)
     public static IRubyObject save_limit(ThreadContext contextIRubyObject recvBlock block) {
         RubyModule c = (RubyModule)recv;
         IRubyObject nCur = c.searchInternalModuleVariable("vpPrecLimit");
         IRubyObject ret;
 
         try {
             ret = block.yieldSpecific(context);
         } finally {
             c.setInternalModuleVariable("vpPrecLimit"nCur);
         }
 
         return ret;
     }
 
     @JRubyMethod(name = "save_exception_mode", meta = true)
     public static IRubyObject save_exception_mode(ThreadContext contextIRubyObject recvBlock block) {
         RubyModule c = (RubyModule)recv;
         IRubyObject nCur = c.searchInternalModuleVariable("vpExceptionMode");
         IRubyObject ret;
 
         try {
             ret = block.yieldSpecific(context);
         } finally {
             c.setInternalModuleVariable("vpExceptionMode"nCur);
         }
 
         return ret;
     }
 
     @JRubyMethod(name = "save_rounding_mode", meta = true)
     public static IRubyObject save_rounding_mode(ThreadContext contextIRubyObject recvBlock block) {
         RubyModule c = (RubyModule)recv;
         IRubyObject nCur = c.searchInternalModuleVariable("vpRoundingMode");
         IRubyObject ret;
 
         try {
             ret = block.yieldSpecific(context);
         } finally {
             c.setInternalModuleVariable("vpRoundingMode"nCur);
         }
 
         return ret;
     }
 
     @JRubyMethod(name = "mode", required = 1, optional = 1, meta = true)
     public static IRubyObject mode(ThreadContext contextIRubyObject recvIRubyObject[] args) {
         // FIXME: I doubt any of the constants referenced in this method
         // are ever redefined -- should compare to the known values, rather
         // than do an expensive constant lookup.
         Ruby runtime = recv.getRuntime();
         RubyClass clazz = runtime.getClass("BigDecimal");
         RubyModule c = (RubyModule)recv;
         
         args = Arity.scanArgs(runtimeargs, 1, 1);
         
         IRubyObject mode = args[0];
         IRubyObject value = args[1];
         
         if (!(mode instanceof RubyFixnum)) {
             throw runtime.newTypeError("wrong argument type " + mode.getMetaClass() + " (expected Fixnum)");
         }
         
         long longMode = ((RubyFixnum)mode).getLongValue();
         long EXCEPTION_ALL = ((RubyFixnum)clazz.getConstant("EXCEPTION_ALL")).getLongValue();
         if ((longMode & EXCEPTION_ALL) != 0) {     
             if (value.isNil()) {
                 return c.searchInternalModuleVariable("vpExceptionMode");
             }
             if (!(value.isNil()) && !(value instanceof RubyBoolean)) {
                 throw runtime.newTypeError("second argument must be true or false");
             }
 
             RubyFixnum currentExceptionMode = (RubyFixnum)c.searchInternalModuleVariable("vpExceptionMode");
             RubyFixnum newExceptionMode = new RubyFixnum(runtimecurrentExceptionMode.getLongValue());
             
             RubyFixnum EXCEPTION_INFINITY = (RubyFixnum)clazz.getConstant("EXCEPTION_INFINITY");
             if ((longMode & EXCEPTION_INFINITY.getLongValue()) != 0) {
                 newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context"|"EXCEPTION_INFINITY)
                         : (RubyFixnum)currentExceptionMode.callCoerced(context"&"new RubyFixnum(runtime, ~(EXCEPTION_INFINITY).getLongValue()));
             }
             
             RubyFixnum EXCEPTION_NaN = (RubyFixnum)clazz.getConstant("EXCEPTION_NaN");
             if ((longMode & EXCEPTION_NaN.getLongValue()) != 0) {
                 newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context"|"EXCEPTION_NaN)
                         : (RubyFixnum)currentExceptionMode.callCoerced(context"&"new RubyFixnum(runtime, ~(EXCEPTION_NaN).getLongValue()));
             }
             
             RubyFixnum EXCEPTION_UNDERFLOW = (RubyFixnum)clazz.getConstant("EXCEPTION_UNDERFLOW");
             if ((longMode & EXCEPTION_UNDERFLOW.getLongValue()) != 0) {
                 newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context"|"EXCEPTION_UNDERFLOW)
                         : (RubyFixnum)currentExceptionMode.callCoerced(context"&"new RubyFixnum(runtime, ~(EXCEPTION_UNDERFLOW).getLongValue()));
             }
             RubyFixnum EXCEPTION_OVERFLOW = (RubyFixnum)clazz.getConstant("EXCEPTION_OVERFLOW");
             if ((longMode & EXCEPTION_OVERFLOW.getLongValue()) != 0) {
                 newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context"|"EXCEPTION_OVERFLOW)
                         : (RubyFixnum)currentExceptionMode.callCoerced(context"&"new RubyFixnum(runtime, ~(EXCEPTION_OVERFLOW).getLongValue()));
             }
             c.setInternalModuleVariable("vpExceptionMode"newExceptionMode);
             return newExceptionMode;
         }
         
         long ROUND_MODE = ((RubyFixnum)clazz.getConstant("ROUND_MODE")).getLongValue();
         if (longMode == ROUND_MODE) {
             if (value.isNil()) {
                 return c.searchInternalModuleVariable("vpRoundingMode");
             }
             if (!(value instanceof RubyFixnum)) {
                 throw runtime.newTypeError("wrong argument type " + mode.getMetaClass() + " (expected Fixnum)");
             }
             
             RubyFixnum roundingMode = (RubyFixnum)value;
             if (roundingMode == clazz.getConstant("ROUND_UP") ||
                     roundingMode == clazz.getConstant("ROUND_DOWN") ||
                     roundingMode == clazz.getConstant("ROUND_FLOOR") ||
                     roundingMode == clazz.getConstant("ROUND_CEILING") ||
                     roundingMode == clazz.getConstant("ROUND_HALF_UP") ||
                     roundingMode == clazz.getConstant("ROUND_HALF_DOWN") ||
                     roundingMode == clazz.getConstant("ROUND_HALF_EVEN")) {
                 c.setInternalModuleVariable("vpRoundingMode"roundingMode);
             } else {
                 throw runtime.newTypeError("invalid rounding mode");
             }
             return c.searchInternalModuleVariable("vpRoundingMode");
         }
         throw runtime.newTypeError("first argument for BigDecimal#mode invalid");
     }
 
     private static RoundingMode getRoundingMode(Ruby runtime) {
         RubyFixnum roundingMode = (RubyFixnum)runtime.getClass("BigDecimal")
                 .searchInternalModuleVariable("vpRoundingMode");
         return RoundingMode.valueOf((int)roundingMode.getLongValue());
     }
 
     private static boolean isNaNExceptionMode(Ruby runtime) {
         RubyFixnum currentExceptionMode = (RubyFixnum)runtime.getClass("BigDecimal")
                 .searchInternalModuleVariable("vpExceptionMode");
         RubyFixnum EXCEPTION_NaN = (RubyFixnum)runtime.getClass("BigDecimal")
                 .getConstant("EXCEPTION_NaN");
         return (currentExceptionMode.getLongValue() & EXCEPTION_NaN.getLongValue()) != 0;
     }
 
     private static boolean isInfinityExceptionMode(Ruby runtime) {
         RubyFixnum currentExceptionMode = (RubyFixnum)runtime.getClass("BigDecimal")
                 .searchInternalModuleVariable("vpExceptionMode");
         RubyFixnum EXCEPTION_INFINITY = (RubyFixnum)runtime.getClass("BigDecimal")
                 .getConstant("EXCEPTION_INFINITY");
         return (currentExceptionMode.getLongValue() & EXCEPTION_INFINITY.getLongValue()) != 0;
     }
     
     private static boolean isOverflowExceptionMode(Ruby runtime) {
         RubyFixnum currentExceptionMode = (RubyFixnum)runtime.getClass("BigDecimal")
                 .searchInternalModuleVariable("vpExceptionMode");
         RubyFixnum EXCEPTION_OVERFLOW = (RubyFixnum)runtime.getClass("BigDecimal")
                 .getConstant("EXCEPTION_OVERFLOW");
         return (currentExceptionMode.getLongValue() & EXCEPTION_OVERFLOW.getLongValue()) != 0;
     }
     
     private static RubyBigDecimal cannotBeCoerced(ThreadContext contextIRubyObject vboolean must) {
         if (must) {
             String err;
             
             if (v.isImmediate()) {
                 err = RubyObject.inspect(contextv).toString();
             } else {
                 err = v.getMetaClass().getBaseName();
             }
             
             throw context.runtime.newTypeError(err + " can't be coerced into BigDecimal");
         }
         
         return null;
     }
     
     private static RubyBigDecimal unableToCoerceWithoutPrec(ThreadContext contextIRubyObject vboolean must) {
         if (must) {
             throw context.runtime.newArgumentError(v.getMetaClass().getBaseName() + " can't be coerced into BigDecimal without a precision");
         }
         
         return null;
     }
     
     private static RubyBigDecimal getVpValue19(ThreadContext contextIRubyObject vboolean must) {
         long precision;
         if (v instanceof RubyFloat) {
             precision = 0;
         } else if (v instanceof RubyRational) {
             precision = 0;
         } else {
             precision = -1;
         }
         return getVpValueWithPrec19(contextvprecisionmust);
     }
     
     private static IRubyObject getVpRubyObjectWithPrec19Inner(ThreadContext contextRubyRational rlong precisionboolean must) {
         long numerator = RubyNumeric.num2long(r.numerator(context));
         long denominator = RubyNumeric.num2long(r.denominator(context));
             
         return new RubyBigDecimal(context.runtime
                 BigDecimal.valueOf(numerator).divide(BigDecimal.valueOf(denominator), getRoundingMode(context.runtime)));
     }
     
     private static RubyBigDecimal getVpValueWithPrec19(ThreadContext contextIRubyObject valuelong precisionboolean must) {
         while (true) {
             if (value instanceof RubyFloat) {
                 if (precision > .cannotBeCoerced(contextvaluemust);
             
                 RubyFloat f = (RubyFloat)value;
                 value = new RubyBigDecimal(context.runtime, BigDecimal.valueOf(f.getDoubleValue()));
                 continue;
             } else if (value instanceof RubyRational) {
                 if (precision < 0) return unableToCoerceWithoutPrec(contextvaluemust);
                 
                 value = getVpRubyObjectWithPrec19Inner(context, (RubyRationalvalueprecisionmust);
                 continue;
             } else if (value instanceof RubyBigDecimal) {
                 return (RubyBigDecimalvalue;
             } else if (value instanceof RubyFixnum || value instanceof RubyBignum) {
                 String s = value.toString();
                 return newInstance(value.getRuntime().getClass("BigDecimal"), new IRubyObject[]{value.getRuntime().newString(s)});            
             } 
             
             return cannotBeCoerced(contextvaluemust);            
         }
     }
 
     private static RubyBigDecimal getVpValue(IRubyObject vboolean must) {
         if (v instanceof RubyBigDecimal) {
             return (RubyBigDecimal)v;
         } else if (v instanceof RubyFixnum || v instanceof RubyBignum) {
             String s = v.toString();
             return newInstance(v.getRuntime().getClass("BigDecimal"), new IRubyObject[]{v.getRuntime().newString(s)});
         }
 
         return cannotBeCoerced(v.getRuntime().getCurrentContext(), vmust);
     }
 
     @JRubyMethod(name = "induced_from", required = 1, meta = true)
     public static IRubyObject induced_from(IRubyObject recvIRubyObject arg) {
         return getVpValue(argtrue);
     }
 
     private final static Pattern INFINITY_PATTERN = Pattern.compile("^([+-])?Infinity$");
     private final static Pattern NUMBER_PATTERN
             = Pattern.compile("^([+-]?\\d*\\.?\\d*([eE][+-]?)?\\d*).*");
     
     @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
     public static RubyBigDecimal newInstance(IRubyObject recvIRubyObject[] args) {
         BigDecimal decimal;
         Ruby runtime = recv.getRuntime();
         ThreadContext ctx = runtime.getCurrentContext();
 
         if (args.length == 0) { 
             decimal = new BigDecimal(0);
         } else {
             MathContext context = .;
 
             if (args.length == 2) {
                 int digits = (int)args[1].convertToInteger().getLongValue();
                 if (digits < 0) {
                     throw runtime.newArgumentError("argument must be positive");
                 }
                 context = new MathContext(digits);
             }
 
             if (runtime.is1_9()) {
                 if (args[0] instanceof RubyBigDecimal) {
                     return new RubyBigDecimal(runtime, (RubyClass)recv, ((RubyBigDecimal)args[0]));
                 } else if (args[0] instanceof RubyFloat || args[0] instanceof RubyRational) {
                     if (args.length != 2) {
                         // float input must be accompanied by precision
                         throw runtime.newArgumentError("can't omit precision for a rational");
                     }
 
                     if (args[0] instanceof RubyFloat) {
                         // precision can be no more than float digits
                         if (context.getPrecision() > . + 1) {
                             throw runtime.newArgumentError("precision too large");
                         }
                         return new RubyBigDecimal(runtime, (RubyClass)recvnew BigDecimal(((RubyFloat)args[0]).getDoubleValue(), context));
                     } else {
                         RubyRational rat = (RubyRational)args[0];
 
                         BigDecimal num = new BigDecimal(rat.numerator(ctx).convertToInteger().getLongValue());
                         BigDecimal den = new BigDecimal(rat.denominator(ctx).convertToInteger().getLongValue());
 
                         BigDecimal value = num.divide(dencontext);
 
                         return new RubyBigDecimal(runtimevalue);
                     }
                 } else if (args[0] instanceof RubyFixnum) {
                     return new RubyBigDecimal(runtime, (RubyClass)recvnew BigDecimal(((RubyFixnum)args[0]).getLongValue(), context));
                 } else if (args[0] instanceof RubyBignum) {
                     return new RubyBigDecimal(runtime, (RubyClass)recvnew BigDecimal(((RubyBignum)args[0]).getBigIntegerValue(), context));
                 }
                 // fall through to String coercion below
             }
 
             String strValue = args[0].convertToString().toString();
             strValue = strValue.trim();
             if ("NaN".equals(strValue)) {
                 return newNaN(runtime);
             }
 
             Matcher m = .matcher(strValue);
             if (m.matches()) {
                 int sign = 1;
                 String signGroup = m.group(1);
                 if ("-".equals(signGroup)) {
                     sign = -1;
                 }
                 return newInfinity(runtimesign);
             }
 
             // Clean-up string representation so that it could be understood
             // by Java's BigDecimal. Not terribly efficient for now.
             // 1. MRI allows d and D as exponent separators
             strValue = strValue.replaceFirst("[dD]""E");
             // 2. MRI allows underscores anywhere
             strValue = strValue.replaceAll("_""");
             // 3. MRI ignores the trailing junk
             strValue = .matcher(strValue).replaceFirst("$1");
 
             try {
                 decimal = new BigDecimal(strValuecontext);
             } catch(NumberFormatException e) {
                 if (isOverflowExceptionMode(runtime)) {
                     throw runtime.newFloatDomainError("exponent overflow");
                 }
                 decimal = new BigDecimal(0);
             }
             if (decimal.signum() == 0) {
                 // MRI behavior: -0 and +0 are two different things
                 if (strValue.matches("^\\s*-.*")) {
                     return newZero(runtime, -1);
                 } else {
                     return newZero(runtime, 1);
                 }
             }
         }
         return new RubyBigDecimal(runtime, (RubyClass)recvdecimal);
     }
 
     private static RubyBigDecimal newZero(Ruby runtimeint sign) {
         int zeroSign;
         if (sign < 0) {
             zeroSign = -1;
         } else {
             zeroSign = 1;
         }
         RubyBigDecimal rbd = new RubyBigDecimal(runtime., 0, zeroSign);
         return rbd;
     }
 
     private static RubyBigDecimal newNaN(Ruby runtime) {
         if (isNaNExceptionMode(runtime)) throw runtime.newFloatDomainError("Computation results to 'NaN'(Not a Number)");
 
         RubyBigDecimal rbd = new RubyBigDecimal(runtime.true);
         return rbd;
     }
     
     private static RubyBigDecimal newInfinity(Ruby runtimeint sign) {
         int infinitySign;
         if (sign < 0) {
             infinitySign = -1;
         } else {
             infinitySign = 1;
         }
         RubyBigDecimal rbd =  new RubyBigDecimal(runtime.infinitySign);
         if (isInfinityExceptionMode(runtime)) throw runtime.newFloatDomainError("Computation results to 'Infinity'");
 
         return rbd;
     }
 
     private RubyBigDecimal setResult() {
         return setResult(0);
     }
 
     private RubyBigDecimal setResult(int scale) {
         int prec = RubyFixnum.fix2int(getRuntime().getClass("BigDecimal").searchInternalModuleVariable("vpPrecLimit"));
         int prec2 = Math.max(scaleprec);
         if (prec2 > 0 && this..scale() > (prec2-getExponent())) {
             this. = this..setScale(prec2-getExponent(), .);
         }
         return this;
     }
     
     @Override
     @JRubyMethod(name = "hash")
     public RubyFixnum hash() {
         return getRuntime().newFixnum(.stripTrailingZeros().hashCode());
     }
 
     @JRubyMethod(name = {"%""modulo"}, required = 1, compat = .)
     public IRubyObject op_mod(ThreadContext contextIRubyObject arg) {
         // TODO: full-precision remainder is 1000x slower than MRI!
         Ruby runtime = context.runtime;
         if (isInfinity() || isNaN()) {
             return newNaN(runtime);
         }
         RubyBigDecimal val = getVpValue(argfalse);
         if (val == null) {
             return callCoerced(context"%"argtrue);
         }
         if (val.isInfinity() || val.isNaN() || val.isZero()) {
             return newNaN(runtime);
         }
 
         // Java and MRI definitions of modulo are different.
         BigDecimal modulo = .remainder(val.value);
         if (modulo.signum() * val.value.signum() < 0) {
             modulo = modulo.add(val.value);
         }
 
         return new RubyBigDecimal(runtimemodulo).setResult();
     }
 
     @JRubyMethod(name = {"%""modulo"}, required = 1, compat = .)
     public IRubyObject op_mod19(ThreadContext contextIRubyObject other) {
         // TODO: full-precision divmod is 1000x slower than MRI!
         Ruby runtime = context.runtime;
         RubyBigDecimal val = getVpValue19(contextotherfalse);
         if (val == null) {
             return callCoerced(context"%"othertrue);
         }
         if (isNaN() || val.isNaN() || (isInfinity() && val.isInfinity())) {
             return newNaN(runtime);
         }
         if (val.isZero()) {
             throw context.runtime.newZeroDivisionError();
         }
         if (isInfinity()) {
             return newNaN(runtime);
         }
         if (val.isInfinity()) {
             return this;
         }
         if (isZero()) {
             return newZero(runtime.signum());
         }
 
         // Java and MRI definitions of modulo are different.
         BigDecimal modulo = .remainder(val.value);
         if (modulo.signum() * val.value.signum() < 0) {
             modulo = modulo.add(val.value);
         }
 
         return new RubyBigDecimal(runtimemodulo).setResult();
     }
 
     @Override
     @JRubyMethod(name = "remainder", required = 1, compat = .)
     public IRubyObject remainder(ThreadContext contextIRubyObject arg) {
         RubyBigDecimal val = getVpValue(argfalse);
         return remainderInternal(contextvalarg);
     }
 
     @JRubyMethod(name = "remainder", required = 1, compat = .)
     public IRubyObject remainder19(ThreadContext contextIRubyObject arg) {
         RubyBigDecimal val = getVpValue19(contextargfalse);
         return remainderInternal(contextvalarg);
     }
 
     private IRubyObject remainderInternal(ThreadContext contextRubyBigDecimal valIRubyObject arg) {
      // TODO: full-precision remainder is 1000x slower than MRI!
         Ruby runtime = context.runtime;
         if (isInfinity() || isNaN()) {
             return newNaN(runtime);
         }
 
         if (val == null) {
             return callCoerced(context"remainder"argtrue);
         }
         if (val.isInfinity() || val.isNaN() || val.isZero()) {
             return newNaN(runtime);
         }
 
         // Java and MRI definitions of remainder are the same.
         return new RubyBigDecimal(runtime.remainder(val.value)).setResult();
     }
 
     @JRubyMethod(name = "*", required = 1, compat = .)
     public IRubyObject op_mul(ThreadContext contextIRubyObject arg) {
         return mult2(contextarggetRuntime().getClass("BigDecimal")
                 .searchInternalModuleVariable("vpPrecLimit"));
     }
 
     @JRubyMethod(name = "*", required = 1, compat = .)
     public IRubyObject op_mul19(ThreadContext contextIRubyObject arg) {
         return mult219(contextarggetRuntime().getClass("BigDecimal")
                 .searchInternalModuleVariable("vpPrecLimit"));
     }
 
     @JRubyMethod(name = "mult", required = 2, compat = .)
     public IRubyObject mult2(ThreadContext contextIRubyObject bIRubyObject n) {
         RubyBigDecimal val = getVpValue(bfalse);
         return multInternal(contextvalbn);
     }
 
     @JRubyMethod(name = "mult", required = 2, compat = .)
     public IRubyObject mult219(ThreadContext contextIRubyObject bIRubyObject n) {
         RubyBigDecimal val = getVpValue19(contextbfalse);
         return multInternal(contextvalbn);
     }
 
     private IRubyObject multInternal(ThreadContext contextRubyBigDecimal valIRubyObject bIRubyObject n) {
         Ruby runtime = context.runtime;
 
         if (val == null) {
             // TODO: what about n arg?
             return callCoerced(context"*"b);
         }
 
         int digits = RubyNumeric.fix2int(n);
 
         if (isNaN() || val.isNaN()) {
             return newNaN(runtime);
         }
 
         if  ((isInfinity() && val.isZero()) || (isZero() && val.isInfinity())) {
             return newNaN(runtime);
         }
 
         if (isZero() || val.isZero()) {
             int sign1 = isZero()?  : .signum();
             int sign2 = val.isZero() ?  val.zeroSign : val.value.signum();
             return newZero(runtimesign1 * sign2);
         }
 
         if (isInfinity() || val.isInfinity()) {
             int sign1 = isInfinity() ?  : .signum();
             int sign2 = val.isInfinity() ? val.infinitySign : val.value.signum();
             return newInfinity(runtimesign1 * sign2);
         }
 
         BigDecimal res = .multiply(val.value);
         if (res.precision() > digits) {
             // TODO: rounding mode should not be hard-coded. See #mode.
             res = res.round(new MathContext(digits,  .));
         }
         return new RubyBigDecimal(runtimeres).setResult();
     }
 
     @JRubyMethod(name = {"**""power"}, required = 1, compat = .)
     public IRubyObject op_pow(IRubyObject arg) {
         if (!(arg instanceof RubyFixnum)) {
             throw getRuntime().newTypeError("wrong argument type " + arg.getMetaClass() + " (expected Fixnum)");
         }
 
         if (isNaN() || isInfinity()) {
             return newNaN(getRuntime());
         }
 
         int times = RubyNumeric.fix2int(arg.convertToInteger());
 
         if (times < 0) {
             if (isZero()) {
                 return newInfinity(getRuntime(), .signum());
             }
 
             // Note: MRI has a very non-trivial way of calculating the precision,
             // so we use very simple approximation here:
             int precision = (-times + 4) * (getAllDigits().length() + 4);
 
             return new RubyBigDecimal(getRuntime(),
                     .pow(timesnew MathContext(precision.)));
         } else {
             return new RubyBigDecimal(getRuntime(), .pow(times));
         }
     }
 
     @JRubyMethod(name = {"**""power"}, required = 1, compat = .)
     public IRubyObject op_pow19(IRubyObject exp) {
         if (!(exp instanceof RubyFixnum)) {
             throw getRuntime().newTypeError("wrong argument type " + exp.getMetaClass() + " (expected Fixnum)");
         }
         Ruby runtime = getRuntime();
         ThreadContext context = runtime.getCurrentContext();
 
         if (isNaN()) {
             return newNaN(runtime);
         }
         if (isInfinity()) {
             if (Numeric.f_negative_p(contextexp)) {
                 if ( < 0) {
                     if (Numeric.f_integer_p(contextexp).isTrue()) {
                         if (is_even(exp)) {
                             /* (-Infinity) ** (-even_integer) -> +0 */
                             return newZero(runtime, 1);
                         } else {
                             /* (-Infinity) ** (-odd_integer) -> -0 */
                             return newZero(runtime, -1);
                         }
                     } else {
                         /* (-Infinity) ** (-non_integer) -> -0 */
                         return newZero(runtime, -1);
                     }
                 } else {
                     return newZero(runtime, 0);
 
                 }
             } else {
                 if ( < 0) {
                     if (Numeric.f_integer_p(contextexp).isTrue()) {
                         if (is_even(exp)) {
                             return newInfinity(runtime, 1);
                         } else {
                             return newInfinity(runtime, -1);
                         }
                     } else {
                         throw runtime.newMathDomainError("a non-integral exponent for a negative base");
                     }
                 } else {
                     return newInfinity(runtime, 1);
                 }
             }
         }
 
         int times = RubyNumeric.fix2int(exp.convertToInteger());
 
         if (times < 0) {
             if (isZero()) {
                 return newInfinity(getRuntime(), .signum());
             }
 
             // Note: MRI has a very non-trivial way of calculating the precision,
             // so we use very simple approximation here:
             int precision = (-times + 4) * (getAllDigits().length() + 4);
 
             return new RubyBigDecimal(getRuntime(),
                     .pow(timesnew MathContext(precision.)));
         } else {
             return new RubyBigDecimal(getRuntime(), .pow(times));
         }
     }
 
     @JRubyMethod(name = "+", compat = .)
     public IRubyObject op_plus(ThreadContext contextIRubyObject b) {
         RubyBigDecimal val = getVpValue(bfalse);
         return addInternal(contextvalbgetRuntime().getClass("BigDecimal")
                 .searchInternalModuleVariable("vpPrecLimit"));
     }
 
     @JRubyMethod(name = "+", compat = .)
     public IRubyObject op_plus19(ThreadContext contextIRubyObject b) {
         RubyBigDecimal val = getVpValue19(contextbfalse);
         return addInternal(contextvalbgetRuntime().getClass("BigDecimal")
                 .searchInternalModuleVariable("vpPrecLimit"));
     }
 
     @JRubyMethod(name = "add", compat = .)
     public IRubyObject add2(ThreadContext contextIRubyObject bIRubyObject digits) {
         RubyBigDecimal val = getVpValue(bfalse);
         return addInternal(contextvalbdigits);
     }
 
     @JRubyMethod(name = "add", compat = .)
     public IRubyObject add219(ThreadContext contextIRubyObject bIRubyObject digits) {
         RubyBigDecimal val = getVpValue19(contextbfalse);
         return addInternal(contextvalbdigits);
     }
 
     private IRubyObject addInternal(ThreadContext contextRubyBigDecimal valIRubyObject bIRubyObject digits) {
         Ruby runtime = context.runtime;
         int prec = getPositiveInt(contextdigits);
 
         if (val == null) {
             // TODO:
             // MRI behavior: Call "+" or "add", depending on the call.
             // But this leads to exceptions when Floats are added. See:
             // http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17374
             // return callCoerced(context, op, b, true); -- this is MRI behavior.
             // We'll use ours for now, thus providing an ability to add Floats.
             return callCoerced(context"+"btrue);
         }
 
         RubyBigDecimal res = handleAddSpecialValues(val);
         if (res != null) {
             return res;
         }
         RoundingMode roundMode = getRoundingMode(runtime);
         return new RubyBigDecimal(runtime.add(
                 val.valuenew MathContext(precroundMode))); // TODO: why this: .setResult();
     }