diff options
63 files changed, 1135 insertions, 372 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 87bd498ea1..32c15b04aa 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -206,9 +206,9 @@ trait Scanners extends ScannersCommon { token = kwArray(idx) if (token == IDENTIFIER && allowIdent != name) { if (name == nme.MACROkw) - syntaxError(name+" is now a reserved word; usage as an identifier is disallowed") + syntaxError(s"$name is now a reserved word; usage as an identifier is disallowed") else if (emitIdentifierDeprecationWarnings) - deprecationWarning(name+" is now a reserved word; usage as an identifier is deprecated") + deprecationWarning(s"$name is now a reserved word; usage as an identifier is deprecated") } } } @@ -389,7 +389,7 @@ trait Scanners extends ScannersCommon { // println("blank line found at "+lastOffset+":"+(lastOffset to idx).map(buf(_)).toList) return true } - if (idx == end) return false + if (idx == end) return false } while (ch <= ' ') } idx += 1; ch = buf(idx) diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 0b5b0759b2..1e544e54f6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -43,11 +43,11 @@ trait Adaptations { def givenString = if (args.isEmpty) "<none>" else args.mkString(", ") def adaptedArgs = if (args.isEmpty) "(): Unit" else args.mkString("(", ", ", "): " + applyArg.tpe) - def adaptWarning(msg: String) = context.warning(t.pos, msg + + def adaptWarningMessage(msg: String, showAdaptation: Boolean = true) = msg + "\n signature: " + sigString + "\n given arguments: " + givenString + - "\n after adaptation: " + callString + "(" + adaptedArgs + ")" - ) + (if (showAdaptation) "\n after adaptation: " + callString + "(" + adaptedArgs + ")" else "") + // A one-argument method accepting Object (which may look like "Any" // at this point if the class is java defined) is a "leaky target" for // which we should be especially reluctant to insert () or auto-tuple. @@ -69,17 +69,20 @@ trait Adaptations { } if (settings.noAdaptedArgs) - adaptWarning("No automatic adaptation here: use explicit parentheses.") - else if (settings.warnAdaptedArgs) - adaptWarning( - if (args.isEmpty) "Adapting argument list by inserting (): " + ( - if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous." - else "this is unlikely to be what you want." - ) - else "Adapting argument list by creating a " + args.size + "-tuple: this may not be what you want." - ) + context.warning(t.pos, adaptWarningMessage("No automatic adaptation here: use explicit parentheses.")) + else if (args.isEmpty) { + if (settings.future) + context.error(t.pos, adaptWarningMessage("Adaptation of argument list by inserting () has been removed.", showAdaptation = false)) + else { + val msg = "Adaptation of argument list by inserting () has been deprecated: " + ( + if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous." + else "this is unlikely to be what you want.") + context.unit.deprecationWarning(t.pos, adaptWarningMessage(msg)) + } + } else if (settings.warnAdaptedArgs) + context.warning(t.pos, adaptWarningMessage(s"Adapting argument list by creating a ${args.size}-tuple: this may not be what you want.")) - !settings.noAdaptedArgs + !settings.noAdaptedArgs || !(args.isEmpty && settings.future) } } } diff --git a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala index f116e4af34..e6f95eb0d6 100644 --- a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala +++ b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala @@ -46,7 +46,7 @@ abstract class CharArrayReader extends CharArrayReaderData { self => def isUnicodeEscape = charOffset == lastUnicodeOffset /** Advance one character; reducing CR;LF pairs to just LF */ - final def nextChar() { + final def nextChar(): Unit = { if (charOffset >= buf.length) { ch = SU } else { @@ -54,7 +54,10 @@ abstract class CharArrayReader extends CharArrayReaderData { self => ch = c charOffset += 1 if (c == '\\') potentialUnicode() - else if (c < ' ') { skipCR(); potentialLineEnd() } + if (ch < ' ') { + skipCR() + potentialLineEnd() + } } } @@ -74,7 +77,7 @@ abstract class CharArrayReader extends CharArrayReaderData { self => } /** Interpret \\uxxxx escapes */ - private def potentialUnicode() { + private def potentialUnicode() = { def evenSlashPrefix: Boolean = { var p = charOffset - 2 while (p >= 0 && buf(p) == '\\') p -= 1 @@ -105,13 +108,17 @@ abstract class CharArrayReader extends CharArrayReaderData { self => } /** replace CR;LF by LF */ - private def skipCR() { - if (ch == CR) - if (charOffset < buf.length && buf(charOffset) == LF) { - charOffset += 1 - ch = LF + private def skipCR() = + if (ch == CR && charOffset < buf.length) + buf(charOffset) match { + case LF => + charOffset += 1 + ch = LF + case '\\' => + if (lookaheadReader.getu == LF) + potentialUnicode() + case _ => } - } /** Handle line ends */ private def potentialLineEnd() { @@ -132,5 +139,6 @@ abstract class CharArrayReader extends CharArrayReaderData { self => def error(offset: Int, msg: String) = self.error(offset, msg) /** A mystery why CharArrayReader.nextChar() returns Unit */ def getc() = { nextChar() ; ch } + def getu() = { require(buf(charOffset) == '\\') ; ch = '\\' ; charOffset += 1 ; potentialUnicode() ; ch } } } diff --git a/src/library/scala/Enumeration.scala b/src/library/scala/Enumeration.scala index 59be0cdfa3..d4b9c17eab 100644 --- a/src/library/scala/Enumeration.scala +++ b/src/library/scala/Enumeration.scala @@ -11,7 +11,7 @@ package scala import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } import scala.reflect.NameTransformer._ -import java.util.regex.Pattern +import scala.util.matching.Regex /** Defines a finite set of values specific to the enumeration. Typically * these values enumerate all possible forms something can take and provide @@ -64,7 +64,7 @@ abstract class Enumeration (initial: Int) extends Serializable { */ override def toString = ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split - Pattern.quote(NAME_JOIN_STRING)).last + Regex.quote(NAME_JOIN_STRING)).last /** The mapping from the integer used to identify values to the actual * values. */ diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala index 8900450fa3..ec587c158d 100644 --- a/src/library/scala/Predef.scala +++ b/src/library/scala/Predef.scala @@ -95,8 +95,6 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { type Set[A] = immutable.Set[A] val Map = immutable.Map val Set = immutable.Set - // @deprecated("Use scala.AnyRef instead", "2.10.0") - // def AnyRef = scala.AnyRef // Manifest types, companions, and incantations for summoning @annotation.implicitNotFound(msg = "No ClassManifest available for ${T}.") diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index 70f95750da..2d79452c5d 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -157,9 +157,8 @@ case class StringContext(parts: String*) { * If a formatting position does not refer to a `%` character (which is assumed to * start a format specifier), then the string format specifier `%s` is inserted. * - * 2. Any `%` characters not in formatting positions are left in the resulting - * string literally. This is achieved by replacing each such occurrence by the - * format specifier `%%`. + * 2. Any `%` characters not in formatting positions must begin one of the conversions + * `%%` (the literal percent) or `%n` (the platform-specific line separator). */ // The implementation is hardwired to `scala.tools.reflect.MacroImplementations.macro_StringInterpolation_f` // Using the mechanism implemented in `scala.tools.reflect.FastTrack` diff --git a/src/library/scala/collection/generic/ImmutableSetFactory.scala b/src/library/scala/collection/generic/ImmutableSetFactory.scala index f4d4e061bb..a72caf2633 100644 --- a/src/library/scala/collection/generic/ImmutableSetFactory.scala +++ b/src/library/scala/collection/generic/ImmutableSetFactory.scala @@ -15,6 +15,7 @@ import scala.language.higherKinds abstract class ImmutableSetFactory[CC[X] <: immutable.Set[X] with SetLike[X, CC[X]]] extends SetFactory[CC] { - + private[collection] def emptyInstance: CC[Any] + override def empty[A] = emptyInstance.asInstanceOf[CC[A]] def newBuilder[A]: Builder[A, CC[A]] = new SetBuilder[A, CC[A]](empty[A]) } diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 062fe7c158..127b22185a 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -137,10 +137,10 @@ object HashSet extends ImmutableSetFactory[HashSet] { /** $setCanBuildFromInfo */ implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, HashSet[A]] = setCanBuildFrom[A] - override def empty[A]: HashSet[A] = EmptyHashSet.asInstanceOf[HashSet[A]] private object EmptyHashSet extends HashSet[Any] { } - + private[collection] def emptyInstance: HashSet[Any] = EmptyHashSet + // utility method to create a HashTrieSet from two leaf HashSets (HashSet1 or HashSetCollision1) with non-colliding hash code) private def makeHashTrieSet[A](hash0:Int, elem0:HashSet[A], hash1:Int, elem1:HashSet[A], level:Int) : HashTrieSet[A] = { val index0 = (hash0 >>> level) & 0x1f diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala index 7ebaa26d26..1bb07eb02d 100644 --- a/src/library/scala/collection/immutable/ListSet.scala +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -22,10 +22,11 @@ import mutable.{ ListBuffer, Builder } object ListSet extends ImmutableSetFactory[ListSet] { /** setCanBuildFromInfo */ implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, ListSet[A]] = setCanBuildFrom[A] - override def empty[A] = EmptyListSet.asInstanceOf[ListSet[A]] + override def newBuilder[A]: Builder[A, ListSet[A]] = new ListSetBuilder[A] private object EmptyListSet extends ListSet[Any] { } + private[collection] def emptyInstance: ListSet[Any] = EmptyListSet /** A custom builder because forgetfully adding elements one at * a time to a list backed set puts the "squared" in N^2. There is a diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala index a888955bb2..e21a8dfa8a 100644 --- a/src/library/scala/collection/immutable/Set.scala +++ b/src/library/scala/collection/immutable/Set.scala @@ -53,8 +53,7 @@ trait Set[A] extends Iterable[A] object Set extends ImmutableSetFactory[Set] { /** $setCanBuildFromInfo */ implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Set[A]] = setCanBuildFrom[A] - override def empty[A]: Set[A] = EmptySet.asInstanceOf[Set[A]] - + /** An optimized representation for immutable empty sets */ private object EmptySet extends AbstractSet[Any] with Set[Any] with Serializable { override def size: Int = 0 @@ -64,6 +63,7 @@ object Set extends ImmutableSetFactory[Set] { def iterator: Iterator[Any] = Iterator.empty override def foreach[U](f: Any => U): Unit = {} } + private[collection] def emptyInstance: Set[Any] = EmptySet /** An optimized representation for immutable sets of size 1 */ @SerialVersionUID(1233385750652442003L) diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index a82e31f5da..49c3b4c3cd 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -960,14 +960,16 @@ self => * }}} */ override def flatten[B](implicit asTraversable: A => /*<:<!!!*/ GenTraversableOnce[B]): Stream[B] = { - def flatten1(t: Traversable[B]): Stream[B] = - if (!t.isEmpty) - cons(t.head, flatten1(t.tail)) - else - tail.flatten - - if (isEmpty) Stream.empty - else flatten1(asTraversable(head).seq.toTraversable) + var st: Stream[A] = this + while (st.nonEmpty) { + val h = asTraversable(st.head) + if (h.isEmpty) { + st = st.tail + } else { + return h.toStream #::: st.tail.flatten + } + } + Stream.empty } override def view = new StreamView[A, Stream[A]] { diff --git a/src/library/scala/collection/immutable/StringLike.scala b/src/library/scala/collection/immutable/StringLike.scala index 5a0d24ddd2..43d46cf4d0 100644 --- a/src/library/scala/collection/immutable/StringLike.scala +++ b/src/library/scala/collection/immutable/StringLike.scala @@ -164,8 +164,8 @@ self => * @return the resulting string */ def replaceAllLiterally(literal: String, replacement: String): String = { - val arg1 = java.util.regex.Pattern.quote(literal) - val arg2 = java.util.regex.Matcher.quoteReplacement(replacement) + val arg1 = Regex.quote(literal) + val arg2 = Regex.quoteReplacement(replacement) toString.replaceAll(arg1, arg2) } diff --git a/src/library/scala/collection/mutable/AVLTree.scala b/src/library/scala/collection/mutable/AVLTree.scala index d2205f9994..de09bb2040 100644 --- a/src/library/scala/collection/mutable/AVLTree.scala +++ b/src/library/scala/collection/mutable/AVLTree.scala @@ -11,10 +11,10 @@ package collection package mutable /** - * An immutable AVL Tree implementation used by mutable.TreeSet + * An immutable AVL Tree implementation formerly used by mutable.TreeSet * * @author Lucien Pereira - * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11") + * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.0") */ private[mutable] sealed trait AVLTree[+A] extends Serializable { def balance: Int @@ -65,7 +65,7 @@ private[mutable] sealed trait AVLTree[+A] extends Serializable { } /** - * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11") + * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.0") */ private case object Leaf extends AVLTree[Nothing] { override val balance: Int = 0 @@ -74,7 +74,7 @@ private case object Leaf extends AVLTree[Nothing] { } /** - * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11") + * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.0") */ private case class Node[A](data: A, left: AVLTree[A], right: AVLTree[A]) extends AVLTree[A] { override val balance: Int = right.depth - left.depth @@ -211,7 +211,7 @@ private case class Node[A](data: A, left: AVLTree[A], right: AVLTree[A]) extends } /** - * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11") + * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.0") */ private class AVLIterator[A](root: Node[A]) extends Iterator[A] { val stack = mutable.ArrayStack[Node[A]](root) diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala index e1f18a7036..e342e134b4 100644 --- a/src/library/scala/collection/mutable/ArrayOps.scala +++ b/src/library/scala/collection/mutable/ArrayOps.scala @@ -53,14 +53,14 @@ trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParalleliza super.toArray[U] } - def :+[B >: T: scala.reflect.ClassTag](elem: B): Array[B] = { + def :+[B >: T: ClassTag](elem: B): Array[B] = { val result = Array.ofDim[B](repr.length + 1) Array.copy(repr, 0, result, 0, repr.length) result(repr.length) = elem result } - def +:[B >: T: scala.reflect.ClassTag](elem: B): Array[B] = { + def +:[B >: T: ClassTag](elem: B): Array[B] = { val result = Array.ofDim[B](repr.length + 1) result(0) = elem Array.copy(repr, 0, result, 1, repr.length) @@ -107,6 +107,54 @@ trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParalleliza bb.result() } } + + /** Converts an array of pairs into an array of first elements and an array of second elements. + * + * @tparam T1 the type of the first half of the element pairs + * @tparam T2 the type of the second half of the element pairs + * @param asPair an implicit conversion which asserts that the element type + * of this Array is a pair. + * @return a pair of Arrays, containing, respectively, the first and second half + * of each element pair of this Array. + */ + def unzip[T1: ClassTag, T2: ClassTag](implicit asPair: T => (T1, T2)): (Array[T1], Array[T2]) = { + val a1 = new Array[T1](length) + val a2 = new Array[T2](length) + var i = 0 + while (i < length) { + val e = apply(i) + a1(i) = e._1 + a2(i) = e._2 + i += 1 + } + (a1, a2) + } + + /** Converts an array of triples into three arrays, one containing the elements from each position of the triple. + * + * @tparam T1 the type of the first of three elements in the triple + * @tparam T2 the type of the second of three elements in the triple + * @tparam T3 the type of the third of three elements in the triple + * @param asTriple an implicit conversion which asserts that the element type + * of this Array is a triple. + * @return a triple of Arrays, containing, respectively, the first, second, and third + * elements from each element triple of this Array. + */ + def unzip3[T1: ClassTag, T2: ClassTag, T3: ClassTag](implicit asTriple: T => (T1, T2, T3)): (Array[T1], Array[T2], Array[T3]) = { + val a1 = new Array[T1](length) + val a2 = new Array[T2](length) + val a3 = new Array[T3](length) + var i = 0 + while (i < length) { + val e = apply(i) + a1(i) = e._1 + a2(i) = e._2 + a3(i) = e._3 + i += 1 + } + (a1, a2, a3) + } + def seq = thisCollection diff --git a/src/library/scala/io/Position.scala b/src/library/scala/io/Position.scala index 85149223ee..011d0f17af 100644 --- a/src/library/scala/io/Position.scala +++ b/src/library/scala/io/Position.scala @@ -34,7 +34,7 @@ package io * @author Burak Emir (translated from work by Matthias Zenger and others) */ @deprecated("This class will be removed.", "2.10.0") -abstract class Position { +private[scala] abstract class Position { /** Definable behavior for overflow conditions. */ def checkInput(line: Int, column: Int): Unit @@ -68,7 +68,7 @@ abstract class Position { def toString(pos: Int): String = line(pos) + ":" + column(pos) } -object Position extends Position { +private[scala] object Position extends Position { def checkInput(line: Int, column: Int) { if (line < 0) throw new IllegalArgumentException(line + " < 0") diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala index d783dd29f5..bcbed645a7 100644 --- a/src/library/scala/math/BigDecimal.scala +++ b/src/library/scala/math/BigDecimal.scala @@ -16,36 +16,165 @@ import scala.collection.immutable.NumericRange import scala.language.implicitConversions -/** +/** * @author Stephane Micheloud - * @version 1.0 + * @author Rex Kerr + * @version 1.1 * @since 2.7 */ object BigDecimal { + private final val maximumHashScale = 4934 // Quit maintaining hash identity with BigInt beyond this scale + private final val hashCodeNotComputed = 0x5D50690F // Magic value (happens to be "BigDecimal" old MurmurHash3 value) + private final val deci2binary = 3.3219280948873626 // Ratio of log(10) to log(2) private val minCached = -512 private val maxCached = 512 val defaultMathContext = MathContext.DECIMAL128 - /** Cache ony for defaultMathContext using BigDecimals in a small range. */ + /** Cache only for defaultMathContext using BigDecimals in a small range. */ private lazy val cache = new Array[BigDecimal](maxCached - minCached + 1) object RoundingMode extends Enumeration { + // Annoying boilerplate to ensure consistency with java.math.RoundingMode + import java.math.{RoundingMode => RM} type RoundingMode = Value - // These are supposed to be the same as java.math.RoundingMode.values, - // though it seems unwise to rely on the correspondence. - val UP, DOWN, CEILING, FLOOR, HALF_UP, HALF_DOWN, HALF_EVEN, UNNECESSARY = Value + val UP = Value(RM.UP.ordinal) + val DOWN = Value(RM.DOWN.ordinal) + val CEILING = Value(RM.CEILING.ordinal) + val FLOOR = Value(RM.FLOOR.ordinal) + val HALF_UP = Value(RM.HALF_UP.ordinal) + val HALF_DOWN = Value(RM.HALF_DOWN.ordinal) + val HALF_EVEN = Value(RM.HALF_EVEN.ordinal) + val UNNECESSARY = Value(RM.UNNECESSARY.ordinal) + } + + /** Constructs a `BigDecimal` using the decimal text representation of `Double` value `d`, rounding if necessary. */ + def decimal(d: Double, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(java.lang.Double.toString(d), mc)) + + /** Constructs a `BigDecimal` using the decimal text representation of `Double` value `d`. */ + def decimal(d: Double): BigDecimal = decimal(d, defaultMathContext) + + /** Constructs a `BigDecimal` using the decimal text representation of `Float` value `f`, rounding if necessary. + * Note that `BigDecimal.decimal(0.1f) != 0.1f` since equality agrees with the `Double` representation, and + * `0.1 != 0.1f`. + */ + def decimal(f: Float, mc: MathContext): BigDecimal = + new BigDecimal(new BigDec(java.lang.Float.toString(f), mc)) + + /** Constructs a `BigDecimal` using the decimal text representation of `Float` value `f`. + * Note that `BigDecimal.decimal(0.1f) != 0.1f` since equality agrees with the `Double` representation, and + * `0.1 != 0.1f`. + */ + def decimal(f: Float): BigDecimal = decimal(f, defaultMathContext) + + // This exists solely to avoid conversion from Int/Long to Float, screwing everything up. + /** Constructs a `BigDecimal` from a `Long`, rounding if necessary. This is identical to `BigDecimal(l, mc)`. */ + def decimal(l: Long, mc: MathContext): BigDecimal = apply(l, mc) + + // This exists solely to avoid conversion from Int/Long to Float, screwing everything up. + /** Constructs a `BigDecimal` from a `Long`. This is identical to `BigDecimal(l)`. */ + def decimal(l: Long): BigDecimal = apply(l) + + /** Constructs a `BigDecimal` using a `java.math.BigDecimal`, rounding if necessary. */ + def decimal(bd: BigDec, mc: MathContext): BigDecimal = new BigDecimal(bd.round(mc), mc) + + /** Constructs a `BigDecimal` by expanding the binary fraction + * contained by `Double` value `d` into a decimal representation, + * rounding if necessary. When a `Float` is converted to a + * `Double`, the binary fraction is preserved, so this method + * also works for converted `Float`s. + */ + def binary(d: Double, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(d, mc), mc) + + /** Constructs a `BigDecimal` by expanding the binary fraction + * contained by `Double` value `d` into a decimal representation. + * Note: this also works correctly on converted `Float`s. + */ + def binary(d: Double): BigDecimal = binary(d, defaultMathContext) + + /** Constructs a `BigDecimal` from a `java.math.BigDecimal`. The + * precision is the default for `BigDecimal` or enough to represent + * the `java.math.BigDecimal` exactly, whichever is greater. + */ + def exact(repr: BigDec): BigDecimal = { + val mc = + if (repr.precision <= defaultMathContext.getPrecision) defaultMathContext + else new MathContext(repr.precision, java.math.RoundingMode.HALF_EVEN) + new BigDecimal(repr, mc) } + + /** Constructs a `BigDecimal` by fully expanding the binary fraction + * contained by `Double` value `d`, adjusting the precision as + * necessary. Note: this works correctly on converted `Float`s also. + */ + def exact(d: Double): BigDecimal = exact(new BigDec(d)) + + /** Constructs a `BigDecimal` that exactly represents a `BigInt`. + */ + def exact(bi: BigInt): BigDecimal = exact(new BigDec(bi.bigInteger)) + + /** Constructs a `BigDecimal` that exactly represents a `Long`. Note that + * all creation methods for `BigDecimal` that do not take a `MathContext` + * represent a `Long`; this is equivalent to `apply`, `valueOf`, etc.. + */ + def exact(l: Long): BigDecimal = apply(l) + + /** Constructs a `BigDecimal` that exactly represents the number + * specified in a `String`. + */ + def exact(s: String): BigDecimal = exact(new BigDec(s)) + + /** Constructs a 'BigDecimal` that exactly represents the number + * specified in base 10 in a character array. + */ + def exact(cs: Array[Char]): BigDecimal = exact(new BigDec(cs)) + /** Constructs a `BigDecimal` using the java BigDecimal static - * valueOf constructor. + * valueOf constructor. Equivalent to `BigDecimal.decimal`. * * @param d the specified double value * @return the constructed `BigDecimal` */ def valueOf(d: Double): BigDecimal = apply(BigDec valueOf d) + + /** Constructs a `BigDecimal` using the java BigDecimal static + * valueOf constructor, specifying a `MathContext` that is + * used for computations but isn't used for rounding. Use + * `BigDecimal.decimal` to use `MathContext` for rounding, + * or `BigDecimal(java.math.BigDecimal.valueOf(d), mc)` for + * no rounding. + * + * @param d the specified double value + * @param mc the `MathContext` used for future computations + * @return the constructed `BigDecimal` + */ + @deprecated("MathContext is not applied to Doubles in valueOf. Use BigDecimal.decimal to use rounding, or java.math.BigDecimal.valueOf to avoid it.","2.11") def valueOf(d: Double, mc: MathContext): BigDecimal = apply(BigDec valueOf d, mc) - def valueOf(x: Long): BigDecimal = apply(x.toDouble) + + /** Constructs a `BigDecimal` using the java BigDecimal static + * valueOf constructor. + * + * @param x the specified `Long` value + * @return the constructed `BigDecimal` + */ + def valueOf(x: Long): BigDecimal = apply(x) + + /** Constructs a `BigDecimal` using the java BigDecimal static + * valueOf constructor. This is unlikely to do what you want; + * use `valueOf(f.toDouble)` or `decimal(f)` instead. + */ + @deprecated("Float arguments to valueOf may not do what you wish. Use decimal or valueOf(f.toDouble).","2.11") + def valueOf(f: Float): BigDecimal = valueOf(f.toDouble) + + /** Constructs a `BigDecimal` using the java BigDecimal static + * valueOf constructor. This is unlikely to do what you want; + * use `valueOf(f.toDouble)` or `decimal(f)` instead. + */ + @deprecated("Float arguments to valueOf may not do what you wish. Use decimal or valueOf(f.toDouble).","2.11") + def valueOf(f: Float, mc: MathContext): BigDecimal = valueOf(f.toDouble, mc) + /** Constructs a `BigDecimal` whose value is equal to that of the * specified `Integer` value. * @@ -53,6 +182,14 @@ object BigDecimal { * @return the constructed `BigDecimal` */ def apply(i: Int): BigDecimal = apply(i, defaultMathContext) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified `Integer` value, rounding if necessary. + * + * @param i the specified integer value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ def apply(i: Int, mc: MathContext): BigDecimal = if (mc == defaultMathContext && minCached <= i && i <= maxCached) { val offset = i - minCached @@ -60,7 +197,7 @@ object BigDecimal { if (n eq null) { n = new BigDecimal(BigDec.valueOf(i.toLong), mc); cache(offset) = n } n } - else new BigDecimal(BigDec.valueOf(i.toLong), mc) + else apply(i.toLong, mc) /** Constructs a `BigDecimal` whose value is equal to that of the * specified long value. @@ -72,6 +209,13 @@ object BigDecimal { if (minCached <= l && l <= maxCached) apply(l.toInt) else new BigDecimal(BigDec.valueOf(l), defaultMathContext) + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified long value, but rounded if necessary. + * + * @param l the specified long value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ def apply(l: Long, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(l, mc), mc) @@ -85,35 +229,62 @@ object BigDecimal { def apply(unscaledVal: Long, scale: Int): BigDecimal = apply(BigInt(unscaledVal), scale) + /** Constructs a `BigDecimal` whose unscaled value is equal to that + * of the specified long value, but rounded if necessary. + * + * @param unscaledVal the value + * @param scale the scale + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ def apply(unscaledVal: Long, scale: Int, mc: MathContext): BigDecimal = apply(BigInt(unscaledVal), scale, mc) /** Constructs a `BigDecimal` whose value is equal to that of the - * specified double value. + * specified double value. Equivalent to `BigDecimal.decimal`. * * @param d the specified `Double` value * @return the constructed `BigDecimal` */ - def apply(d: Double): BigDecimal = apply(d, defaultMathContext) + def apply(d: Double): BigDecimal = decimal(d, defaultMathContext) + // note we don't use the static valueOf because it doesn't let us supply // a MathContext, but we should be duplicating its logic, modulo caching. - def apply(d: Double, mc: MathContext): BigDecimal = - new BigDecimal(new BigDec(jl.Double.toString(d), mc), mc) + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified double value, but rounded if necessary. Equivalent to + * `BigDecimal.decimal`. + * + * @param d the specified `Double` value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ + def apply(d: Double, mc: MathContext): BigDecimal = decimal(d, mc) + @deprecated("The default conversion from Float may not do what you want. Use BigDecimal.decimal for a String representation, or explicitly convert the Float with .toDouble.", "2.11") def apply(x: Float): BigDecimal = apply(x.toDouble) + + @deprecated("The default conversion from Float may not do what you want. Use BigDecimal.decimal for a String representation, or explicitly convert the Float with .toDouble.", "2.11") def apply(x: Float, mc: MathContext): BigDecimal = apply(x.toDouble, mc) /** Translates a character array representation of a `BigDecimal` * into a `BigDecimal`. */ - def apply(x: Array[Char]): BigDecimal = apply(x, defaultMathContext) + def apply(x: Array[Char]): BigDecimal = exact(x) + + /** Translates a character array representation of a `BigDecimal` + * into a `BigDecimal`, rounding if necessary. + */ def apply(x: Array[Char], mc: MathContext): BigDecimal = - new BigDecimal(new BigDec(x.mkString, mc), mc) + new BigDecimal(new BigDec(x, mc), mc) /** Translates the decimal String representation of a `BigDecimal` * into a `BigDecimal`. */ - def apply(x: String): BigDecimal = apply(x, defaultMathContext) + def apply(x: String): BigDecimal = exact(x) + + /** Translates the decimal String representation of a `BigDecimal` + * into a `BigDecimal`, rounding if necessary. + */ def apply(x: String, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(x, mc), mc) @@ -123,7 +294,15 @@ object BigDecimal { * @param x the specified `BigInt` value * @return the constructed `BigDecimal` */ - def apply(x: BigInt): BigDecimal = apply(x, defaultMathContext) + def apply(x: BigInt): BigDecimal = exact(x) + + /** Constructs a `BigDecimal` whose value is equal to that of the + * specified `BigInt` value, rounding if necessary. + * + * @param x the specified `BigInt` value + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ def apply(x: BigInt, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(x.bigInteger, mc), mc) @@ -134,11 +313,24 @@ object BigDecimal { * @param scale the scale * @return the constructed `BigDecimal` */ - def apply(unscaledVal: BigInt, scale: Int): BigDecimal = apply(unscaledVal, scale, defaultMathContext) + def apply(unscaledVal: BigInt, scale: Int): BigDecimal = + exact(new BigDec(unscaledVal.bigInteger, scale)) + + /** Constructs a `BigDecimal` whose unscaled value is equal to that + * of the specified `BigInt` value. + * + * @param unscaledVal the specified `BigInt` value + * @param scale the scale + * @param mc the precision and rounding mode for creation of this value and future operations on it + * @return the constructed `BigDecimal` + */ def apply(unscaledVal: BigInt, scale: Int, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(unscaledVal.bigInteger, scale, mc), mc) + /** Constructs a `BigDecimal` from a `java.math.BigDecimal`. */ def apply(bd: BigDec): BigDecimal = apply(bd, defaultMathContext) + + @deprecated("This method appears to round a java.math.BigDecimal but actually doesn't. Use new BigDecimal(bd, mc) instead for no rounding, or BigDecimal.decimal(bd, mc) for rounding.", "2.11") def apply(bd: BigDec, mc: MathContext): BigDecimal = new BigDecimal(bd, mc) /** Implicit conversion from `Int` to `BigDecimal`. */ @@ -148,43 +340,123 @@ object BigDecimal { implicit def long2bigDecimal(l: Long): BigDecimal = apply(l) /** Implicit conversion from `Double` to `BigDecimal`. */ - implicit def double2bigDecimal(d: Double): BigDecimal = valueOf(d, defaultMathContext) + implicit def double2bigDecimal(d: Double): BigDecimal = decimal(d) /** Implicit conversion from `java.math.BigDecimal` to `scala.BigDecimal`. */ implicit def javaBigDecimal2bigDecimal(x: BigDec): BigDecimal = apply(x) } /** + * `BigDecimal` represents decimal floating-point numbers of arbitrary precision. + * By default, the precision approximately matches that of IEEE 128-bit floating + * point numbers (34 decimal digits, `HALF_EVEN` rounding mode). Within the range + * of IEEE binary128 numbers, `BigDecimal` will agree with `BigInt` for both + * equality and hash codes (and will agree with primitive types as well). Beyond + * that range--numbers with more than 4934 digits when written out in full--the + * `hashCode` of `BigInt` and `BigDecimal` is allowed to diverge due to difficulty + * in efficiently computing both the decimal representation in `BigDecimal` and the + * binary representation in `BigInt`. + * + * When creating a `BigDecimal` from a `Double` or `Float`, care must be taken as + * the binary fraction representation of `Double` and `Float` does not easily + * convert into a decimal representation. Three explicit schemes are available + * for conversion. `BigDecimal.decimal` will convert the floating-point number + * to a decimal text representation, and build a `BigDecimal` based on that. + * `BigDecimal.binary` will expand the binary fraction to the requested or default + * precision. `BigDecimal.exact` will expand the binary fraction to the + * full number of digits, thus producing the exact decimal value corrsponding to + * the binary fraction of that floating-point number. `BigDecimal` equality + * matches the decimal expansion of `Double`: `BigDecimal.decimal(0.1) == 0.1`. + * Note that since `0.1f != 0.1`, the same is not true for `Float`. Instead, + * `0.1f == BigDecimal.decimal((0.1f).toDouble)`. + * + * To test whether a `BigDecimal` number can be converted to a `Double` or + * `Float` and then back without loss of information by using one of these + * methods, test with `isDecimalDouble`, `isBinaryDouble`, or `isExactDouble` + * or the corresponding `Float` versions. Note that `BigInt`'s `isValidDouble` + * will agree with `isExactDouble`, not the `isDecimalDouble` used by default. + * + * `BigDecimal` uses the decimal representation of binary floating-point numbers + * to determine equality and hash codes. This yields different answers than + * conversion between `Long` and `Double` values, where the exact form is used. + * As always, since floating-point is a lossy representation, it is advisable to + * take care when assuming identity will be maintained across multiple conversions. + * + * `BigDecimal` maintains a `MathContext` that determines the rounding that + * is applied to certain calculations. In most cases, the value of the + * `BigDecimal` is also rounded to the precision specified by the `MathContext`. + * To create a `BigDecimal` with a different precision than its `MathContext`, + * use `new BigDecimal(new java.math.BigDecimal(...), mc)`. Rounding will + * be applied on those mathematical operations that can dramatically change the + * number of digits in a full representation, namely multiplication, division, + * and powers. The left-hand argument's `MathContext` always determines the + * degree of rounding, if any, and is the one propagated through arithmetic + * operations that do not apply rounding themselves. + * * @author Stephane Micheloud - * @version 1.0 + * @author Rex Kerr + * @version 1.1 */ -final class BigDecimal( - val bigDecimal: BigDec, - val mc: MathContext) +final class BigDecimal(val bigDecimal: BigDec, val mc: MathContext) extends ScalaNumber with ScalaNumericConversions with Serializable { def this(bigDecimal: BigDec) = this(bigDecimal, BigDecimal.defaultMathContext) import BigDecimal.RoundingMode._ - - /** Cuts way down on the wrapper noise. */ - private implicit def bigdec2BigDecimal(x: BigDec): BigDecimal = new BigDecimal(x, mc) - + import BigDecimal.{decimal, binary, exact} + + if (bigDecimal eq null) throw new IllegalArgumentException("null value for BigDecimal") + if (mc eq null) throw new IllegalArgumentException("null MathContext for BigDecimal") + + // There was an implicit to cut down on the wrapper noise for BigDec -> BigDecimal. + // However, this may mask introduction of surprising behavior (e.g. lack of rounding + // where one might expect it). Wrappers should be applied explicitly with an + // eye to correctness. + + // Sane hash code computation (which is surprisingly hard). + // Note--not lazy val because we can't afford the extra space. + private final var computedHashCode: Int = BigDecimal.hashCodeNotComputed + private final def computeHashCode(): Unit = { + computedHashCode = + if (isWhole && (precision - scale) < BigDecimal.maximumHashScale) toBigInt.hashCode + else if (isValidDouble) doubleValue.## + else { + val temp = bigDecimal.stripTrailingZeros + scala.util.hashing.MurmurHash3.mixLast( temp.scaleByPowerOfTen(temp.scale).toBigInteger.hashCode, temp.scale ) + } + } + /** Returns the hash code for this BigDecimal. - * Note that this does not use the underlying java object's - * hashCode because we compare BigDecimals with compareTo + * Note that this does not merely use the underlying java object's + * `hashCode` because we compare `BigDecimal`s with `compareTo` * which deems 2 == 2.00, whereas in java these are unequal - * with unequal hashCodes. - */ - override def hashCode(): Int = - if (isWhole()) unifiedPrimitiveHashcode() - else doubleValue.## + * with unequal `hashCode`s. These hash codes agree with `BigInt` + * for whole numbers up ~4934 digits (the range of IEEE 128 bit floating + * point). Beyond this, hash codes will disagree; this prevents the + * explicit represention of the `BigInt` form for `BigDecimal` values + * with large exponents. + */ + override def hashCode(): Int = { + if (computedHashCode == BigDecimal.hashCodeNotComputed) computeHashCode + computedHashCode + } - /** Compares this BigDecimal with the specified value for equality. + /** Compares this BigDecimal with the specified value for equality. Where `Float` and `Double` + * disagree, `BigDecimal` will agree with the `Double` value */ override def equals (that: Any): Boolean = that match { case that: BigDecimal => this equals that - case that: BigInt => this.toBigIntExact exists (that equals _) - case that: Double => isValidDouble && toDouble == that - case that: Float => isValidFloat && toFloat == that + case that: BigInt => + that.bitLength > (precision-scale-2)*BigDecimal.deci2binary && + this.toBigIntExact.exists(that equals _) + case that: Double => + !that.isInfinity && { + val d = toDouble + !d.isInfinity && d == that && equals(decimal(d)) + } + case that: Float => + !that.isInfinity && { + val f = toFloat + !f.isInfinity && f == that && equals(decimal(f.toDouble)) + } case _ => isValidLong && unifiedPrimitiveEquals(that) } override def isValidByte = noArithmeticException(toByteExact) @@ -192,26 +464,71 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { override def isValidChar = isValidInt && toIntExact >= Char.MinValue && toIntExact <= Char.MaxValue override def isValidInt = noArithmeticException(toIntExact) def isValidLong = noArithmeticException(toLongExact) - /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. + /** Tests whether the value is a valid Float. "Valid" has several distinct meanings, however. Use + * `isExactFloat`, `isBinaryFloat`, or `isDecimalFloat`, depending on the intended meaning. + * By default, `decimal` creation is used, so `isDecimalFloat` is probably what you want. */ + @deprecated("What constitutes validity is unclear. Use `isExactFloat`, `isBinaryFloat`, or `isDecimalFloat` instead.", "2.11") def isValidFloat = { val f = toFloat - !f.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(f.toDouble)) == 0 + !f.isInfinity && bigDecimal.compareTo(new BigDec(f.toDouble)) == 0 } - /** Returns `true` iff this can be represented exactly by [[scala.Double]]; otherwise returns `false`. + /** Tests whether the value is a valid Double. "Valid" has several distinct meanings, however. Use + * `isExactDouble`, `isBinaryDouble`, or `isDecimalDouble`, depending on the intended meaning. + * By default, `decimal` creation is used, so `isDecimalDouble` is probably what you want. */ + @deprecated("Validity has two distinct meanings. Use `isExactBinaryDouble` or `equivalentToDouble` instead.", "2.11") def isValidDouble = { val d = toDouble - !d.isInfinity && bigDecimal.compareTo(new java.math.BigDecimal(d)) == 0 + !d.isInfinity && bigDecimal.compareTo(new BigDec(d)) == 0 + } + + /** Tests whether this `BigDecimal` holds the decimal representation of a `Double`. */ + def isDecimalDouble = { + val d = toDouble + !d.isInfinity && equals(decimal(d)) + } + + /** Tests whether this `BigDecimal` holds the decimal representation of a `Float`. */ + def isDecimalFloat = { + val f = toFloat + !f.isInfinity && equals(decimal(f)) + } + + /** Tests whether this `BigDecimal` holds, to within precision, the binary representation of a `Double`. */ + def isBinaryDouble = { + val d = toDouble + !d.isInfinity && equals(binary(d,mc)) + } + + /** Tests whether this `BigDecimal` holds, to within precision, the binary representation of a `Float`. */ + def isBinaryFloat = { + val f = toFloat + !f.isInfinity && equals(binary(f,mc)) + } + + /** Tests whether this `BigDecimal` holds the exact expansion of a `Double`'s binary fractional form into base 10. */ + def isExactDouble = { + val d = toDouble + !d.isInfinity && equals(exact(d)) + } + + /** Tests whether this `BigDecimal` holds the exact expansion of a `Float`'s binary fractional form into base 10. */ + def isExactFloat = { + val f = toFloat + !f.isInfinity && equals(exact(f.toDouble)) } + private def noArithmeticException(body: => Unit): Boolean = { try { body ; true } catch { case _: ArithmeticException => false } } - def isWhole() = (this remainder 1) == BigDecimal(0) + def isWhole() = scale <= 0 || bigDecimal.stripTrailingZeros.scale <= 0 + def underlying = bigDecimal + /** Compares this BigDecimal with the specified BigDecimal for equality. */ @@ -239,60 +556,66 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { /** Addition of BigDecimals */ - def + (that: BigDecimal): BigDecimal = this.bigDecimal.add(that.bigDecimal) + def + (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal add that.bigDecimal, mc) /** Subtraction of BigDecimals */ - def - (that: BigDecimal): BigDecimal = this.bigDecimal.subtract(that.bigDecimal) + def - (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal subtract that.bigDecimal, mc) /** Multiplication of BigDecimals */ - def * (that: BigDecimal): BigDecimal = this.bigDecimal.multiply(that.bigDecimal, mc) + def * (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal.multiply(that.bigDecimal, mc), mc) /** Division of BigDecimals */ - def / (that: BigDecimal): BigDecimal = this.bigDecimal.divide(that.bigDecimal, mc) + def / (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal.divide(that.bigDecimal, mc), mc) /** Division and Remainder - returns tuple containing the result of - * divideToIntegralValue and the remainder. + * divideToIntegralValue and the remainder. The computation is exact: no rounding is applied. */ def /% (that: BigDecimal): (BigDecimal, BigDecimal) = this.bigDecimal.divideAndRemainder(that.bigDecimal) match { - case Array(q, r) => (q, r) + case Array(q, r) => (new BigDecimal(q, mc), new BigDecimal(r, mc)) } /** Divide to Integral value. */ def quot (that: BigDecimal): BigDecimal = - this.bigDecimal.divideToIntegralValue(that.bigDecimal) + new BigDecimal(this.bigDecimal divideToIntegralValue that.bigDecimal, mc) - /** Returns the minimum of this and that + /** Returns the minimum of this and that, or this if the two are equal */ - def min (that: BigDecimal): BigDecimal = this.bigDecimal min that.bigDecimal - - /** Returns the maximum of this and that + def min (that: BigDecimal): BigDecimal = (this compare that) match { + case x if x <= 0 => this + case _ => that + } + + /** Returns the maximum of this and that, or this if the two are equal */ - def max (that: BigDecimal): BigDecimal = this.bigDecimal max that.bigDecimal - + def max (that: BigDecimal): BigDecimal = (this compare that) match { + case x if x >= 0 => this + case _ => that + } + /** Remainder after dividing this by that. */ - def remainder (that: BigDecimal): BigDecimal = this.bigDecimal.remainder(that.bigDecimal) + def remainder (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal remainder that.bigDecimal, mc) /** Remainder after dividing this by that. */ - def % (that: BigDecimal): BigDecimal = this.remainder(that) + def % (that: BigDecimal): BigDecimal = this remainder that /** Returns a BigDecimal whose value is this ** n. */ - def pow (n: Int): BigDecimal = this.bigDecimal.pow(n, mc) + def pow (n: Int): BigDecimal = new BigDecimal(this.bigDecimal.pow(n, mc), mc) /** Returns a BigDecimal whose value is the negation of this BigDecimal */ - def unary_- : BigDecimal = this.bigDecimal.negate() + def unary_- : BigDecimal = new BigDecimal(this.bigDecimal.negate(), mc) /** Returns the absolute value of this BigDecimal */ - def abs: BigDecimal = this.bigDecimal.abs + def abs: BigDecimal = if (signum < 0) unary_- else this /** Returns the sign of this BigDecimal, i.e. * -1 if it is less than 0, @@ -305,9 +628,19 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { */ def precision: Int = this.bigDecimal.precision() - /** Returns a BigDecimal rounded according to the MathContext settings. + /** Returns a BigDecimal rounded according to the supplied MathContext settings, but + * preserving its own MathContext for future operations. */ - def round(mc: MathContext): BigDecimal = this.bigDecimal round mc + def round(mc: MathContext): BigDecimal = { + val r = this.bigDecimal round mc + if (r eq bigDecimal) this else new BigDecimal(r, this.mc) + } + + /** Returns a `BigDecimal` rounded according to its own `MathContext` */ + def rounded: BigDecimal = { + val r = bigDecimal round mc + if (r eq bigDecimal) this else new BigDecimal(r, mc) + } /** Returns the scale of this `BigDecimal`. */ @@ -315,19 +648,22 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { /** Returns the size of an ulp, a unit in the last place, of this BigDecimal. */ - def ulp: BigDecimal = this.bigDecimal.ulp + def ulp: BigDecimal = new BigDecimal(this.bigDecimal.ulp, mc) - /** Returns a new BigDecimal based on the supplied MathContext. + /** Returns a new BigDecimal based on the supplied MathContext, rounded as needed. */ - def apply(mc: MathContext): BigDecimal = BigDecimal(this.bigDecimal.toString, mc) + def apply(mc: MathContext): BigDecimal = new BigDecimal(this.bigDecimal round mc, mc) /** Returns a `BigDecimal` whose scale is the specified value, and whose value is * numerically equal to this BigDecimal's. */ - def setScale(scale: Int): BigDecimal = this.bigDecimal setScale scale + def setScale(scale: Int): BigDecimal = + if (this.scale == scale) this + else new BigDecimal(this.bigDecimal setScale scale, mc) def setScale(scale: Int, mode: RoundingMode): BigDecimal = - this.bigDecimal.setScale(scale, mode.id) + if (this.scale == scale) this + else new BigDecimal(this.bigDecimal.setScale(scale, mode.id), mc) /** Converts this BigDecimal to a Byte. * If the BigDecimal is too big to fit in a Byte, only the low-order 8 bits are returned. @@ -442,8 +778,11 @@ extends ScalaNumber with ScalaNumericConversions with Serializable { * can be done losslessly, returning Some(BigInt) or None. */ def toBigIntExact(): Option[BigInt] = - try Some(new BigInt(this.bigDecimal.toBigIntegerExact())) - catch { case _: ArithmeticException => None } + if (isWhole()) { + try Some(new BigInt(this.bigDecimal.toBigIntegerExact())) + catch { case _: ArithmeticException => None } + } + else None /** Returns the decimal String representation of this BigDecimal. */ diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index 5e70bdc2f6..689fc0c3e1 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -119,7 +119,7 @@ final class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNum */ override def equals(that: Any): Boolean = that match { case that: BigInt => this equals that - case that: BigDecimal => that.toBigIntExact exists (this equals _) + case that: BigDecimal => that equals this case that: Double => isValidDouble && toDouble == that case that: Float => isValidFloat && toFloat == that case x => isValidLong && unifiedPrimitiveEquals(x) diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 74c1f2172c..509d181d87 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -61,21 +61,6 @@ package object reflect { // using the mechanism implemented in `scala.tools.reflect.FastTrack` // todo. once we have implicit macros for tag generation, we can remove this anchor private[scala] def materializeClassTag[T](): ClassTag[T] = macro ??? - - @deprecated("Use `@scala.beans.BeanDescription` instead", "2.10.0") - type BeanDescription = scala.beans.BeanDescription - @deprecated("Use `@scala.beans.BeanDisplayName` instead", "2.10.0") - type BeanDisplayName = scala.beans.BeanDisplayName - @deprecated("Use `@scala.beans.BeanInfo` instead", "2.10.0") - type BeanInfo = scala.beans.BeanInfo - @deprecated("Use `@scala.beans.BeanInfoSkip` instead", "2.10.0") - type BeanInfoSkip = scala.beans.BeanInfoSkip - @deprecated("Use `@scala.beans.BeanProperty` instead", "2.10.0") - type BeanProperty = scala.beans.BeanProperty - @deprecated("Use `@scala.beans.BooleanBeanProperty` instead", "2.10.0") - type BooleanBeanProperty = scala.beans.BooleanBeanProperty - @deprecated("Use `@scala.beans.ScalaBeanInfo` instead", "2.10.0") - type ScalaBeanInfo = scala.beans.ScalaBeanInfo } /** An exception that indicates an error during Scala reflection */ diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 22dbb37789..86132bb876 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -704,6 +704,14 @@ object Regex { def replace(rs: String) = matcher.appendReplacement(sb, rs) } + /** Quotes strings to be used literally in regex patterns. + * + * All regex metacharacters in the input match themselves literally in the output. + * + * @example {{{List("US$", "CAN$").map(Regex.quote).mkString("|").r}}} + */ + def quote(text: String): String = Pattern quote text + /** Quotes replacement strings to be used in replacement methods. * * Replacement methods give special meaning to backslashes (`\`) and diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 11c1d66190..e66ed9a3d4 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -479,7 +479,7 @@ class Flags extends ModifierFlags { ) @deprecated("Use flagString on the flag-carrying member", "2.10.0") - def flagsToString(flags: Long, privateWithin: String): String = { + private[scala] def flagsToString(flags: Long, privateWithin: String): String = { val access = accessString(flags, privateWithin) val nonAccess = flagsToString(flags & ~AccessFlags) @@ -487,7 +487,7 @@ class Flags extends ModifierFlags { } @deprecated("Use flagString on the flag-carrying member", "2.10.0") - def flagsToString(flags: Long): String = { + private[scala] def flagsToString(flags: Long): String = { // Fast path for common case if (flags == 0L) "" else { var sb: StringBuilder = null diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index b7a1681838..cf3f356daa 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -387,7 +387,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => if (toList forall p) this else newScopeWith(toList filter p: _*) ) - @deprecated("Use `toList.reverse` instead", "2.10.0") + @deprecated("Use `toList.reverse` instead", "2.10.0") // Used in SBT 0.12.4 def reverse: List[Symbol] = toList.reverse override def mkString(start: String, sep: String, end: String) = diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index bed8310767..571c4cfa5d 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -65,9 +65,6 @@ abstract class SymbolTable extends macros.Universe def isPastTyper = false protected def isDeveloper: Boolean = settings.debug - @deprecated("Give us a reason", "2.10.0") - def abort(): Nothing = abort("unknown error") - @deprecated("Use devWarning if this is really a warning; otherwise use log", "2.11.0") def debugwarn(msg: => String): Unit = devWarning(msg) @@ -405,10 +402,9 @@ abstract class SymbolTable extends macros.Universe */ def isCompilerUniverse = false - @deprecated("Use enteringPhase", "2.10.0") + @deprecated("Use enteringPhase", "2.10.0") // Used in SBT 0.12.4 @inline final def atPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph)(op) - @deprecated("Use enteringPhaseNotLaterThan", "2.10.0") - @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = enteringPhaseNotLaterThan(target)(op) + /** * Adds the `sm` String interpolator to a [[scala.StringContext]]. diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index e9230aceee..d0c322ca6b 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -894,7 +894,7 @@ trait Types if (sym == btssym) return mid else if (sym isLess btssym) hi = mid - 1 else if (btssym isLess sym) lo = mid + 1 - else abort() + else abort("sym is neither `sym == btssym`, `sym isLess btssym` nor `btssym isLess sym`") } -1 } @@ -3609,7 +3609,7 @@ trait Types } def genPolyType(params: List[Symbol], tpe: Type): Type = GenPolyType(params, tpe) - @deprecated("use genPolyType(...) instead", "2.10.0") + @deprecated("use genPolyType(...) instead", "2.10.0") // Used in reflection API def polyType(params: List[Symbol], tpe: Type): Type = GenPolyType(params, tpe) /** A creator for anonymous type functions, where the symbol for the type function still needs to be created. diff --git a/src/reflect/scala/reflect/internal/util/Position.scala b/src/reflect/scala/reflect/internal/util/Position.scala index 15cfda26b5..f3eedb88e7 100644 --- a/src/reflect/scala/reflect/internal/util/Position.scala +++ b/src/reflect/scala/reflect/internal/util/Position.scala @@ -202,12 +202,31 @@ private[util] trait InternalPositionImpl { def line: Int = if (hasSource) source.offsetToLine(point) + 1 else 0 def column: Int = if (hasSource) calculateColumn() else 0 def lineContent: String = if (hasSource) source.lineToString(line - 1) else "" - def lineCarat: String = if (hasSource) " " * (column - 1) + "^" else "" - - def showError(msg: String): String = finalPosition match { - case FakePos(fmsg) => s"$fmsg $msg" - case NoPosition => msg - case pos => s"${pos.line}: $msg\n${pos.lineContent}\n${pos.lineCarat}" + def lineCaret: String = if (hasSource) " " * (column - 1) + "^" else "" + @deprecated("use `lineCaret`", since="2.11.0") + def lineCarat: String = lineCaret + + def showError(msg: String): String = { + def escaped(s: String) = { + def u(c: Int) = f"\\u$c%04x" + def uable(c: Int) = (c < 0x20 && c != '\t') || c == 0x7F + if (s exists (c => uable(c))) { + val sb = new StringBuilder + s foreach (c => sb append (if (uable(c)) u(c) else c)) + sb.toString + } else s + } + def errorAt(p: Pos) = { + def where = p.line + def content = escaped(p.lineContent) + def indicator = p.lineCaret + f"$where: $msg%n$content%n$indicator" + } + finalPosition match { + case FakePos(fmsg) => s"$fmsg $msg" + case NoPosition => msg + case pos => errorAt(pos) + } } def showDebug: String = toString def show = ( @@ -239,8 +258,8 @@ private[util] trait InternalPositionImpl { private[util] trait DeprecatedPosition { self: Position => - @deprecated("use `point`", "2.9.0") - def offset: Option[Int] = if (isDefined) Some(point) else None // used by sbt + @deprecated("use `point`", "2.9.0") // Used in SBT 0.12.4 + def offset: Option[Int] = if (isDefined) Some(point) else None @deprecated("use `focus`", "2.11.0") def toSingleLine: Position = this @@ -254,7 +273,7 @@ private[util] trait DeprecatedPosition { @deprecated("use `finalPosition`", "2.11.0") def inUltimateSource(source: SourceFile): Position = source positionInUltimateSource this - @deprecated("use `lineCarat`", "2.11.0") + @deprecated("use `lineCaret`", since="2.11.0") def lineWithCarat(maxWidth: Int): (String, String) = ("", "") @deprecated("Use `withSource(source)` and `withShift`", "2.11.0") diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index 3b6c57e955..9866b043bb 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -16,12 +16,13 @@ import scala.reflect.internal.Chars._ /** abstract base class of a source file used in the compiler */ abstract class SourceFile { - def content : Array[Char] // normalized, must end in SU - def file : AbstractFile - def isLineBreak(idx : Int) : Boolean + def content: Array[Char] // normalized, must end in SU + def file : AbstractFile + def isLineBreak(idx: Int): Boolean + def isEndOfLine(idx: Int): Boolean def isSelfContained: Boolean def length : Int - def position(offset: Int) : Position = { + def position(offset: Int): Position = { assert(offset < length, file + ": " + offset + " >= " + length) Position.offset(this, offset) } @@ -36,8 +37,12 @@ abstract class SourceFile { override def toString() = file.name def path = file.path - def lineToString(index: Int): String = - content drop lineToOffset(index) takeWhile (c => !isLineBreakChar(c.toChar)) mkString "" + def lineToString(index: Int): String = { + val start = lineToOffset(index) + var end = start + while (!isEndOfLine(end)) end += 1 + content.slice(start, end) mkString "" + } @tailrec final def skipWhitespace(offset: Int): Int = @@ -52,6 +57,7 @@ object NoSourceFile extends SourceFile { def content = Array() def file = NoFile def isLineBreak(idx: Int) = false + def isEndOfLine(idx: Int) = false def isSelfContained = true def length = -1 def offsetToLine(offset: Int) = -1 @@ -128,18 +134,24 @@ class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extend super.identifier(pos) } - def isLineBreak(idx: Int) = - if (idx >= length) false else { - val ch = content(idx) - // don't identify the CR in CR LF as a line break, since LF will do. - if (ch == CR) (idx + 1 == length) || (content(idx + 1) != LF) - else isLineBreakChar(ch) - } + private def charAtIsEOL(idx: Int)(p: Char => Boolean) = { + // don't identify the CR in CR LF as a line break, since LF will do. + def notCRLF0 = content(idx) != CR || idx + 1 >= length || content(idx + 1) != LF + + idx < length && notCRLF0 && p(content(idx)) + } + + def isLineBreak(idx: Int) = charAtIsEOL(idx)(isLineBreakChar) + + def isEndOfLine(idx: Int) = charAtIsEOL(idx) { + case CR | LF => true + case _ => false + } def calculateLineIndices(cs: Array[Char]) = { val buf = new ArrayBuffer[Int] buf += 0 - for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1 + for (i <- 0 until cs.length) if (isEndOfLine(i)) buf += i + 1 buf += cs.length // sentinel, so that findLine below works smoother buf.toArray } @@ -149,8 +161,8 @@ class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extend private var lastLine = 0 - /** Convert offset to line in this source file - * Lines are numbered from 0 + /** Convert offset to line in this source file. + * Lines are numbered from 0. */ def offsetToLine(offset: Int): Int = { val lines = lineIndices diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala index 496d5face1..915fd57bf8 100644 --- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala @@ -11,7 +11,6 @@ import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } import scala.tools.nsc.util.ScalaClassLoader import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, PrintWriter, Writer } import java.util.{ Locale } -import java.util.regex.Pattern import java.util.concurrent.ConcurrentLinkedQueue import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, ForwardingJavaFileManager, JavaFileManager, JavaFileObject, @@ -20,6 +19,7 @@ import scala.reflect.io.{ AbstractFile, Directory, File, Path } import scala.io.Source import scala.util.{ Try, Success, Failure } import scala.util.Properties.lineSeparator +import scala.util.matching.Regex import scala.collection.JavaConverters import scala.collection.generic.Clearable import java.net.URL @@ -608,12 +608,12 @@ object JavapClass { // class k, candidate f without prefix def isFunOfClass(k: String, f: String) = { - val p = (s"${Pattern quote k}\\$$+anonfun").r + val p = (s"${Regex quote k}\\$$+anonfun").r (p findPrefixOf f).nonEmpty } // class k, candidate f without prefix, method m def isFunOfMethod(k: String, m: String, f: String) = { - val p = (s"${Pattern quote k}\\$$+anonfun\\$$${Pattern quote m}\\$$").r + val p = (s"${Regex quote k}\\$$+anonfun\\$$${Regex quote m}\\$$").r (p findPrefixOf f).nonEmpty } def isFunOfTarget(k: String, m: Option[String], f: String) = diff --git a/src/repl/scala/tools/nsc/interpreter/Naming.scala b/src/repl/scala/tools/nsc/interpreter/Naming.scala index cf38a2ae3a..e09c6f315e 100644 --- a/src/repl/scala/tools/nsc/interpreter/Naming.scala +++ b/src/repl/scala/tools/nsc/interpreter/Naming.scala @@ -8,6 +8,7 @@ package tools.nsc package interpreter import scala.util.Properties.lineSeparator +import scala.util.matching.Regex /** This is for name logic which is independent of the compiler (notice there's no Global.) * That includes at least generating, metaquoting, mangling, and unmangling. @@ -38,12 +39,10 @@ trait Naming { // // $line3.$read.$iw.$iw.Bippy = // $line3.$read$$iw$$iw$Bippy@4a6a00ca - - private def noMeta(s: String) = "\\Q" + s + "\\E" lazy val lineRegex = { val sn = sessionNames - val members = List(sn.read, sn.eval, sn.print) map noMeta mkString ("(?:", "|", ")") - debugging("lineRegex")(noMeta(sn.line) + """\d+[./]""" + members + """[$.]""") + val members = List(sn.read, sn.eval, sn.print) map Regex.quote mkString ("(?:", "|", ")") + debugging("lineRegex")(Regex.quote(sn.line) + """\d+[./]""" + members + """[$.]""") } private def removeLineWrapper(s: String) = s.replaceAll(lineRegex, "") diff --git a/test/files/jvm/bigints.scala b/test/files/jvm/bigints.scala index f0d05f8b71..06197cbb43 100644 --- a/test/files/jvm/bigints.scala +++ b/test/files/jvm/bigints.scala @@ -31,7 +31,6 @@ object Test_BigDecimal { val xi: BigDecimal = 1 val xd: BigDecimal = 1.0 - val xf: BigDecimal = BigDecimal(1.0f) val xs: BigDecimal = BigDecimal("1.0") val xbi: BigDecimal = BigDecimal(scala.BigInt(1)) diff --git a/test/files/jvm/daemon-actor-termination.scala b/test/files/jvm/daemon-actor-termination.scala index 40acd8583e..9bac6340ba 100644 --- a/test/files/jvm/daemon-actor-termination.scala +++ b/test/files/jvm/daemon-actor-termination.scala @@ -11,7 +11,7 @@ object Test { react { case 'hello => println("MSG1") - reply() + reply(()) react { case 'bye => println("done") diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index cfdcc31ac5..a290af9cd3 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -1,6 +1,3 @@ - - - import scala.concurrent._ import scala.concurrent.duration._ import scala.concurrent.duration.Duration.Inf @@ -518,7 +515,7 @@ class FutureTests extends MinimalScalaTest { } "should not deadlock with nested await (ticket 1313)" in { - val simple = Future() map { + val simple = Future(()) map { _ => val unit = Future(()) val umap = unit map { _ => () } @@ -527,7 +524,7 @@ class FutureTests extends MinimalScalaTest { Await.ready(simple, Inf).isCompleted mustBe (true) val l1, l2 = new TestLatch - val complex = Future() map { + val complex = Future(()) map { _ => blocking { val nested = Future(()) @@ -549,5 +546,3 @@ class FutureTests extends MinimalScalaTest { } } - - diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index 6e5fada381..b55ecc10e6 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -58,7 +58,7 @@ t1513: Array[Null] = Array(null) scala> // ambiguous toString problem from #547 -scala> val atom = new scala.xml.Atom() +scala> val atom = new scala.xml.Atom(()) atom: scala.xml.Atom[Unit] = () scala> // overriding toString problem from #1404 diff --git a/test/files/jvm/interpreter.scala b/test/files/jvm/interpreter.scala index bd1851053f..c68c064c31 100644 --- a/test/files/jvm/interpreter.scala +++ b/test/files/jvm/interpreter.scala @@ -25,7 +25,7 @@ println("hello") // ticket #1513 val t1513 = Array(null) // ambiguous toString problem from #547 -val atom = new scala.xml.Atom() +val atom = new scala.xml.Atom(()) // overriding toString problem from #1404 class S(override val toString : String) val fish = new S("fish") diff --git a/test/files/jvm/try-type-tests.scala b/test/files/jvm/try-type-tests.scala index e5e53ee737..962afbd30f 100644 --- a/test/files/jvm/try-type-tests.scala +++ b/test/files/jvm/try-type-tests.scala @@ -3,139 +3,139 @@ import scala.util.{Try, Success, Failure} // tests the basic combinators on Try trait TryStandard { - def testForeachSuccess(): Unit = { - val t = Success(1) - var res = 0 - t.foreach(x => res = x * 10) - assert(res == 10) - } - - def testForeachFailure(): Unit = { - val t = Failure(new Exception("foo")) - t.foreach(x => assert(false)) - } - - def testFlatMapSuccess(): Unit = { - val t = Success(1) - val n = t.flatMap(x => Try(x * 10)) - assert(n.get == 10) - } - - def testFlatMapFailure(): Unit = { - val t = Failure(new Exception("foo")) - val n = t.flatMap{ x => assert(false); Try() } - } - - def testMapSuccess(): Unit = { - val t = Success(1) - val n = t.map(x => x * 10) - assert(n.get == 10) - } - - def testMapFailure(): Unit = { - val t = Failure(new Exception("foo")) - val n = t.map(x => assert(false)) - } - - def testFilterSuccessTrue(): Unit = { - val t = Success(1) - val n = t.filter(x => x > 0) - assert(n.get == 1) - } - - def testFilterSuccessFalse(): Unit = { - val t = Success(1) - val n = t.filter(x => x < 0) - n match { - case Success(v) => assert(false) - case Failure(e: NoSuchElementException) => assert(true) + def testForeachSuccess(): Unit = { + val t = Success(1) + var res = 0 + t.foreach(x => res = x * 10) + assert(res == 10) + } + + def testForeachFailure(): Unit = { + val t = Failure(new Exception("foo")) + t.foreach(x => assert(false)) + } + + def testFlatMapSuccess(): Unit = { + val t = Success(1) + val n = t.flatMap(x => Try(x * 10)) + assert(n.get == 10) + } + + def testFlatMapFailure(): Unit = { + val t = Failure(new Exception("foo")) + val n = t.flatMap{ x => assert(false); Try(()) } + } + + def testMapSuccess(): Unit = { + val t = Success(1) + val n = t.map(x => x * 10) + assert(n.get == 10) + } + + def testMapFailure(): Unit = { + val t = Failure(new Exception("foo")) + val n = t.map(x => assert(false)) + } + + def testFilterSuccessTrue(): Unit = { + val t = Success(1) + val n = t.filter(x => x > 0) + assert(n.get == 1) + } + + def testFilterSuccessFalse(): Unit = { + val t = Success(1) + val n = t.filter(x => x < 0) + n match { + case Success(v) => assert(false) + case Failure(e: NoSuchElementException) => assert(true) case _ => assert(false) - } - } - - def testFilterFailure(): Unit = { - val t = Failure(new Exception("foo")) - val n = t.filter{ x => assert(false); true } - } - - def testRescueSuccess(): Unit = { - val t = Success(1) - t.recoverWith{ case x => assert(false); Try() } - } - - def testRescueFailure(): Unit = { - val t = Failure(new Exception("foo")) - val n = t.recoverWith{ case x => Try(1) } - assert(n.get == 1) - } - - def testRecoverSuccess(): Unit = { - val t = Success(1) - t.recover{ case x => assert(false); 99 } - } - - def testRecoverFailure(): Unit = { - val t = Failure(new Exception("foo")) - val n = t.recover{ case x => 1 } - assert(n.get == 1) - } - - def testFlattenSuccess(): Unit = { - val f = Failure(new Exception("foo")) - val t = Success(f) - assert(t.flatten == f) - } - - def testFailedSuccess(): Unit = { - val t = Success(1) - val n = t.failed - n match { - case Failure(e: UnsupportedOperationException) => assert(true) - case _ => assert(false) - } - } - - def testFailedFailure(): Unit = { - val t = Failure(new Exception("foo")) - val n = t.failed - n match { - case Success(e: Exception) => assert(true) - case _ => assert(false) - } - } - - def testSuccessTransform(): Unit = { - val s = Success(1) - val succ = (x: Int) => Success(x * 10) - val fail = (x: Throwable) => Success(0) - assert(s.transform(succ, fail).get == 10) - } - - def testFailureTransform(): Unit = { - val f = Failure(new Exception("foo")) - val succ = (x: Int) => Success(x * 10) - val fail = (x: Throwable) => Success(0) - assert(f.transform(succ, fail).get == 0) - } - - testForeachSuccess() - testForeachFailure() - testFlatMapSuccess() - testFlatMapFailure() - testMapSuccess() - testMapFailure() - testFilterSuccessTrue() - testFilterSuccessFalse() - testFilterFailure() - testRescueSuccess() - testRescueFailure() - testRecoverSuccess() - testRecoverFailure() - testFlattenSuccess() - testFailedSuccess() - testFailedFailure() - testSuccessTransform() - testFailureTransform() + } + } + + def testFilterFailure(): Unit = { + val t = Failure(new Exception("foo")) + val n = t.filter{ x => assert(false); true } + } + + def testRescueSuccess(): Unit = { + val t = Success(1) + t.recoverWith{ case x => assert(false); Try(()) } + } + + def testRescueFailure(): Unit = { + val t = Failure(new Exception("foo")) + val n = t.recoverWith{ case x => Try(1) } + assert(n.get == 1) + } + + def testRecoverSuccess(): Unit = { + val t = Success(1) + t.recover{ case x => assert(false); 99 } + } + + def testRecoverFailure(): Unit = { + val t = Failure(new Exception("foo")) + val n = t.recover{ case x => 1 } + assert(n.get == 1) + } + + def testFlattenSuccess(): Unit = { + val f = Failure(new Exception("foo")) + val t = Success(f) + assert(t.flatten == f) + } + + def testFailedSuccess(): Unit = { + val t = Success(1) + val n = t.failed + n match { + case Failure(e: UnsupportedOperationException) => assert(true) + case _ => assert(false) + } + } + + def testFailedFailure(): Unit = { + val t = Failure(new Exception("foo")) + val n = t.failed + n match { + case Success(e: Exception) => assert(true) + case _ => assert(false) + } + } + + def testSuccessTransform(): Unit = { + val s = Success(1) + val succ = (x: Int) => Success(x * 10) + val fail = (x: Throwable) => Success(0) + assert(s.transform(succ, fail).get == 10) + } + + def testFailureTransform(): Unit = { + val f = Failure(new Exception("foo")) + val succ = (x: Int) => Success(x * 10) + val fail = (x: Throwable) => Success(0) + assert(f.transform(succ, fail).get == 0) + } + + testForeachSuccess() + testForeachFailure() + testFlatMapSuccess() + testFlatMapFailure() + testMapSuccess() + testMapFailure() + testFilterSuccessTrue() + testFilterSuccessFalse() + testFilterFailure() + testRescueSuccess() + testRescueFailure() + testRecoverSuccess() + testRecoverFailure() + testFlattenSuccess() + testFailedSuccess() + testFailedFailure() + testSuccessTransform() + testFailureTransform() } object Test diff --git a/test/files/neg/macro-invalidret.check b/test/files/neg/macro-invalidret.check index 99f29e57ce..568cc7c570 100644 --- a/test/files/neg/macro-invalidret.check +++ b/test/files/neg/macro-invalidret.check @@ -20,7 +20,7 @@ Macros_Test_2.scala:7: warning: macro defs must have explicitly specified return ^ Macros_Test_2.scala:14: error: exception during macro expansion: scala.NotImplementedError: an implementation is missing - at scala.Predef$.$qmark$qmark$qmark(Predef.scala:227) + at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225) at Impls$.foo3(Impls_1.scala:7) foo3 diff --git a/test/files/neg/t3403.scala b/test/files/neg/t3403.scala index 8be6ab2a31..7cf0c3e0f7 100644 --- a/test/files/neg/t3403.scala +++ b/test/files/neg/t3403.scala @@ -1,2 +1,2 @@ -import scala.reflect.{BeanProperty => bp} +import scala.beans.{BeanProperty => bp} class Foo { @bp var bar: Int = 1 } diff --git a/test/files/neg/t4851.check b/test/files/neg/t4851.check index 4f2919807e..132dd91b50 100644 --- a/test/files/neg/t4851.check +++ b/test/files/neg/t4851.check @@ -1,10 +1,10 @@ -S.scala:2: warning: Adapting argument list by inserting (): leaky (Object-receiving) target makes this especially dangerous. +S.scala:2: warning: Adaptation of argument list by inserting () has been deprecated: leaky (Object-receiving) target makes this especially dangerous. signature: J(x: Any): J given arguments: <none> after adaptation: new J((): Unit) val x1 = new J ^ -S.scala:3: warning: Adapting argument list by inserting (): leaky (Object-receiving) target makes this especially dangerous. +S.scala:3: warning: Adaptation of argument list by inserting () has been deprecated: leaky (Object-receiving) target makes this especially dangerous. signature: J(x: Any): J given arguments: <none> after adaptation: new J((): Unit) @@ -28,13 +28,13 @@ S.scala:7: warning: Adapting argument list by creating a 3-tuple: this may not b after adaptation: new Some((1, 2, 3): (Int, Int, Int)) val y2 = new Some(1, 2, 3) ^ -S.scala:9: warning: Adapting argument list by inserting (): this is unlikely to be what you want. +S.scala:9: warning: Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want. signature: J2[T](x: T): J2[T] given arguments: <none> after adaptation: new J2((): Unit) val z1 = new J2 ^ -S.scala:10: warning: Adapting argument list by inserting (): this is unlikely to be what you want. +S.scala:10: warning: Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want. signature: J2[T](x: T): J2[T] given arguments: <none> after adaptation: new J2((): Unit) diff --git a/test/files/neg/t4851.flags b/test/files/neg/t4851.flags index 0545cb8b84..ca0d0a0ba3 100644 --- a/test/files/neg/t4851.flags +++ b/test/files/neg/t4851.flags @@ -1 +1 @@ --Ywarn-adapted-args -Xfatal-warnings +-Ywarn-adapted-args -Xfatal-warnings -deprecation diff --git a/test/files/neg/t8015-ffa.check b/test/files/neg/t8015-ffa.check new file mode 100644 index 0000000000..0f28be7fe7 --- /dev/null +++ b/test/files/neg/t8015-ffa.check @@ -0,0 +1,6 @@ +t8015-ffa.scala:7: error: type mismatch; + found : String("3") + required: Int + val i: Int = "3" // error line 7 (was 8) + ^ +one error found diff --git a/test/files/neg/t8015-ffa.scala b/test/files/neg/t8015-ffa.scala new file mode 100644 index 0000000000..60876d9139 --- /dev/null +++ b/test/files/neg/t8015-ffa.scala @@ -0,0 +1,8 @@ + +package foo + +//-------object Next + +trait F { + val i: Int = "3" // error line 7 (was 8) +} diff --git a/test/files/neg/t8015-ffb.check b/test/files/neg/t8015-ffb.check new file mode 100644 index 0000000000..9b2171ea47 --- /dev/null +++ b/test/files/neg/t8015-ffb.check @@ -0,0 +1,6 @@ +t8015-ffb.scala:10: warning: side-effecting nullary methods are discouraged: suggest defining as `def w()` instead + def w = { x\u000c() } // ^L is colored blue on this screen, hardly visible + ^ +error: No warnings can be incurred under -Xfatal-warnings. +one warning found +one error found diff --git a/test/files/neg/t8015-ffb.flags b/test/files/neg/t8015-ffb.flags new file mode 100644 index 0000000000..7949c2afa2 --- /dev/null +++ b/test/files/neg/t8015-ffb.flags @@ -0,0 +1 @@ +-Xlint -Xfatal-warnings diff --git a/test/files/neg/t8015-ffb.scala b/test/files/neg/t8015-ffb.scala new file mode 100644 index 0000000000..dbdd942555 --- /dev/null +++ b/test/files/neg/t8015-ffb.scala @@ -0,0 +1,11 @@ + +trait G { + val c: Char = '\u000a' // disallowed! + def x\u000d\u000a = 9 // as nl + def y() = x + def z() = { + y()\u000a() // was Int does not take parameters + } + def v = y()\u000c() // was Int does not take parameters + def w = { x() } // ^L is colored blue on this screen, hardly visible +} diff --git a/test/files/neg/t8035-deprecated.check b/test/files/neg/t8035-deprecated.check new file mode 100644 index 0000000000..01f27e5310 --- /dev/null +++ b/test/files/neg/t8035-deprecated.check @@ -0,0 +1,21 @@ +t8035-deprecated.scala:2: warning: Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want. + signature: GenSetLike.apply(elem: A): Boolean + given arguments: <none> + after adaptation: GenSetLike((): Unit) + List(1,2,3).toSet() + ^ +t8035-deprecated.scala:5: warning: Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want. + signature: A(x: T): Foo.A[T] + given arguments: <none> + after adaptation: new A((): Unit) + new A + ^ +t8035-deprecated.scala:9: warning: Adaptation of argument list by inserting () has been deprecated: leaky (Object-receiving) target makes this especially dangerous. + signature: Format.format(x$1: Any): String + given arguments: <none> + after adaptation: Format.format((): Unit) + sdf.format() + ^ +error: No warnings can be incurred under -Xfatal-warnings. +three warnings found +one error found diff --git a/test/files/neg/t8035-deprecated.flags b/test/files/neg/t8035-deprecated.flags new file mode 100644 index 0000000000..c6bfaf1f64 --- /dev/null +++ b/test/files/neg/t8035-deprecated.flags @@ -0,0 +1 @@ +-deprecation -Xfatal-warnings diff --git a/test/files/neg/t8035-deprecated.scala b/test/files/neg/t8035-deprecated.scala new file mode 100644 index 0000000000..6423157530 --- /dev/null +++ b/test/files/neg/t8035-deprecated.scala @@ -0,0 +1,10 @@ +object Foo { + List(1,2,3).toSet() + + class A[T](val x: T) + new A + + import java.text.SimpleDateFormat + val sdf = new SimpleDateFormat("yyyyMMdd-HH0000") + sdf.format() +} diff --git a/test/files/neg/t8035-removed.check b/test/files/neg/t8035-removed.check new file mode 100644 index 0000000000..e24a0b4e63 --- /dev/null +++ b/test/files/neg/t8035-removed.check @@ -0,0 +1,16 @@ +t8035-removed.scala:2: error: Adaptation of argument list by inserting () has been removed. + signature: GenSetLike.apply(elem: A): Boolean + given arguments: <none> + List(1,2,3).toSet() + ^ +t8035-removed.scala:5: error: Adaptation of argument list by inserting () has been removed. + signature: A(x: T): Foo.A[T] + given arguments: <none> + new A + ^ +t8035-removed.scala:9: error: Adaptation of argument list by inserting () has been removed. + signature: Format.format(x$1: Any): String + given arguments: <none> + sdf.format() + ^ +three errors found diff --git a/test/files/neg/t8035-removed.flags b/test/files/neg/t8035-removed.flags new file mode 100644 index 0000000000..29f4ede37a --- /dev/null +++ b/test/files/neg/t8035-removed.flags @@ -0,0 +1 @@ +-Xfuture diff --git a/test/files/neg/t8035-removed.scala b/test/files/neg/t8035-removed.scala new file mode 100644 index 0000000000..6423157530 --- /dev/null +++ b/test/files/neg/t8035-removed.scala @@ -0,0 +1,10 @@ +object Foo { + List(1,2,3).toSet() + + class A[T](val x: T) + new A + + import java.text.SimpleDateFormat + val sdf = new SimpleDateFormat("yyyyMMdd-HH0000") + sdf.format() +} diff --git a/test/files/run/bigDecimalTest.check b/test/files/run/bigDecimalTest.check index 6d11c23fcd..36db6aaafe 100644 --- a/test/files/run/bigDecimalTest.check +++ b/test/files/run/bigDecimalTest.check @@ -3,4 +3,4 @@ 0 0 0 -14 +15 diff --git a/test/files/run/is-valid-num.scala b/test/files/run/is-valid-num.scala index d314015dd4..65e8ceeca6 100644 --- a/test/files/run/is-valid-num.scala +++ b/test/files/run/is-valid-num.scala @@ -19,25 +19,27 @@ object Test { assert(!x.isValidChar, x) assert(!x.isValidShort, x) assert(!x.isValidByte, x) -// assert(y.isWhole, y) + assert(y.isWhole, y) assert(!y.isValidShort, y) assert(y.isValidChar, y) assert(y.isValidInt, y) - assert(y.isValidFloat, y) - assert(y.isValidDouble, y) + assert(y.isDecimalFloat, y) + assert(y.isDecimalDouble, y) assert(y.isValidLong, y) assert(!y.isValidByte, y) -// assert(!y1.isWhole) + assert(!y1.isWhole) assert(!y1.isValidLong, y1) - assert(!y1.isValidFloat, y1) - assert(!y1.isValidDouble, y1) + assert(y1.isDecimalFloat, y1) + assert(y1.isDecimalDouble, y1) + assert(!y1.isExactFloat, y1) + assert(!y1.isExactDouble, y1) assert(!y1.isValidInt, y1) assert(!y1.isValidChar, y1) assert(!y1.isValidShort, y1) assert(!y1.isValidByte, y1) assert(!y2.isValidLong, y2) - assert(y2.isValidFloat, y2) - assert(y2.isValidDouble, y2) + assert(y2.isExactFloat, y2) + assert(y2.isExactDouble, y2) assert(!l1.isValidInt && (l1 - 1).isValidInt, l1) assert(!l2.isValidInt && (l2 + 1).isValidInt, l2) @@ -170,8 +172,8 @@ object Test { if (!d.isInfinity) { val bd = BigDecimal(new java.math.BigDecimal(d)) // assert(!bd.isWhole, bd) - assert(bd.isValidDouble, bd) - assert(bd.isValidFloat == isFloat, bd) + assert(bd.isExactDouble, bd) + assert(bd.isExactFloat == isFloat, bd) assert(!bd.isValidLong, bd) assert(!bd.isValidInt, bd) assert(!bd.isValidChar, bd) @@ -210,9 +212,9 @@ object Test { val isFloat = !bi.toFloat.isInfinity && bd.compare(BigDecimal(new java.math.BigDecimal(bi.toFloat))) == 0 val isDouble = !bi.toDouble.isInfinity && bd.compare(BigDecimal(new java.math.BigDecimal(bi.toDouble))) == 0 -// assert(bd.isWhole, bd) - assert(bd.isValidDouble == isDouble, bd) - assert(bd.isValidFloat == isFloat, bd) + assert(bd.isWhole, bd) + assert(bd.isBinaryDouble == isDouble, bd) + assert(bd.isBinaryFloat == isFloat, bd) assert(bd.isValidLong == isLong, bd) assert(bd.isValidInt == isInt, bd) assert(bd.isValidChar == isChar, bd) diff --git a/test/files/run/numbereq.scala b/test/files/run/numbereq.scala index d50db6d049..7ce4b23cf8 100644 --- a/test/files/run/numbereq.scala +++ b/test/files/run/numbereq.scala @@ -31,6 +31,24 @@ object Test { ).flatten } + // Don't necessarily expect BigDecimal created from BigInt to agree with Double here. + def isIffy(x: Any, y: Any, canSwap: Boolean = true): Boolean = x match { + case bd: BigDecimal => y match { + case _: Float | _: Double => bd.toString.length > 15 + case _ => false + } + case _ => canSwap && isIffy(y, x, false) + } + + // Don't necessarily expect BigInt to agree with Float/Double beyond a Long + def isIffyB(x: Any, y: Any, canSwap: Boolean = true): Boolean = x match { + case bi: BigInt => y match { + case _: Float | _: Double => bi < Long.MinValue || bi > Long.MaxValue + case _ => false + } + case _ => canSwap && isIffyB(y, x, false) + } + def main(args: Array[String]): Unit = { val ints = (0 to 15).toList map (Short.MinValue >> _) val ints2 = ints map (x => -x) @@ -46,7 +64,6 @@ object Test { val sets = setneg1 ++ setneg2 ++ List(zero) ++ setpos1 ++ setpos2 for (set <- sets ; x <- set ; y <- set) { - // println("'%s' == '%s' (%s == %s) (%s == %s)".format(x, y, x.hashCode, y.hashCode, x.##, y.##)) assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) assert(x.## == y.##, "%s != %s".format(x.getClass, y.getClass)) } @@ -64,9 +81,11 @@ object Test { val sets2 = setneg1 ++ setneg1b ++ setneg2 ++ setneg2b ++ List(zero) ++ setpos1 ++ setpos1b ++ setpos2 ++ setpos2b for (set <- sets2 ; x <- set ; y <- set) { -// println("'%s' == '%s' (%s == %s) (%s == %s)".format(x, y, x.hashCode, y.hashCode, x.##, y.##)) - assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) -// assert(x.## == y.##, "%s != %s".format(x.getClass, y.getClass)) Disable until Double.## is fixed (SI-5640) + if (!isIffy(x,y)) { + assert(x == y, "%s/%s != %s/%s".format(x, x.getClass, y, y.getClass)) + // The following is blocked by SI-8150 + // if (!isIffyB(x,y)) assert(x.## == y.##, "%x/%s != %x/%s from %s.## and %s.##".format(x.##, x.getClass, y.##, y.getClass, x, y)) + } } } } diff --git a/test/files/run/partialfun.scala b/test/files/run/partialfun.scala index f3c53b94ae..71c7d3e61c 100644 --- a/test/files/run/partialfun.scala +++ b/test/files/run/partialfun.scala @@ -76,7 +76,7 @@ object Test { } val chained = pf0 orElse pf1 orElse pf2 - chained() + chained(()) } def main(args: Array[String]): Unit = { diff --git a/test/files/run/t5629b.check b/test/files/run/t5629b.check index 1bc0248c3d..e0f25f0b05 100644 --- a/test/files/run/t5629b.check +++ b/test/files/run/t5629b.check @@ -2,7 +2,7 @@ MySmartPF.apply entered... newPF.applyOrElse entered... default -scala.MatchError: () (of class scala.runtime.BoxedUnit) +scala.MatchError: 1 (of class java.lang.Integer) === pf(42): MySmartPF.apply entered... newPF.applyOrElse entered... diff --git a/test/files/run/t5629b.scala b/test/files/run/t5629b.scala index 9ff29c8d89..5d402201ae 100644 --- a/test/files/run/t5629b.scala +++ b/test/files/run/t5629b.scala @@ -1,8 +1,3 @@ - - - - - object Test extends App { trait MyPF[@specialized(Int) -A] extends (A => Unit) { @@ -16,7 +11,7 @@ object Test extends App { trait MySmartPF[@specialized(Int) -A] extends MyPF[A] { def apply(x: A): Unit = { println("MySmartPF.apply entered...") - applyOrElse(x, { _: Any => throw new MatchError }) + applyOrElse(x, { default: Any => throw new MatchError(default) }) } } diff --git a/test/files/run/t576.check b/test/files/run/t576.check index 8a1218a102..6458d5d743 100644 --- a/test/files/run/t576.check +++ b/test/files/run/t576.check @@ -1,3 +1,4 @@ +warning: there were 1 deprecation warning(s); re-run with -deprecation for details 1 2 3 diff --git a/test/files/run/t6662/Test_2.scala b/test/files/run/t6662/Test_2.scala index 03a80b655a..82ac54cb46 100644 --- a/test/files/run/t6662/Test_2.scala +++ b/test/files/run/t6662/Test_2.scala @@ -2,7 +2,7 @@ object Test { def main(args: Array[String]) { - val s = Demo id () + val s = Demo id (()) println(s) } } diff --git a/test/files/run/t6863.check b/test/files/run/t6863.check index 030cb8b265..fea22b582f 100644 --- a/test/files/run/t6863.check +++ b/test/files/run/t6863.check @@ -10,3 +10,4 @@ t6863.scala:46: warning: comparing values of types Unit and Unit using `==' will t6863.scala:59: warning: comparing values of types Unit and Unit using `==' will always yield true assert({ () => x }.apply == ()) ^ +warning: there were 4 deprecation warning(s); re-run with -deprecation for details diff --git a/test/files/run/t6935.check b/test/files/run/t6935.check new file mode 100644 index 0000000000..844ca54682 --- /dev/null +++ b/test/files/run/t6935.check @@ -0,0 +1 @@ +warning: there were 1 deprecation warning(s); re-run with -deprecation for details diff --git a/test/files/run/t6935.scala b/test/files/run/t6935.scala index dea2d7f2e2..fdaf02e5ce 100644 --- a/test/files/run/t6935.scala +++ b/test/files/run/t6935.scala @@ -1,14 +1,14 @@ object Test { def main(args: Array[String]): Unit = { - import java.io._ - val bytes = new ByteArrayOutputStream() - val out = new ObjectOutputStream(bytes) - out.writeObject(()) - out.close() - val buf = bytes.toByteArray - val in = new ObjectInputStream(new ByteArrayInputStream(buf)) - val unit = in.readObject() - assert(unit == ()) + import java.io._ + val bytes = new ByteArrayOutputStream() + val out = new ObjectOutputStream(bytes) + out.writeObject(()) + out.close() + val buf = bytes.toByteArray + val in = new ObjectInputStream(new ByteArrayInputStream(buf)) + val unit = in.readObject() + assert(unit == ()) } } diff --git a/test/files/run/t8015-ffc.scala b/test/files/run/t8015-ffc.scala new file mode 100644 index 0000000000..fe6781be42 --- /dev/null +++ b/test/files/run/t8015-ffc.scala @@ -0,0 +1,7 @@ + +object Test extends App { + val ms = """This is a long multiline string + with \u000d\u000a CRLF embedded.""" + assert(ms.lines.size == 3, s"lines.size ${ms.lines.size}") + assert(ms contains "\r\n CRLF", "no CRLF") +} diff --git a/test/files/run/t8100.check b/test/files/run/t8100.check new file mode 100644 index 0000000000..cdd927fd88 --- /dev/null +++ b/test/files/run/t8100.check @@ -0,0 +1 @@ +Success(0) diff --git a/test/files/run/t8100.scala b/test/files/run/t8100.scala new file mode 100644 index 0000000000..b9d0fe5003 --- /dev/null +++ b/test/files/run/t8100.scala @@ -0,0 +1,8 @@ +object Test { + import scala.util.Try + + def main(args: Array[String]): Unit = { + def stream = Stream.from(0).take(100000).map(n => None) + println(Try(stream.flatten.length)) + } +} diff --git a/test/junit/scala/math/BigDecimalTest.scala b/test/junit/scala/math/BigDecimalTest.scala new file mode 100644 index 0000000000..d1ba96fcc8 --- /dev/null +++ b/test/junit/scala/math/BigDecimalTest.scala @@ -0,0 +1,225 @@ +package scala.math + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import java.math.{BigDecimal => BD, MathContext => MC} + +/* Tests various maps by making sure they all agree on the same answers. */ +@RunWith(classOf[JUnit4]) +class BigDecimalTest { + + // Motivated by SI-6173: BigDecimal#isWhole implementation is very heap intensive + @Test + def isWholeTest() { + val wholes = List( + BigDecimal(1), + BigDecimal(10L), + BigDecimal(14.000), + BigDecimal(new BD("19127981892347012385719827340123471923850195")), + BigDecimal("1e1000000000"), + BigDecimal(14.1928857191985e22), + BigDecimal(14.12519823759817, new MC(2)) + ) + val fracs = List( + BigDecimal(0.1), + BigDecimal(new BD("1.000000000000000000000000000000000001")), + BigDecimal(new BD("275712375971892375127591745810580123751.99999")), + BigDecimal("14.19238571927581e6"), + BigDecimal("912834718237510238591285")/2 + ) + assert(wholes.forall(_.isWhole) && fracs.forall(! _.isWhole)) + } + + // Motivated by SI-6699: BigDecimal.isValidDouble behaves unexpectedly + @Test + def isValidDoubleTest() { + val valids = List( + BigDecimal(1), + BigDecimal(19571.125), + BigDecimal.decimal(0.1), + BigDecimal(1e15) + ) + val invalids = List( + BigDecimal(new BD("1.0000000000000000000000000000000000000000001")), + BigDecimal("10e1000000"), + BigDecimal("10e-1000000") + ) + assert( + valids.forall(_.isDecimalDouble) && + invalids.forall(! _.isDecimalDouble) + ) + } + + // Motivated by SI-6173: BigDecimal#isWhole implementation is very heap intensive + @Test + def doesNotExplodeTest() { + val troublemaker = BigDecimal("1e1000000000") + val reasonable = BigDecimal("1e1000") + val reasonableInt = reasonable.toBigInt + assert( + reasonable.hashCode == reasonableInt.hashCode && + reasonable == reasonableInt && + reasonableInt == reasonable && + troublemaker.hashCode != reasonable.hashCode && + !(troublemaker == reasonableInt) && + !(reasonableInt == troublemaker) + ) + } + + // Motivated by SI-6456: scala.math.BigDecimal should not accept a null value + @Test + def refusesNullTest() { + def isIAE[A](a: => A) = try { a; false } catch { case iae: IllegalArgumentException => true } + def isNPE[A](a: => A) = try { a; false } catch { case npe: NullPointerException => true } + assert( + isIAE(new BigDecimal(null: BD, new MC(2))) && + isIAE(new BigDecimal(new BD("5.7"), null: MC)) && + isNPE(BigDecimal(null: BigInt)) && + isNPE(BigDecimal(null: String)) && + isNPE(BigDecimal(null: Array[Char])) + ) + } + + // Motivated by SI-6153: BigDecimal.hashCode() has high collision rate + @Test + def hashCodesAgreeTest() { + val bi: BigInt = 100000 + val bd: BigDecimal = 100000 + val l: Long = 100000 + val d: Double = 100000 + assert( + d.## == l.## && + l.## == bd.## && + bd.## == bi.## && + (bd pow 4).hashCode == (bi pow 4).hashCode && + BigDecimal("1e150000").hashCode != BigDecimal("1e150000").toBigInt.hashCode + ) + } + + // Motivated by noticing BigDecimal(0.1f) != BigDecimal(0.1) + @Test + def consistentTenthsTest() { + def tenths = List[Any]( + BigDecimal("0.1"), + 0.1, + BigDecimal.decimal(0.1f), + BigDecimal.decimal(0.1), + BigDecimal(0.1), + BigDecimal(BigInt(1), 1), + BigDecimal(new BD("0.1")), + BigDecimal(1L, 1), + BigDecimal(1) / BigDecimal(10), + BigDecimal(10).pow(-1) + ) + for (a <- tenths; b <- tenths) assert(a == b, s"$a != $b but both should be 0.1") + } + + // Motivated by noticing BigDecimal(123456789, mc6) != BigDecimal(123456789L, mc6) + // where mc6 is a MathContext that rounds to six digits + @Test + def consistentRoundingTest() { + val mc6 = new MC(6) + val sameRounding = List( + List( + 123457000, + 123457000L, + 123457e3, + BigDecimal(123456789, mc6), + BigDecimal(123456789L, mc6), + BigDecimal(123456789d, mc6), + BigDecimal("123456789", mc6), + BigDecimal(Array('1','2','3','4','5','6','7','8','9'), mc6), + BigDecimal(BigInt(123456789), mc6), + BigDecimal(BigInt(1234567890), 1, mc6), + BigDecimal.decimal(123456789, mc6), + BigDecimal.decimal(123456789d, mc6), + BigDecimal.decimal(new BD("123456789"), mc6) + ), + List( + 123456789, + 123456789L, + 123456789d, + new BigDecimal(new BD("123456789"), mc6), + new BigDecimal(new BD("123456789")), + BigDecimal(123456789), + BigDecimal(123456789L), + BigDecimal(123456789d), + BigDecimal("123456789"), + BigDecimal(Array('1','2','3','4','5','6','7','8','9')), + BigDecimal(BigInt(123456789)), + BigDecimal(BigInt(1234567890), 1), + BigDecimal.decimal(123456789), + BigDecimal.decimal(123456789d), + BigDecimal.valueOf(123456789d, mc6) + ) + ) + sameRounding.map(_.zipWithIndex).foreach{ case xs => + for ((a,i) <- xs; (b,j) <- xs) { + assert(a == b, s"$a != $b (#$i != #$j) but should be the same") + assert(a.## == b.##, s"Hash code mismatch in equal BigDecimals: #$i != #$j") + } + } + val List(xs, ys) = sameRounding.map(_.zipWithIndex) + for ((a,i) <- xs; (b,j) <- ys) assert(a != b, s"$a == $b (#$i == #$j) but should be different") + } + + // This was unexpectedly truncated in 2.10 + @Test + def noPrematureRoundingTest() { + val text = "9791375983750284059237954823745923845928547807345082378340572986452364" + val same = List[Any]( + BigInt(text), BigDecimal(text), BigDecimal(new BD(text)) + ) + for (a <- same; b <- same) assert(a == b, s"$a != $b but should be the same") + } + + // Tests attempts to make sane the representation of IEEE binary32 and binary64 + // (i.e. Float and Double) with Scala's text-is-King BigDecimal policy + @Test + def churnRepresentationTest() { + val rn = new scala.util.Random(42) + for (i <- 1 to 1000) { + val d = rn.nextDouble + assert({ + BigDecimal.decimal(d).isDecimalDouble && + BigDecimal.binary(d).isBinaryDouble && + BigDecimal.exact(d).isExactDouble + }, s"At least one wrong BigDecimal representation for $d") + } + for (i <- 1 to 1000) { + val f = rn.nextFloat + assert({ + BigDecimal.decimal(f).isDecimalFloat && + BigDecimal.binary(f).isBinaryFloat && + BigDecimal.exact(f).isExactFloat + }, s"At least one wrong BigDecimal representation for $f") + } + for (i <- 1 to 1000) { + val ndig = 15+rn.nextInt(5) + val s = Array.fill(ndig)((rn.nextInt(10)+'0').toChar).mkString + val bi = BigInt(s) + val l = bi.toLong + val d = bi.toDouble + val bd = BigDecimal(bi) + val bd2 = BigDecimal.decimal(d) + assert(!bi.isValidLong || bi == l, s"Should be invalid or equal: $bi $l") + assert(!bi.isValidDouble || bi == d, s"Should be invalid or equal: $bi $d") + assert(bd == bi, s"Should be equal $bi $bd") + assert(bd.## == bi.##, s"Hash codes for $bi, $bd should be equal") + assert(bd == bd2 || bd2 != BigDecimal.exact(d) || !bi.isValidDouble, + s"$bd != $bd2 should only be when inexact or invalid") + assert(d == bd2 && bd2 == d, s"$d != $bd2 but they should equal") + } + val different = List( + BigDecimal.decimal(0.1), + BigDecimal.binary(0.1), + BigDecimal.binary(0.1, new MC(25)), + BigDecimal.exact(0.1), + BigDecimal.exact(0.1f), + BigDecimal.decimal((0.1f).toDouble) + ) + for (a <- different; b <- different if (a ne b)) + assert(a != b, "BigDecimal representations of Double mistakenly conflated") + } +} |