text:
package org.apache.spark.sql.catalyst.analysis

import java.sql.Timestamp

import org.apache.spark.sql.catalyst.analysis.TypeCoercion._
import org.apache.spark.sql.catalyst.dsl.expressions._
import org.apache.spark.sql.catalyst.dsl.plans._
import org.apache.spark.sql.catalyst.expressions._
import org.apache.spark.sql.catalyst.plans.logical._
import org.apache.spark.sql.catalyst.rules.{Rule, RuleExecutor}
import org.apache.spark.sql.internal.SQLConf
import org.apache.spark.sql.types._

class TypeCoercionSuite extends AnalysisTest {
  import TypeCoercionSuite._

  // scalastyle:off line.size.limit
  // The following table shows all implicit data type conversions that are not visible to the user.
  // +----------------------+----------+-----------+-------------+----------+------------+-----------+------------+------------+-------------+------------+----------+---------------+------------+----------+-------------+----------+----------------------+---------------------+-------------+--------------+
  // | Source Type\CAST TO  | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | BinaryType | BooleanType | StringType | DateType | TimestampType | ArrayType  | MapType  | StructType  | NullType | CalendarIntervalType |     DecimalType     | NumericType | IntegralType |
  // +----------------------+----------+-----------+-------------+----------+------------+-----------+------------+------------+-------------+------------+----------+---------------+------------+----------+-------------+----------+----------------------+---------------------+-------------+--------------+
  // | ByteType             | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | X          | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | DecimalType(3, 0)   | ByteType    | ByteType     |
  // | ShortType            | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | X          | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | DecimalType(5, 0)   | ShortType   | ShortType    |
  // | IntegerType          | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | X          | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | DecimalType(10, 0)  | IntegerType | IntegerType  |
  // | LongType             | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | X          | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | DecimalType(20, 0)  | LongType    | LongType     |
  // | DoubleType           | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | X          | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | DecimalType(30, 15) | DoubleType  | IntegerType  |
  // | FloatType            | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | X          | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | DecimalType(14, 7)  | FloatType   | IntegerType  |
  // | Dec(10, 2)           | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | X          | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | DecimalType(10, 2)  | Dec(10, 2)  | IntegerType  |
  // | BinaryType           | X        | X         | X           | X        | X          | X         | X          | BinaryType | X           | StringType | X        | X             | X          | X        | X           | X        | X                    | X                   | X           | X            |
  // | BooleanType          | X        | X         | X           | X        | X          | X         | X          | X          | BooleanType | StringType | X        | X             | X          | X        | X           | X        | X                    | X                   | X           | X            |
  // | StringType           | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | BinaryType | X           | StringType | DateType | TimestampType | X          | X        | X           | X        | X                    | DecimalType(38, 18) | DoubleType  | X            |
  // | DateType             | X        | X         | X           | X        | X          | X         | X          | X          | X           | StringType | DateType | TimestampType | X          | X        | X           | X        | X                    | X                   | X           | X            |
  // | TimestampType        | X        | X         | X           | X        | X          | X         | X          | X          | X           | StringType | DateType | TimestampType | X          | X        | X           | X        | X                    | X                   | X           | X            |
  // | ArrayType            | X        | X         | X           | X        | X          | X         | X          | X          | X           | X          | X        | X             | ArrayType* | X        | X           | X        | X                    | X                   | X           | X            |
  // | MapType              | X        | X         | X           | X        | X          | X         | X          | X          | X           | X          | X        | X             | X          | MapType* | X           | X        | X                    | X                   | X           | X            |
  // | StructType           | X        | X         | X           | X        | X          | X         | X          | X          | X           | X          | X        | X             | X          | X        | StructType* | X        | X                    | X                   | X           | X            |
  // | NullType             | ByteType | ShortType | IntegerType | LongType | DoubleType | FloatType | Dec(10, 2) | BinaryType | BooleanType | StringType | DateType | TimestampType | ArrayType  | MapType  | StructType  | NullType | CalendarIntervalType | DecimalType(38, 18) | DoubleType  | IntegerType  |
  // | CalendarIntervalType | X        | X         | X           | X        | X          | X         | X          | X          | X           | X          | X        | X             | X          | X        | X           | X        | CalendarIntervalType | X                   | X           | X            |
  // +----------------------+----------+-----------+-------------+----------+------------+-----------+------------+------------+-------------+------------+----------+---------------+------------+----------+-------------+----------+----------------------+---------------------+-------------+--------------+
  // Note: StructType* is castable when all the internal child types are castable according to the table.
  // Note: ArrayType* is castable when the element type is castable according to the table.
  // Note: MapType* is castable when both the key type and the value type are castable according to the table.
  // scalastyle:on line.size.limit

  private def shouldCast(from: DataType, to: AbstractDataType, expected: DataType): Unit = {
    // Check default value
    val castDefault = TypeCoercion.ImplicitTypeCasts.implicitCast(default(from), to)
    assert(DataType.equalsIgnoreCompatibleNullability(
      castDefault.map(_.dataType).getOrElse(null), expected),
      s"Failed to cast $from to $to")

    // Check null value
    val castNull = TypeCoercion.ImplicitTypeCasts.implicitCast(createNull(from), to)
    assert(DataType.equalsIgnoreCaseAndNullability(
      castNull.map(_.dataType).getOrElse(null), expected),
      s"Failed to cast $from to $to")
  }

  private def shouldNotCast(from: DataType, to: AbstractDataType): Unit = {
    // Check default value
    val castDefault = TypeCoercion.ImplicitTypeCasts.implicitCast(default(from), to)
    assert(castDefault.isEmpty, s"Should not be able to cast $from to $to, but got $castDefault")

    // Check null value
    val castNull = TypeCoercion.ImplicitTypeCasts.implicitCast(createNull(from), to)
    assert(castNull.isEmpty, s"Should not be able to cast $from to $to, but got $castNull")
  }

  private def default(dataType: DataType): Expression = dataType match {
    case ArrayType(internalType: DataType, _) =>
      CreateArray(Seq(Literal.default(internalType)))
    case MapType(keyDataType: DataType, valueDataType: DataType, _) =>
      CreateMap(Seq(Literal.default(keyDataType), Literal.default(valueDataType)))
    case _ => Literal.default(dataType)
  }

  private def createNull(dataType: DataType): Expression = dataType match {
    case ArrayType(internalType: DataType, _) =>
      CreateArray(Seq(Literal.create(null, internalType)))
    case MapType(keyDataType: DataType, valueDataType: DataType, _) =>
      CreateMap(Seq(Literal.create(null, keyDataType), Literal.create(null, valueDataType)))
    case _ => Literal.create(null, dataType)
  }

  // Check whether the type `checkedType` can be cast to all the types in `castableTypes`,
  // but cannot be cast to the other types in `allTypes`.
  private def checkTypeCasting(checkedType: DataType, castableTypes: Seq[DataType]): Unit = {
    val nonCastableTypes = allTypes.filterNot(castableTypes.contains)

    castableTypes.foreach { tpe =>
      shouldCast(checkedType, tpe, tpe)
    }
    nonCastableTypes.foreach { tpe =>
      shouldNotCast(checkedType, tpe)
    }
  }

  private def checkWidenType(
      widenFunc: (DataType, DataType) => Option[DataType],
      t1: DataType,
      t2: DataType,
      expected: Option[DataType],
      isSymmetric: Boolean = true): Unit = {
    var found = widenFunc(t1, t2)
    assert(found == expected,
      s"Expected $expected as wider common type for $t1 and $t2, found $found")
    // Test both directions to make sure the widening is symmetric.
    if (isSymmetric) {
      found = widenFunc(t2, t1)
      assert(found == expected,
        s"Expected $expected as wider common type for $t2 and $t1, found $found")
    }
  }

  test("implicit type cast - ByteType") {
    val checkedType = ByteType
    checkTypeCasting(checkedType, castableTypes = numericTypes ++ Seq(StringType))
    shouldCast(checkedType, DecimalType, DecimalType.ByteDecimal)
    shouldCast(checkedType, NumericType, checkedType)
    shouldCast(checkedType, IntegralType, checkedType)
  }

  test("implicit type cast - ShortType") {
    val checkedType = ShortType
    checkTypeCasting(checkedType, castableTypes = numericTypes ++ Seq(StringType))
    shouldCast(checkedType, DecimalType, DecimalType.ShortDecimal)
    shouldCast(checkedType, NumericType, checkedType)
    shouldCast(checkedType, IntegralType, checkedType)
  }

  test("implicit type cast - IntegerType") {
    val checkedType = IntegerType
    checkTypeCasting(checkedType, castableTypes = numericTypes ++ Seq(StringType))
    shouldCast(IntegerType, DecimalType, DecimalType.IntDecimal)
    shouldCast(checkedType, NumericType, checkedType)
    shouldCast(checkedType, IntegralType, checkedType)
  }

  test("implicit type cast - LongType") {
    val checkedType = LongType
    checkTypeCasting(checkedType, castableTypes = numericTypes ++ Seq(StringType))
    shouldCast(checkedType, DecimalType, DecimalType.LongDecimal)
    shouldCast(checkedType, NumericType, checkedType)
    shouldCast(checkedType, IntegralType, checkedType)
  }

  test("implicit type cast - FloatType") {
    val checkedType = FloatType
    checkTypeCasting(checkedType, castableTypes = numericTypes ++ Seq(StringType))
    shouldCast(checkedType, DecimalType, DecimalType.FloatDecimal)
    shouldCast(checkedType, NumericType, checkedType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - DoubleType") {
    val checkedType = DoubleType
    checkTypeCasting(checkedType, castableTypes = numericTypes ++ Seq(StringType))
    shouldCast(checkedType, DecimalType, DecimalType.DoubleDecimal)
    shouldCast(checkedType, NumericType, checkedType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - DecimalType(10, 2)") {
    val checkedType = DecimalType(10, 2)
    checkTypeCasting(checkedType, castableTypes = numericTypes ++ Seq(StringType))
    shouldCast(checkedType, DecimalType, checkedType)
    shouldCast(checkedType, NumericType, checkedType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - BinaryType") {
    val checkedType = BinaryType
    checkTypeCasting(checkedType, castableTypes = Seq(checkedType, StringType))
    shouldNotCast(checkedType, DecimalType)
    shouldNotCast(checkedType, NumericType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - BooleanType") {
    val checkedType = BooleanType
    checkTypeCasting(checkedType, castableTypes = Seq(checkedType, StringType))
    shouldNotCast(checkedType, DecimalType)
    shouldNotCast(checkedType, NumericType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - StringType") {
    val checkedType = StringType
    val nonCastableTypes =
      complexTypes ++ Seq(BooleanType, NullType, CalendarIntervalType)
    checkTypeCasting(checkedType, castableTypes = allTypes.filterNot(nonCastableTypes.contains))
    shouldCast(checkedType, DecimalType, DecimalType.SYSTEM_DEFAULT)
    shouldCast(checkedType, NumericType, NumericType.defaultConcreteType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - DateType") {
    val checkedType = DateType
    checkTypeCasting(checkedType, castableTypes = Seq(checkedType, StringType, TimestampType))
    shouldNotCast(checkedType, DecimalType)
    shouldNotCast(checkedType, NumericType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - TimestampType") {
    val checkedType = TimestampType
    checkTypeCasting(checkedType, castableTypes = Seq(checkedType, StringType, DateType))
    shouldNotCast(checkedType, DecimalType)
    shouldNotCast(checkedType, NumericType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - ArrayType(StringType)") {
    val checkedType = ArrayType(StringType)
    val nonCastableTypes =
      complexTypes ++ Seq(BooleanType, NullType, CalendarIntervalType)
    checkTypeCasting(checkedType,
      castableTypes = allTypes.filterNot(nonCastableTypes.contains).map(ArrayType(_)))
    nonCastableTypes.map(ArrayType(_)).foreach(shouldNotCast(checkedType, _))
    shouldNotCast(ArrayType(DoubleType, containsNull = false),
      ArrayType(LongType, containsNull = false))
    shouldNotCast(checkedType, DecimalType)
    shouldNotCast(checkedType, NumericType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast between two Map types") {
    val sourceType = MapType(IntegerType, IntegerType, true)
    val castableTypes = numericTypes ++ Seq(StringType).filter(!Cast.forceNullable(IntegerType, _))
    val targetTypes = numericTypes.filter(!Cast.forceNullable(IntegerType, _)).map { t =>
      MapType(t, sourceType.valueType, valueContainsNull = true)
    }
    val nonCastableTargetTypes = allTypes.filterNot(castableTypes.contains(_)).map {t =>
      MapType(t, sourceType.valueType, valueContainsNull = true)
    }

    // Tests that its possible to setup implicit casts between two map types when
    // source map's key type is integer and the target map's key type are either Byte, Short,
    // Long, Double, Float, Decimal(38, 18) or String.
    targetTypes.foreach { targetType =>
      shouldCast(sourceType, targetType, targetType)
    }

    // Tests that its not possible to setup implicit casts between two map types when
    // source map's key type is integer and the target map's key type are either Binary,
    // Boolean, Date, Timestamp, Array, Struct, CaleandarIntervalType or NullType
    nonCastableTargetTypes.foreach { targetType =>
      shouldNotCast(sourceType, targetType)
    }

    // Tests that its not possible to cast from nullable map type to not nullable map type.
    val targetNotNullableTypes = allTypes.filterNot(_ == IntegerType).map { t =>
      MapType(t, sourceType.valueType, valueContainsNull = false)
    }
    val sourceMapExprWithValueNull =
      CreateMap(Seq(Literal.default(sourceType.keyType),
        Literal.create(null, sourceType.valueType)))
    targetNotNullableTypes.foreach { targetType =>
      val castDefault =
        TypeCoercion.ImplicitTypeCasts.implicitCast(sourceMapExprWithValueNull, targetType)
      assert(castDefault.isEmpty,
        s"Should not be able to cast $sourceType to $targetType, but got $castDefault")
    }
  }

  test("implicit type cast - StructType().add(\"a1\", StringType)") {
    val checkedType = new StructType().add("a1", StringType)
    checkTypeCasting(checkedType, castableTypes = Seq(checkedType))
    shouldNotCast(checkedType, DecimalType)
    shouldNotCast(checkedType, NumericType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("implicit type cast - NullType") {
    val checkedType = NullType
    checkTypeCasting(checkedType, castableTypes = allTypes)
    shouldCast(checkedType, DecimalType, DecimalType.SYSTEM_DEFAULT)
    shouldCast(checkedType, NumericType, NumericType.defaultConcreteType)
    shouldCast(checkedType, IntegralType, IntegralType.defaultConcreteType)
  }

  test("implicit type cast - CalendarIntervalType") {
    val checkedType = CalendarIntervalType
    checkTypeCasting(checkedType, castableTypes = Seq(checkedType))
    shouldNotCast(checkedType, DecimalType)
    shouldNotCast(checkedType, NumericType)
    shouldNotCast(checkedType, IntegralType)
  }

  test("eligible implicit type cast - TypeCollection") {
    shouldCast(NullType, TypeCollection(StringType, BinaryType), StringType)

    shouldCast(StringType, TypeCollection(StringType, BinaryType), StringType)
    shouldCast(BinaryType, TypeCollection(StringType, BinaryType), BinaryType)
    shouldCast(StringType, TypeCollection(BinaryType, StringType), StringType)

    shouldCast(IntegerType, TypeCollection(IntegerType, BinaryType), IntegerType)
    shouldCast(IntegerType, TypeCollection(BinaryType, IntegerType), IntegerType)
    shouldCast(BinaryType, TypeCollection(BinaryType, IntegerType), BinaryType)
    shouldCast(BinaryType, TypeCollection(IntegerType, BinaryType), BinaryType)

    shouldCast(IntegerType, TypeCollection(StringType, BinaryType), StringType)
    shouldCast(IntegerType, TypeCollection(BinaryType, StringType), StringType)

    shouldCast(DecimalType.SYSTEM_DEFAULT,
      TypeCollection(IntegerType, DecimalType), DecimalType.SYSTEM_DEFAULT)
    shouldCast(DecimalType(10, 2), TypeCollection(IntegerType, DecimalType), DecimalType(10, 2))
    shouldCast(DecimalType(10, 2), TypeCollection(DecimalType, IntegerType), DecimalType(10, 2))
    shouldCast(IntegerType, TypeCollection(DecimalType(10, 2), StringType), DecimalType(10, 2))

    shouldCast(StringType, TypeCollection(NumericType, BinaryType), DoubleType)

    shouldCast(
      ArrayType(StringType, false),
      TypeCollection(ArrayType(StringType), StringType),
      ArrayType(StringType, false))

    shouldCast(
      ArrayType(StringType, true),
      TypeCollection(ArrayType(StringType), StringType),
      ArrayType(StringType, true))
  }

  test("ineligible implicit type cast - TypeCollection") {
    shouldNotCast(IntegerType, TypeCollection(DateType, TimestampType))
  }

  test("tightest common bound for types") {
    def widenTest(t1: DataType, t2: DataType, expected: Option[DataType]): Unit =
      checkWidenType(TypeCoercion.findTightestCommonType, t1, t2, expected)

    // Null
    widenTest(NullType, NullType, Some(NullType))

    // Boolean
    widenTest(NullType, BooleanType, Some(BooleanType))
    widenTest(BooleanType, BooleanType, Some(BooleanType))
    widenTest(IntegerType, BooleanType, None)
    widenTest(LongType, BooleanType, None)

    // Integral
    widenTest(NullType, ByteType, Some(ByteType))
    widenTest(NullType, IntegerType, Some(IntegerType))
    widenTest(NullType, LongType, Some(LongType))
    widenTest(ShortType, IntegerType, Some(IntegerType))
    widenTest(ShortType, LongType, Some(LongType))
    widenTest(IntegerType, LongType, Some(LongType))
    widenTest(LongType, LongType, Some(LongType))

    // Floating point
    widenTest(NullType, FloatType, Some(FloatType))
    widenTest(NullType, DoubleType, Some(DoubleType))
    widenTest(FloatType, DoubleType, Some(DoubleType))
    widenTest(FloatType, FloatType, Some(FloatType))
    widenTest(DoubleType, DoubleType, Some(DoubleType))

    // Integral mixed with floating point.
    widenTest(IntegerType, FloatType, Some(FloatType))
    widenTest(IntegerType, DoubleType, Some(DoubleType))
    widenTest(IntegerType, DoubleType, Some(DoubleType))
    widenTest(LongType, FloatType, Some(FloatType))
    widenTest(LongType, DoubleType, Some(DoubleType))

    // No up-casting for fixed-precision decimal (this is handled by arithmetic rules)
    widenTest(DecimalType(2, 1), DecimalType(3, 2), None)
    widenTest(DecimalType(2, 1), DoubleType, None)
    widenTest(DecimalType(2, 1), IntegerType, None)
    widenTest(DoubleType, DecimalType(2, 1), None)

    // StringType
    widenTest(NullType, StringType, Some(StringType))
    widenTest(StringType, StringType, Some(StringType))
    widenTest(IntegerType, StringType, None)
    widenTest(LongType, StringType, None)

    // TimestampType
    widenTest(NullType, TimestampType, Some(TimestampType))
    widenTest(TimestampType, TimestampType, Some(TimestampType))
    widenTest(DateType, TimestampType, Some(TimestampType))
    widenTest(IntegerType, TimestampType, None)
    widenTest(StringType, TimestampType, None)

    // ComplexType
    widenTest(NullType,
      MapType(IntegerType, StringType, false),
      Some(MapType(IntegerType, StringType, false)))
    widenTest(NullType, StructType(Seq()), Some(StructType(Seq())))
    widenTest(StringType, MapType(IntegerType, StringType, true), None)
    widenTest(ArrayType(IntegerType), StructType(Seq()), None)

    widenTest(
      StructType(Seq(StructField("a", IntegerType))),
      StructType(Seq(StructField("b", IntegerType))),
      None)
    widenTest(
      StructType(Seq(StructField("a", IntegerType, nullable = false))),
      StructType(Seq(StructField("a", DoubleType, nullable = false))),
      Some(StructType(Seq(StructField("a", DoubleType, nullable = false)))))

    widenTest(
      StructType(Seq(StructField("a", IntegerType, nullable = false))),
      StructType(Seq(StructField("a", IntegerType, nullable = false))),
      Some(StructType(Seq(StructField("a", IntegerType, nullable = false)))))
    widenTest(
      StructType(Seq(StructField("a", IntegerType, nullable = false))),
      StructType(Seq(StructField("a", IntegerType, nullable = true))),
      Some(StructType(Seq(StructField("a", IntegerType, nullable = true)))))
    widenTest(
      StructType(Seq(StructField("a", IntegerType, nullable = true))),
      StructType(Seq(StructField("a", IntegerType, nullable = false))),
      Some(StructType(Seq(StructField("a", IntegerType, nullable = true)))))
    widenTest(
      StructType(Seq(StructField("a", IntegerType, nullable = true))),
      StructType(Seq(StructField("a", IntegerType, nullable = true))),
      Some(StructType(Seq(StructField("a", IntegerType, nullable = true)))))

    withSQLConf(SQLConf.CASE_SENSITIVE.key -> "true") {
      widenTest(
        StructType(Seq(StructField("a", IntegerType))),
        StructType(Seq(StructField("A", IntegerType))),
        None)
    }
    withSQLConf(SQLConf.CASE_SENSITIVE.key -> "false") {
      checkWidenType(
        TypeCoercion.findTightestCommonType,
        StructType(Seq(StructField("a", IntegerType), StructField("B", IntegerType))),
        StructType(Seq(StructField("A", IntegerType), StructField("b", IntegerType))),
        Some(StructType(Seq(StructField("a", IntegerType), StructField("B", IntegerType)))),
        isSymmetric = false)
    }

    widenTest(
      ArrayType(IntegerType, containsNull = true),
      ArrayType(IntegerType, containsNull = false),
      Some(ArrayType(IntegerType, containsNull = true)))

    widenTest(
      ArrayType(NullType, containsNull = true),
      ArrayType(IntegerType, containsNull = false),
      Some(ArrayType(IntegerType, containsNull = true)))

    widenTest(
      MapType(IntegerType, StringType, valueContainsNull = true),
      MapType(IntegerType, StringType, valueContainsNull = false),
      Some(MapType(IntegerType, StringType, valueContainsNull = true)))

    widenTest(
      MapType(NullType, NullType, true),
      MapType(IntegerType, StringType, false),
      Some(MapType(IntegerType, StringType, true)))

    widenTest(
      new StructType()
        .add("arr", ArrayType(IntegerType, containsNull = true), nullable = false),
      new StructType()
        .add("arr", ArrayType(IntegerType, containsNull = false), nullable = true),
      Some(new StructType()
        .add("arr", ArrayType(IntegerType, containsNull = true), nullable = true)))

    widenTest(
      new StructType()
        .add("null", NullType, nullable = true),
      new StructType()
        .add("null", IntegerType, nullable = false),
      Some(new StructType()
        .add("null", IntegerType, nullable = true)))

    widenTest(
      ArrayType(NullType, containsNull = false),
      ArrayType(IntegerType, containsNull = false),
      Some(ArrayType(IntegerType, containsNull = false)))

    widenTest(MapType(NullType, NullType, false),
      MapType(IntegerType, StringType, false),
      Some(MapType(IntegerType, StringType, false)))

    widenTest(
      new StructType()
        .add("null", NullType, nullable = false),
      new StructType()
        .add("null", IntegerType, nullable = false),
      Some(new StructType()
        .add("null", IntegerType, nullable = false)))
  }

  test("wider common type for decimal and array") {
    def widenTestWithStringPromotion(
        t1: DataType,
        t2: DataType,
        expected: Option[DataType],
        isSymmetric: Boolean = true): Unit = {
      checkWidenType(TypeCoercion.findWiderTypeForTwo, t1, t2, expected, isSymmetric)
    }

    def widenTestWithoutStringPromotion(
        t1: DataType,
        t2: DataType,
        expected: Option[DataType],
        isSymmetric: Boolean = true): Unit = {
      checkWidenType(
        TypeCoercion.findWiderTypeWithoutStringPromotionForTwo, t1, t2, expected, isSymmetric)
    }

    // Decimal
    widenTestWithStringPromotion(
      DecimalType(2, 1), DecimalType(3, 2), Some(DecimalType(3, 2)))
    widenTestWithStringPromotion(
      DecimalType(2, 1), DoubleType, Some(DoubleType))
    widenTestWithStringPromotion(
      DecimalType(2, 1), IntegerType, Some(DecimalType(11, 1)))
    widenTestWithStringPromotion(
      DecimalType(2, 1), LongType, Some(DecimalType(21, 1)))

    // ArrayType
    widenTestWithStringPromotion(
      ArrayType(ShortType, containsNull = true),
      ArrayType(DoubleType, containsNull = false),
      Some(ArrayType(DoubleType, containsNull = true)))
    widenTestWithStringPromotion(
      ArrayType(TimestampType, containsNull = false),
      ArrayType(StringType, containsNull = true),
      Some(ArrayType(StringType, containsNull = true)))
    widenTestWithStringPromotion(
      ArrayType(ArrayType(IntegerType), containsNull = false),
      ArrayType(ArrayType(LongType), containsNull = false),
      Some(ArrayType(ArrayType(LongType), containsNull = false)))
    widenTestWithStringPromotion(
      ArrayType(MapType(IntegerType, FloatType), containsNull = false),
      ArrayType(MapType(LongType, DoubleType), containsNull = false),
      Some(ArrayType(MapType(LongType, DoubleType), containsNull = false)))
    widenTestWithStringPromotion(
      ArrayType(new StructType().add("num", ShortType), containsNull = false),
      ArrayType(new StructType().add("num", LongType), containsNull = false),
      Some(ArrayType(new StructType().add("num", LongType), containsNull = false)))
    widenTestWithStringPromotion(
      ArrayType(IntegerType, containsNull = false),
      ArrayType(DecimalType.IntDecimal, containsNull = false),
      Some(ArrayType(DecimalType.IntDecimal, containsNull = false)))
    widenTestWithStringPromotion(
      ArrayType(DecimalType(36, 0), containsNull = false),
      ArrayType(DecimalType(36, 35), containsNull = false),
      Some(ArrayType(DecimalType(38, 35), containsNull = true)))

    // MapType
    widenTestWithStringPromotion(
      MapType(ShortType, TimestampType, valueContainsNull = true),
      MapType(DoubleType, StringType, valueContainsNull = false),
      Some(MapType(DoubleType, StringType, valueContainsNull = true)))
    widenTestWithStringPromotion(
      MapType(IntegerType, ArrayType(TimestampType), valueContainsNull = false),
      MapType(LongType, ArrayType(StringType), valueContainsNull = true),
      Some(MapType(LongType, ArrayType(StringType), valueContainsNull = true)))
    widenTestWithStringPromotion(
      MapType(IntegerType, MapType(ShortType, TimestampType), valueContainsNull = false),
      MapType(LongType, MapType(DoubleType, StringType), valueContainsNull = false),
      Some(MapType(LongType, MapType(DoubleType, StringType), valueContainsNull = false)))
    widenTestWithStringPromotion(
      MapType(IntegerType, new StructType().add("num", ShortType), valueContainsNull = false),
      MapType(LongType, new StructType().add("num", LongType), valueContainsNull = false),
      Some(MapType(LongType, new StructType().add("num", LongType), valueContainsNull = false)))
    widenTestWithStringPromotion(
      MapType(StringType, IntegerType, valueContainsNull = false),
      MapType(StringType, DecimalType.IntDecimal, valueContainsNull = false),
      Some(MapType(StringType, DecimalType.IntDecimal, valueContainsNull = false)))
    widenTestWithStringPromotion(
      MapType(StringType, DecimalType(36, 0), valueContainsNull = false),
      MapType(StringType, DecimalType(36, 35), valueContainsNull = false),
      Some(MapType(StringType, DecimalType(38, 35), valueContainsNull = true)))
    widenTestWithStringPromotion(
      MapType(IntegerType, StringType, valueContainsNull = false),
      MapType(DecimalType.IntDecimal, StringType, valueContainsNull = false),
      Some(MapType(DecimalType.IntDecimal, StringType, valueContainsNull = false)))
    widenTestWithStringPromotion(
      MapType(DecimalType(36, 0), StringType, valueContainsNull = false),
      MapType(DecimalType(36, 35), StringType, valueContainsNull = false),
      None)

    // StructType
    widenTestWithStringPromotion(
      new StructType()
        .add("num", ShortType, nullable = true).add("ts", StringType, nullable = false),
      new StructType()
        .add("num", DoubleType, nullable = false).add("ts", TimestampType, nullable = true),
      Some(new StructType()
        .add("num", DoubleType, nullable = true).add("ts", StringType, nullable = true)))
    widenTestWithStringPromotion(
      new StructType()
        .add("arr", ArrayType(ShortType, containsNull = false), nullable = false),
      new StructType()
        .add("arr", ArrayType(DoubleType, containsNull = true), nullable = false),
      Some(new StructType()
        .add("arr", ArrayType(DoubleType, containsNull = true), nullable = false)))
    widenTestWithStringPromotion(
      new StructType()
        .add("map", MapType(ShortType, TimestampType, valueContainsNull = true), nullable = false),
      new StructType()
        .add("map", MapType(DoubleType, StringType, valueContainsNull = false), nullable = false),
      Some(new StructType()
        .add("map", MapType(DoubleType, StringType, valueContainsNull = true), nullable = false)))
    widenTestWithStringPromotion(
      new StructType().add("num", IntegerType, nullable = false),
      new StructType().add("num", DecimalType.IntDecimal, nullable = false),
      Some(new StructType().add("num", DecimalType.IntDecimal, nullable = false)))
    widenTestWithStringPromotion(
      new StructType().add("num", DecimalType(36, 0), nullable = false),
      new StructType().add("num", DecimalType(36, 35), nullable = false),
      Some(new StructType().add("num", DecimalType(38, 35), nullable = true)))

    widenTestWithStringPromotion(
      new StructType().add("num", IntegerType),
      new StructType().add("num", LongType).add("str", StringType),
      None)
    widenTestWithoutStringPromotion(
      new StructType().add("num", IntegerType),
      new StructType().add("num", LongType).add("str", StringType),
      None)
    withSQLConf(SQLConf.CASE_SENSITIVE.key -> "true") {
      widenTestWithStringPromotion(
        new StructType().add("a", IntegerType),
        new StructType().add("A", LongType),
        None)
      widenTestWithoutStringPromotion(
        new StructType().add("a", IntegerType),
        new StructType().add("A", LongType),
        None)
    }
    withSQLConf(SQLConf.CASE_SENSITIVE.key -> "false") {
      widenTestWithStringPromotion(
        new StructType().add("a", IntegerType),
        new StructType().add("A", LongType),
        Some(new StructType().add("a", LongType)),
        isSymmetric = false)
      widenTestWithoutStringPromotion(
        new StructType().add("a", IntegerType),
        new StructType().add("A", LongType),
        Some(new StructType().add("a", LongType)),
        isSymmetric = false)
    }

    // Without string promotion
    widenTestWithoutStringPromotion(IntegerType, StringType, None)
    widenTestWithoutStringPromotion(StringType, TimestampType, None)
    widenTestWithoutStringPromotion(ArrayType(LongType), ArrayType(StringType), None)
    widenTestWithoutStringPromotion(ArrayType(StringType), ArrayType(TimestampType), None)
    widenTestWithoutStringPromotion(
      MapType(LongType, IntegerType), MapType(StringType, IntegerType), None)
    widenTestWithoutStringPromotion(
      MapType(IntegerType, LongType), MapType(IntegerType, StringType), None)
    widenTestWithoutStringPromotion(
      MapType(StringType, IntegerType), MapType(TimestampType, IntegerType), None)
    widenTestWithoutStringPromotion(
      MapType(IntegerType, StringType), MapType(IntegerType, TimestampType), None)
    widenTestWithoutStringPromotion(
      new StructType().add("a", IntegerType),
      new StructType().add("a", StringType),
      None)
    widenTestWithoutStringPromotion(
      new StructType().add("a", StringType),
      new StructType().add("a", IntegerType),
      None)

    // String promotion
    widenTestWithStringPromotion(IntegerType, StringType, Some(StringType))
    widenTestWithStringPromotion(StringType, TimestampType, Some(StringType))
    widenTestWithStringPromotion(
      ArrayType(LongType), ArrayType(StringType), Some(ArrayType(StringType)))
    widenTestWithStringPromotion(
      ArrayType(StringType), ArrayType(TimestampType), Some(ArrayType(StringType)))
    widenTestWithStringPromotion(
      MapType(LongType, IntegerType),
      MapType(StringType, IntegerType),
      Some(MapType(StringType, IntegerType)))
    widenTestWithStringPromotion(
      MapType(IntegerType, LongType),
      MapType(IntegerType, StringType),
      Some(MapType(IntegerType, StringType)))
    widenTestWithStringPromotion(
      MapType(StringType, IntegerType),
      MapType(TimestampType, IntegerType),
      Some(MapType(StringType, IntegerType)))
    widenTestWithStringPromotion(
      MapType(IntegerType, StringType),
      MapType(IntegerType, TimestampType),
      Some(MapType(IntegerType, StringType)))
    widenTestWithStringPromotion(
      new StructType().add("a", IntegerType),
      new StructType().add("a", StringType),
      Some(new StructType().add("a", StringType)))
    widenTestWithStringPromotion(
      new StructType().add("a", StringType),
      new StructType().add("a", IntegerType),
      Some(new StructType().add("a", StringType)))
  }

  private def ruleTest(rule: Rule[LogicalPlan],
      initial: Expression, transformed: Expression): Unit = {
    ruleTest(Seq(rule), initial, transformed)
  }

  private def ruleTest(
      rules: Seq[Rule[LogicalPlan]],
      initial: Expression,
      transformed: Expression): Unit = {
    val testRelation = LocalRelation(AttributeReference("a", IntegerType)())
    val analyzer = new RuleExecutor[LogicalPlan] {
      override val batches = Seq(Batch("Resolution", FixedPoint(3), rules: _*))
    }

    comparePlans(
      analyzer.execute(Project(Seq(Alias(initial, "a")()), testRelation)),
      Project(Seq(Alias(transformed, "a")()), testRelation))
  }

  test("cast NullType for expressions that implement ExpectsInputTypes") {
    ruleTest(TypeCoercion.ImplicitTypeCasts,
      AnyTypeUnaryExpression(Literal.create(null, NullType)),
      AnyTypeUnaryExpression(Literal.create(null, NullType)))

    ruleTest(TypeCoercion.ImplicitTypeCasts,
      NumericTypeUnaryExpression(Literal.create(null, NullType)),
      NumericTypeUnaryExpression(Literal.create(null, DoubleType)))
  }

  test("cast NullType for binary operators") {
    ruleTest(TypeCoercion.ImplicitTypeCasts,
      AnyTypeBinaryOperator(Literal.create(null, NullType), Literal.create(null, NullType)),
      AnyTypeBinaryOperator(Literal.create(null, NullType), Literal.create(null, NullType)))

    ruleTest(TypeCoercion.ImplicitTypeCasts,
      NumericTypeBinaryOperator(Literal.create(null, NullType), Literal.create(null, NullType)),
      NumericTypeBinaryOperator(Literal.create(null, DoubleType), Literal.create(null, DoubleType)))
  }

  test("coalesce casts") {
    val rule = TypeCoercion.FunctionArgumentConversion

    val intLit = Literal(1)
    val longLit = Literal.create(1L)
    val doubleLit = Literal(1.0)
    val stringLit = Literal.create("c", StringType)
    val nullLit = Literal.create(null, NullType)
    val floatNullLit = Literal.create(null, FloatType)
    val floatLit = Literal.create(1.0f, FloatType)
    val timestampLit = Literal.create(Timestamp.valueOf("2017-04-12 00:00:00"), TimestampType)
    val decimalLit = Literal(new java.math.BigDecimal("1000000000000000000000"))
    val tsArrayLit = Literal(Array(new Timestamp(System.currentTimeMillis())))
    val strArrayLit = Literal(Array("c"))
    val intArrayLit = Literal(Array(1))

    ruleTest(rule,
      Coalesce(Seq(doubleLit, intLit, floatLit)),
      Coalesce(Seq(doubleLit, Cast(intLit, DoubleType), Cast(floatLit, DoubleType))))

    ruleTest(rule,
      Coalesce(Seq(longLit, intLit, decimalLit)),
      Coalesce(Seq(Cast(longLit, DecimalType(22, 0)),
        Cast(intLit, DecimalType(22, 0)), decimalLit)))

    ruleTest(rule,
      Coalesce(Seq(nullLit, intLit)),
      Coalesce(Seq(Cast(nullLit, IntegerType), intLit)))

    ruleTest(rule,
      Coalesce(Seq(timestampLit, stringLit)),
      Coalesce(Seq(Cast(timestampLit, StringType), stringLit)))

    ruleTest(rule,
      Coalesce(Seq(nullLit, floatNullLit, intLit)),
      Coalesce(Seq(Cast(nullLit, FloatType), floatNullLit, Cast(intLit, FloatType))))

    ruleTest(rule,
      Coalesce(Seq(nullLit, intLit, decimalLit, doubleLit)),
      Coalesce(Seq(Cast(nullLit, DoubleType), Cast(intLit, DoubleType),
        Cast(decimalLit, DoubleType), doubleLit)))

    ruleTest(rule,
      Coalesce(Seq(nullLit, floatNullLit, doubleLit, stringLit)),
      Coalesce(Seq(Cast(nullLit, StringType), Cast(floatNullLit, StringType),
        Cast(doubleLit, StringType), stringLit)))

    ruleTest(rule,
      Coalesce(Seq(timestampLit, intLit, stringLit)),
      Coalesce(Seq(Cast(timestampLit, StringType), Cast(intLit, StringType), stringLit)))

    ruleTest(rule,
      Coalesce(Seq(tsArrayLit, intArrayLit, strArrayLit)),
      Coalesce(Seq(Cast(tsArrayLit, ArrayType(StringType)),
        Cast(intArrayLit, ArrayType(StringType)), strArrayLit)))
  }

  test("CreateArray casts") {
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateArray(Literal(1.0)
        :: Literal(1)
        :: Literal.create(1.0f, FloatType)
        :: Nil),
      CreateArray(Literal(1.0)
        :: Cast(Literal(1), DoubleType)
        :: Cast(Literal.create(1.0f, FloatType), DoubleType)
        :: Nil))

    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateArray(Literal(1.0)
        :: Literal(1)
        :: Literal("a")
        :: Nil),
      CreateArray(Cast(Literal(1.0), StringType)
        :: Cast(Literal(1), StringType)
        :: Literal("a")
        :: Nil))

    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateArray(Literal.create(null, DecimalType(5, 3))
        :: Literal(1)
        :: Nil),
      CreateArray(Literal.create(null, DecimalType(5, 3)).cast(DecimalType(13, 3))
        :: Literal(1).cast(DecimalType(13, 3))
        :: Nil))

    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateArray(Literal.create(null, DecimalType(5, 3))
        :: Literal.create(null, DecimalType(22, 10))
        :: Literal.create(null, DecimalType(38, 38))
        :: Nil),
      CreateArray(Literal.create(null, DecimalType(5, 3)).cast(DecimalType(38, 38))
        :: Literal.create(null, DecimalType(22, 10)).cast(DecimalType(38, 38))
        :: Literal.create(null, DecimalType(38, 38))
        :: Nil))
  }

  test("CreateMap casts") {
    // type coercion for map keys
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateMap(Literal(1)
        :: Literal("a")
        :: Literal.create(2.0f, FloatType)
        :: Literal("b")
        :: Nil),
      CreateMap(Cast(Literal(1), FloatType)
        :: Literal("a")
        :: Literal.create(2.0f, FloatType)
        :: Literal("b")
        :: Nil))
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateMap(Literal.create(null, DecimalType(5, 3))
        :: Literal("a")
        :: Literal.create(2.0f, FloatType)
        :: Literal("b")
        :: Nil),
      CreateMap(Literal.create(null, DecimalType(5, 3)).cast(DoubleType)
        :: Literal("a")
        :: Literal.create(2.0f, FloatType).cast(DoubleType)
        :: Literal("b")
        :: Nil))
    // type coercion for map values
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateMap(Literal(1)
        :: Literal("a")
        :: Literal(2)
        :: Literal(3.0)
        :: Nil),
      CreateMap(Literal(1)
        :: Literal("a")
        :: Literal(2)
        :: Cast(Literal(3.0), StringType)
        :: Nil))
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateMap(Literal(1)
        :: Literal.create(null, DecimalType(38, 0))
        :: Literal(2)
        :: Literal.create(null, DecimalType(38, 38))
        :: Nil),
      CreateMap(Literal(1)
        :: Literal.create(null, DecimalType(38, 0)).cast(DecimalType(38, 38))
        :: Literal(2)
        :: Literal.create(null, DecimalType(38, 38))
        :: Nil))
    // type coercion for both map keys and values
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      CreateMap(Literal(1)
        :: Literal("a")
        :: Literal(2.0)
        :: Literal(3.0)
        :: Nil),
      CreateMap(Cast(Literal(1), DoubleType)
        :: Literal("a")
        :: Literal(2.0)
        :: Cast(Literal(3.0), StringType)
        :: Nil))
  }

  test("greatest/least cast") {
    for (operator <- Seq[(Seq[Expression] => Expression)](Greatest, Least)) {
      ruleTest(TypeCoercion.FunctionArgumentConversion,
        operator(Literal(1.0)
          :: Literal(1)
          :: Literal.create(1.0f, FloatType)
          :: Nil),
        operator(Literal(1.0)
          :: Cast(Literal(1), DoubleType)
          :: Cast(Literal.create(1.0f, FloatType), DoubleType)
          :: Nil))
      ruleTest(TypeCoercion.FunctionArgumentConversion,
        operator(Literal(1L)
          :: Literal(1)
          :: Literal(new java.math.BigDecimal("1000000000000000000000"))
          :: Nil),
        operator(Cast(Literal(1L), DecimalType(22, 0))
          :: Cast(Literal(1), DecimalType(22, 0))
          :: Literal(new java.math.BigDecimal("1000000000000000000000"))
          :: Nil))
      ruleTest(TypeCoercion.FunctionArgumentConversion,
        operator(Literal(1.0)
          :: Literal.create(null, DecimalType(10, 5))
          :: Literal(1)
          :: Nil),
        operator(Literal(1.0)
          :: Literal.create(null, DecimalType(10, 5)).cast(DoubleType)
          :: Literal(1).cast(DoubleType)
          :: Nil))
      ruleTest(TypeCoercion.FunctionArgumentConversion,
        operator(Literal.create(null, DecimalType(15, 0))
          :: Literal.create(null, DecimalType(10, 5))
          :: Literal(1)
          :: Nil),
        operator(Literal.create(null, DecimalType(15, 0)).cast(DecimalType(20, 5))
          :: Literal.create(null, DecimalType(10, 5)).cast(DecimalType(20, 5))
          :: Literal(1).cast(DecimalType(20, 5))
          :: Nil))
      ruleTest(TypeCoercion.FunctionArgumentConversion,
        operator(Literal.create(2L, LongType)
          :: Literal(1)
          :: Literal.create(null, DecimalType(10, 5))
          :: Nil),
        operator(Literal.create(2L, LongType).cast(DecimalType(25, 5))
          :: Literal(1).cast(DecimalType(25, 5))
          :: Literal.create(null, DecimalType(10, 5)).cast(DecimalType(25, 5))
          :: Nil))
    }
  }

  test("nanvl casts") {
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      NaNvl(Literal.create(1.0f, FloatType), Literal.create(1.0, DoubleType)),
      NaNvl(Cast(Literal.create(1.0f, FloatType), DoubleType), Literal.create(1.0, DoubleType)))
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      NaNvl(Literal.create(1.0, DoubleType), Literal.create(1.0f, FloatType)),
      NaNvl(Literal.create(1.0, DoubleType), Cast(Literal.create(1.0f, FloatType), DoubleType)))
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      NaNvl(Literal.create(1.0, DoubleType), Literal.create(1.0, DoubleType)),
      NaNvl(Literal.create(1.0, DoubleType), Literal.create(1.0, DoubleType)))
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      NaNvl(Literal.create(1.0f, FloatType), Literal.create(null, NullType)),
      NaNvl(Literal.create(1.0f, FloatType), Cast(Literal.create(null, NullType), FloatType)))
    ruleTest(TypeCoercion.FunctionArgumentConversion,
      NaNvl(Literal.create(1.0, DoubleType), Literal.create(null, NullType)),
      NaNvl(Literal.create(1.0, DoubleType), Cast(Literal.create(null, NullType), DoubleType)))
  }

  test("type coercion for If") {
    val rule = TypeCoercion.IfCoercion
    val intLit = Literal(1)
    val doubleLit = Literal(1.0)
    val trueLit = Literal.create(true, BooleanType)
    val falseLit = Literal.create(false, BooleanType)
    val stringLit = Literal.create("c", StringType)
    val floatLit = Literal.create(1.0f, FloatType)
    val timestampLit = Literal.create(Timestamp.valueOf("2017-04-12 00:00:00"), TimestampType)
    val decimalLit = Literal(new java.math.BigDecimal("1000000000000000000000"))

    ruleTest(rule,
      If(Literal(true), Literal(1), Literal(1L)),
      If(Literal(true), Cast(Literal(1), LongType), Literal(1L)))

    ruleTest(rule,
      If(Literal.create(null, NullType), Literal(1), Literal(1)),
      If(Literal.create(null, BooleanType), Literal(1), Literal(1)))

    ruleTest(rule,
      If(AssertTrue(trueLit), Literal(1), Literal(2)),
      If(Cast(AssertTrue(trueLit), BooleanType), Literal(1), Literal(2)))

    ruleTest(rule,
      If(AssertTrue(falseLit), Literal(1), Literal(2)),
      If(Cast(AssertTrue(falseLit), BooleanType), Literal(1), Literal(2)))

    ruleTest(rule,
      If(trueLit, intLit, doubleLit),
      If(trueLit, Cast(intLit, DoubleType), doubleLit))

    ruleTest(rule,
      If(trueLit, floatLit, doubleLit),
      If(trueLit, Cast(floatLit, DoubleType), doubleLit))

    ruleTest(rule,
      If(trueLit, floatLit, decimalLit),
      If(trueLit, Cast(floatLit, DoubleType), Cast(decimalLit, DoubleType)))

    ruleTest(rule,
      If(falseLit, stringLit, doubleLit),
      If(falseLit, stringLit, Cast(doubleLit, StringType)))

    ruleTest(rule,
      If(trueLit, timestampLit, stringLit),
      If(trueLit, Cast(timestampLit, StringType), stringLit))
  }

  test("type coercion for CaseKeyWhen") {
    ruleTest(TypeCoercion.ImplicitTypeCasts,
      CaseKeyWhen(Literal(1.toShort), Seq(Literal(1), Literal("a"))),
      CaseKeyWhen(Cast(Literal(1.toShort), IntegerType), Seq(Literal(1), Literal("a")))
    )
    ruleTest(TypeCoercion.CaseWhenCoercion,
      CaseKeyWhen(Literal(true), Seq(Literal(1), Literal("a"))),
      CaseKeyWhen(Literal(true), Seq(Literal(1), Literal("a")))
    )
    ruleTest(TypeCoercion.CaseWhenCoercion,
      CaseWhen(Seq((Literal(true), Literal(1.2))),
        Literal.create(BigDecimal.valueOf(1), DecimalType(7, 2))),
      CaseWhen(Seq((Literal(true), Literal(1.2))),
        Cast(Literal.create(BigDecimal.valueOf(1), DecimalType(7, 2)), DoubleType))
    )
    ruleTest(TypeCoercion.CaseWhenCoercion,
      CaseWhen(Seq((Literal(true), Literal(100L))),
        Literal.create(BigDecimal.valueOf(1), DecimalType(7, 2))),
      CaseWhen(Seq((Literal(true), Cast(Literal(100L), DecimalType(22, 2)))),
        Cast(Literal.create(BigDecimal.valueOf(1), DecimalType(7, 2)), DecimalType(22, 2)))
    )
  }

  test("type coercion for Stack") {
    val rule = TypeCoercion.StackCoercion

    ruleTest(rule,
      Stack(Seq(Literal(3), Literal(1), Literal(2), Literal(null))),
      Stack(Seq(Literal(3), Literal(1), Literal(2), Literal.create(null, IntegerType))))
    ruleTest(rule,
      Stack(Seq(Literal(3), Literal(1.0), Literal(null), Literal(3.0))),
      Stack(Seq(Literal(3), Literal(1.0), Literal.create(null, DoubleType), Literal(3.0))))
    ruleTest(rule,
      Stack(Seq(Literal(3), Literal(null), Literal("2"), Literal("3"))),
      Stack(Seq(Literal(3), Literal.create(null, StringType), Literal("2"), Literal("3"))))
    ruleTest(rule,
      Stack(Seq(Literal(3), Literal(null), Literal(null), Literal(null))),
      Stack(Seq(Literal(3), Literal(null), Literal(null), Literal(null))))

    ruleTest(rule,
      Stack(Seq(Literal(2),
        Literal(1), Literal("2"),
        Literal(null), Literal(null))),
      Stack(Seq(Literal(2),
        Literal(1), Literal("2"),
        Literal.create(null, IntegerType), Literal.create(null, StringType))))

    ruleTest(rule,
      Stack(Seq(Literal(2),
        Literal(1), Literal(null),
        Literal(null), Literal("2"))),
      Stack(Seq(Literal(2),
        Literal(1), Literal.create(null, StringType),
        Literal.create(null, IntegerType), Literal("2"))))

    ruleTest(rule,
      Stack(Seq(Literal(2),
        Literal(null), Literal(1),
        Literal("2"), Literal(null))),
      Stack(Seq(Literal(2),
        Literal.create(null, StringType), Literal(1),
        Literal("2"), Literal.create(null, IntegerType))))

    ruleTest(rule,
      Stack(Seq(Literal(2),
        Literal(null), Literal(null),
        Literal(1), Literal("2"))),
      Stack(Seq(Literal(2),
        Literal.create(null, IntegerType), Literal.create(null, StringType),
        Literal(1), Literal("2"))))

    ruleTest(rule,
      Stack(Seq(Subtract(Literal(3), Literal(1)),
        Literal(1), Literal("2"),
        Literal(null), Literal(null))),
      Stack(Seq(Subtract(Literal(3), Literal(1)),
        Literal(1), Literal("2"),
        Literal.create(null, IntegerType), Literal.create(null, StringType))))
  }

  test("type coercion for Concat") {
    val rule = TypeCoercion.ConcatCoercion

    ruleTest(rule,
      Concat(Seq(Literal("ab"), Literal("cde"))),
      Concat(Seq(Literal("ab"), Literal("cde"))))
    ruleTest(rule,
      Concat(Seq(Literal(null), Literal("abc"))),
      Concat(Seq(Cast(Literal(null), StringType), Literal("abc"))))
    ruleTest(rule,
      Concat(Seq(Literal(1), Literal("234"))),
      Concat(Seq(Cast(Literal(1), StringType), Literal("234"))))
    ruleTest(rule,
      Concat(Seq(Literal("1"), Literal("234".getBytes()))),
      Concat(Seq(Literal("1"), Cast(Literal("234".getBytes()), StringType))))
    ruleTest(rule,
      Concat(Seq(Literal(1L), Literal(2.toByte), Literal(0.1))),
      Concat(Seq(Cast(Literal(1L), StringType), Cast(Literal(2.toByte), StringType),
        Cast(Literal(0.1), StringType))))
    ruleTest(rule,
      Concat(Seq(Literal(true), Literal(0.1f), Literal(3.toShort))),
      Concat(Seq(Cast(Literal(true), StringType), Cast(Literal(0.1f), StringType),
        Cast(Literal(3.toShort), StringType))))
    ruleTest(rule,
      Concat(Seq(Literal(1L), Literal(0.1))),
      Concat(Seq(Cast(Literal(1L), StringType), Cast(Literal(0.1), StringType))))
    ruleTest(rule,
      Concat(Seq(Literal(Decimal(10)))),
      Concat(Seq(Cast(Literal(Decimal(10)), StringType))))
    ruleTest(rule,
      Concat(Seq(Literal(BigDecimal.valueOf(10)))),
      Concat(Seq(Cast(Literal(BigDecimal.valueOf(10)), StringType))))
    ruleTest(rule,
      Concat(Seq(Literal(java.math.BigDecimal.valueOf(10)))),
      Concat(Seq(Cast(Literal(java.math.BigDecimal.valueOf(10)), StringType))))
    ruleTest(rule,
      Concat(Seq(Literal(new java.sql.Date(0)), Literal(new Timestamp(0)))),
      Concat(Seq(Cast(Literal(new java.sql.Date(0)), StringType),
        Cast(Literal(new Timestamp(0)), StringType))))

    withSQLConf(SQLConf.CONCAT_BINARY_AS_STRING.key -> "true") {
      ruleTest(rule,
        Concat(Seq(Literal("123".getBytes), Literal("456".getBytes))),
        Concat(Seq(Cast(Literal("123".getBytes), StringType),
          Cast(Literal("456".getBytes), StringType))))
    }

    withSQLConf(SQLConf.CONCAT_BINARY_AS_STRING.key -> "false") {
      ruleTest(rule,
        Concat(Seq(Literal("123".getBytes), Literal("456".getBytes))),
        Concat(Seq(Literal("123".getBytes), Literal("456".getBytes))))
    }
  }

  test("type coercion for Elt") {
    val rule = TypeCoercion.EltCoercion

    ruleTest(rule,
      Elt(Seq(Literal(1), Literal("ab"), Literal("cde"))),
      Elt(Seq(Literal(1), Literal("ab"), Literal("cde"))))
    ruleTest(rule,
      Elt(Seq(Literal(1.toShort), Literal("ab"), Literal("cde"))),
      Elt(Seq(Cast(Literal(1.toShort), IntegerType), Literal("ab"), Literal("cde"))))
    ruleTest(rule,
      Elt(Seq(Literal(2), Literal(null), Literal("abc"))),
      Elt(Seq(Literal(2), Cast(Literal(null), StringType), Literal("abc"))))
    ruleTest(rule,
      Elt(Seq(Literal(2), Literal(1), Literal("234"))),
      Elt(Seq(Literal(2), Cast(Literal(1), StringType), Literal("234"))))
    ruleTest(rule,
      Elt(Seq(Literal(3), Literal(1L), Literal(2.toByte), Literal(0.1))),
      Elt(Seq(Literal(3), Cast(Literal(1L), StringType), Cast(Literal(2.toByte), StringType),
        Cast(Literal(0.1), StringType))))
    ruleTest(rule,
      Elt(Seq(Literal(2), Literal(true), Literal(0.1f), Literal(3.toShort))),
      Elt(Seq(Literal(2), Cast(Literal(true), StringType), Cast(Literal(0.1f), StringType),
        Cast(Literal(3.toShort), StringType))))
    ruleTest(rule,
      Elt(Seq(Literal(1), Literal(1L), Literal(0.1))),
      Elt(Seq(Literal(1), Cast(Literal(1L), StringType), Cast(Literal(0.1), StringType))))
    ruleTest(rule,
      Elt(Seq(Literal(1), Literal(Decimal(10)))),
      Elt(Seq(Literal(1), Cast(Literal(Decimal(10)), StringType))))
    ruleTest(rule,
      Elt(Seq(Literal(1), Literal(BigDecimal.valueOf(10)))),
      Elt(Seq(Literal(1), Cast(Literal(BigDecimal.valueOf(10)), StringType))))
    ruleTest(rule,
      Elt(Seq(Literal(1), Literal(java.math.BigDecimal.valueOf(10)))),
      Elt(Seq(Literal(1), Cast(Literal(java.math.BigDecimal.valueOf(10)), StringType))))
    ruleTest(rule,
      Elt(Seq(Literal(2), Literal(new java.sql.Date(0)), Literal(new Timestamp(0)))),
      Elt(Seq(Literal(2), Cast(Literal(new java.sql.Date(0)), StringType),
        Cast(Literal(new Timestamp(0)), StringType))))

    withSQLConf(SQLConf.ELT_OUTPUT_AS_STRING.key -> "true") {
      ruleTest(rule,
        Elt(Seq(Literal(1), Literal("123".getBytes), Literal("456".getBytes))),
        Elt(Seq(Literal(1), Cast(Literal("123".getBytes), StringType),
          Cast(Literal("456".getBytes), StringType))))
    }

    withSQLConf(SQLConf.ELT_OUTPUT_AS_STRING.key -> "false") {
      ruleTest(rule,
        Elt(Seq(Literal(1), Literal("123".getBytes), Literal("456".getBytes))),
        Elt(Seq(Literal(1), Literal("123".getBytes), Literal("456".getBytes))))
    }
  }

  test("BooleanEquality type cast") {
    val be = TypeCoercion.BooleanEquality
    // Use something more than a literal to avoid triggering the simplification rules.
    val one = Add(Literal(Decimal(1)), Literal(Decimal(0)))

    ruleTest(be,
      EqualTo(Literal(true), one),
      EqualTo(Cast(Literal(true), one.dataType), one)
    )

    ruleTest(be,
      EqualTo(one, Literal(true)),
      EqualTo(one, Cast(Literal(true), one.dataType))
    )

    ruleTest(be,
      EqualNullSafe(Literal(true), one),
      EqualNullSafe(Cast(Literal(true), one.dataType), one)
    )

    ruleTest(be,
      EqualNullSafe(one, Literal(true)),
      EqualNullSafe(one, Cast(Literal(true), one.dataType))
    )
  }

  test("BooleanEquality simplification") {
    val be = TypeCoercion.BooleanEquality

    ruleTest(be,
      EqualTo(Literal(true), Literal(1)),
      Literal(true)
    )
    ruleTest(be,
      EqualTo(Literal(true), Literal(0)),
      Not(Literal(true))
    )
    ruleTest(be,
      EqualNullSafe(Literal(true), Literal(1)),
      And(IsNotNull(Literal(true)), Literal(true))
    )
    ruleTest(be,
      EqualNullSafe(Literal(true), Literal(0)),
      And(IsNotNull(Literal(true)), Not(Literal(true)))
    )

    ruleTest(be,
      EqualTo(Literal(true), Literal(1L)),
      Literal(true)
    )
    ruleTest(be,
      EqualTo(Literal(new java.math.BigDecimal(1)), Literal(true)),
      Literal(true)
    )
    ruleTest(be,
      EqualTo(Literal(BigDecimal(0)), Literal(true)),
      Not(Literal(true))
    )
    ruleTest(be,
      EqualTo(Literal(Decimal(1)), Literal(true)),
      Literal(true)
    )
    ruleTest(be,
      EqualTo(Literal.create(Decimal(1), DecimalType(8, 0)), Literal(true)),
      Literal(true)
    )
  }

  private def checkOutput(logical: LogicalPlan, expectTypes: Seq[DataType]): Unit = {
    logical.output.zip(expectTypes).foreach { case (attr, dt) =>
      assert(attr.dataType === dt)
    }
  }

  private val timeZoneResolver = ResolveTimeZone

  private def widenSetOperationTypes(plan: LogicalPlan): LogicalPlan = {
    timeZoneResolver(TypeCoercion.WidenSetOperationTypes(plan))
  }

  test("WidenSetOperationTypes for except and intersect") {
    val firstTable = LocalRelation(
      AttributeReference("i", IntegerType)(),
      AttributeReference("u", DecimalType.SYSTEM_DEFAULT)(),
      AttributeReference("b", ByteType)(),
      AttributeReference("d", DoubleType)())
    val secondTable = LocalRelation(
      AttributeReference("s", StringType)(),
      AttributeReference("d", DecimalType(2, 1))(),
      AttributeReference("f", FloatType)(),
      AttributeReference("l", LongType)())

    val expectedTypes = Seq(StringType, DecimalType.SYSTEM_DEFAULT, FloatType, DoubleType)

    val r1 = widenSetOperationTypes(
      Except(firstTable, secondTable, isAll = false)).asInstanceOf[Except]
    val r2 = widenSetOperationTypes(
      Intersect(firstTable, secondTable, isAll = false)).asInstanceOf[Intersect]
    checkOutput(r1.left, expectedTypes)
    checkOutput(r1.right, expectedTypes)
    checkOutput(r2.left, expectedTypes)
    checkOutput(r2.right, expectedTypes)

    // Check if a Project is added
    assert(r1.left.isInstanceOf[Project])
    assert(r1.right.isInstanceOf[Project])
    assert(r2.left.isInstanceOf[Project])
    assert(r2.right.isInstanceOf[Project])
  }

  test("WidenSetOperationTypes for union") {
    val firstTable = LocalRelation(
      AttributeReference("i", IntegerType)(),
      AttributeReference("u", DecimalType.SYSTEM_DEFAULT)(),
      AttributeReference("b", ByteType)(),
      AttributeReference("d", DoubleType)())
    val secondTable = LocalRelation(
      AttributeReference("s", StringType)(),
      AttributeReference("d", DecimalType(2, 1))(),
      AttributeReference("f", FloatType)(),
      AttributeReference("l", LongType)())
    val thirdTable = LocalRelation(
      AttributeReference("m", StringType)(),
      AttributeReference("n", DecimalType.SYSTEM_DEFAULT)(),
      AttributeReference("p", FloatType)(),
      AttributeReference("q", DoubleType)())
    val forthTable = LocalRelation(
      AttributeReference("m", StringType)(),
      AttributeReference("n", DecimalType.SYSTEM_DEFAULT)(),
      AttributeReference("p", ByteType)(),
      AttributeReference("q", DoubleType)())

    val expectedTypes = Seq(StringType, DecimalType.SYSTEM_DEFAULT, FloatType, DoubleType)

    val unionRelation = widenSetOperationTypes(
      Union(firstTable :: secondTable :: thirdTable :: forthTable :: Nil)).asInstanceOf[Union]
    assert(unionRelation.children.length == 4)
    checkOutput(unionRelation.children.head, expectedTypes)
    checkOutput(unionRelation.children(1), expectedTypes)
    checkOutput(unionRelation.children(2), expectedTypes)
    checkOutput(unionRelation.children(3), expectedTypes)

    assert(unionRelation.children.head.isInstanceOf[Project])
    assert(unionRelation.children(1).isInstanceOf[Project])
    assert(unionRelation.children(2).isInstanceOf[Project])
    assert(unionRelation.children(3).isInstanceOf[Project])
  }

  test("Transform Decimal precision/scale for union except and intersect") {
    def checkOutput(logical: LogicalPlan, expectTypes: Seq[DataType]): Unit = {
      logical.output.zip(expectTypes).foreach { case (attr, dt) =>
        assert(attr.dataType === dt)
      }
    }

    val left1 = LocalRelation(
      AttributeReference("l", DecimalType(10, 8))())
    val right1 = LocalRelation(
      AttributeReference("r", DecimalType(5, 5))())
    val expectedType1 = Seq(DecimalType(10, 8))

    val r1 = widenSetOperationTypes(Union(left1, right1)).asInstanceOf[Union]
    val r2 = widenSetOperationTypes(
      Except(left1, right1, isAll = false)).asInstanceOf[Except]
    val r3 = widenSetOperationTypes(
      Intersect(left1, right1, isAll = false)).asInstanceOf[Intersect]

    checkOutput(r1.children.head, expectedType1)
    checkOutput(r1.children.last, expectedType1)
    checkOutput(r2.left, expectedType1)
    checkOutput(r2.right, expectedType1)
    checkOutput(r3.left, expectedType1)
    checkOutput(r3.right, expectedType1)

    val plan1 = LocalRelation(AttributeReference("l", DecimalType(10, 5))())

    val rightTypes = Seq(ByteType, ShortType, IntegerType, LongType, FloatType, DoubleType)
    val expectedTypes = Seq(DecimalType(10, 5), DecimalType(10, 5), DecimalType(15, 5),
      DecimalType(25, 5), DoubleType, DoubleType)

    rightTypes.zip(expectedTypes).foreach { case (rType, expectedType) =>
      val plan2 = LocalRelation(
        AttributeReference("r", rType)())

      val r1 = widenSetOperationTypes(Union(plan1, plan2)).asInstanceOf[Union]
      val r2 = widenSetOperationTypes(
        Except(plan1, plan2, isAll = false)).asInstanceOf[Except]
      val r3 = widenSetOperationTypes(
        Intersect(plan1, plan2, isAll = false)).asInstanceOf[Intersect]

      checkOutput(r1.children.last, Seq(expectedType))
      checkOutput(r2.right, Seq(expectedType))
      checkOutput(r3.right, Seq(expectedType))

      val r4 = widenSetOperationTypes(Union(plan2, plan1)).asInstanceOf[Union]
      val r5 = widenSetOperationTypes(
        Except(plan2, plan1, isAll = false)).asInstanceOf[Except]
      val r6 = widenSetOperationTypes(
        Intersect(plan2, plan1, isAll = false)).asInstanceOf[Intersect]

      checkOutput(r4.children.last, Seq(expectedType))
      checkOutput(r5.left, Seq(expectedType))
      checkOutput(r6.left, Seq(expectedType))
    }
  }

  test("SPARK-32638: corrects references when adding aliases in WidenSetOperationTypes") {
    val t1 = LocalRelation(AttributeReference("v", DecimalType(10, 0))())
    val t2 = LocalRelation(AttributeReference("v", DecimalType(11, 0))())
    val p1 = t1.select(t1.output.head).as("p1")
    val p2 = t2.select(t2.output.head).as("p2")
    val union = p1.union(p2)
    val wp1 = widenSetOperationTypes(union.select(p1.output.head, $"p2.v"))
    assert(wp1.isInstanceOf[Project])
    // The attribute `p1.output.head` should be replaced in the root `Project`.
    assert(wp1.expressions.forall(_.find(_ == p1.output.head).isEmpty))
    val wp2 = widenSetOperationTypes(Aggregate(Nil, sum(p1.output.head).as("v") :: Nil, union))
    assert(wp2.isInstanceOf[Aggregate])
    assert(wp2.missingInput.isEmpty)
  }

  /**
   * There are rules that need to not fire before child expressions get resolved.
   * We use this test to make sure those rules do not fire early.
   */
  test("make sure rules do not fire early") {
    // InConversion
    val inConversion = TypeCoercion.InConversion
    ruleTest(inConversion,
      In(UnresolvedAttribute("a"), Seq(Literal(1))),
      In(UnresolvedAttribute("a"), Seq(Literal(1)))
    )
    ruleTest(inConversion,
      In(Literal("test"), Seq(UnresolvedAttribute("a"), Literal(1))),
      In(Literal("test"), Seq(UnresolvedAttribute("a"), Literal(1)))
    )
    ruleTest(inConversion,
      In(Literal("a"), Seq(Literal(1), Literal("b"))),
      In(Cast(Literal("a"), StringType),
        Seq(Cast(Literal(1), StringType), Cast(Literal("b"), StringType)))
    )
  }

  test("SPARK-15776 Divide expression's dataType should be casted to Double or Decimal " +
    "in aggregation function like sum") {
    val rules = Seq(FunctionArgumentConversion, Division)
    // Casts Integer to Double
    ruleTest(rules, sum(Divide(4, 3)), sum(Divide(Cast(4, DoubleType), Cast(3, DoubleType))))
    // Left expression is Double, right expression is Int. Another rule ImplicitTypeCasts will
    // cast the right expression to Double.
    ruleTest(rules, sum(Divide(4.0, 3)), sum(Divide(4.0, 3)))
    // Left expression is Int, right expression is Double
    ruleTest(rules, sum(Divide(4, 3.0)), sum(Divide(Cast(4, DoubleType), Cast(3.0, DoubleType))))
    // Casts Float to Double
    ruleTest(
      rules,
      sum(Divide(4.0f, 3)),
      sum(Divide(Cast(4.0f, DoubleType), Cast(3, DoubleType))))
    // Left expression is Decimal, right expression is Int. Another rule DecimalPrecision will cast
    // the right expression to Decimal.
    ruleTest(rules, sum(Divide(Decimal(4.0), 3)), sum(Divide(Decimal(4.0), 3)))
  }

  test("SPARK-17117 null type coercion in divide") {
    val rules = Seq(FunctionArgumentConversion, Division, ImplicitTypeCasts)
    val nullLit = Literal.create(null, NullType)
    ruleTest(rules, Divide(1L, nullLit), Divide(Cast(1L, DoubleType), Cast(nullLit, DoubleType)))
    ruleTest(rules, Divide(nullLit, 1L), Divide(Cast(nullLit, DoubleType), Cast(1L, DoubleType)))
  }

  test("binary comparison with string promotion") {
    val rule = TypeCoercion.PromoteStrings
    ruleTest(rule,
      GreaterThan(Literal("123"), Literal(1)),
      GreaterThan(Cast(Literal("123"), IntegerType), Literal(1)))
    ruleTest(rule,
      LessThan(Literal(true), Literal("123")),
      LessThan(Literal(true), Cast(Literal("123"), BooleanType)))
    ruleTest(rule,
      EqualTo(Literal(Array(1, 2)), Literal("123")),
      EqualTo(Literal(Array(1, 2)), Literal("123")))
    ruleTest(rule,
      GreaterThan(Literal("1.5"), Literal(BigDecimal("0.5"))),
      GreaterThan(Cast(Literal("1.5"), DoubleType), Cast(Literal(BigDecimal("0.5")),
        DoubleType)))
    // Checks that dates/timestamps are not promoted to strings
    val date0301 = Literal(java.sql.Date.valueOf("2017-03-01"))
    val timestamp0301000000 = Literal(Timestamp.valueOf("2017-03-01 00:00:00"))
    val timestamp0301000001 = Literal(Timestamp.valueOf("2017-03-01 00:00:01"))
    // `Date` should be treated as timestamp at 00:00:00 See SPARK-23549
    ruleTest(rule, EqualTo(date0301, timestamp0301000000),
      EqualTo(Cast(date0301, TimestampType), timestamp0301000000))
    ruleTest(rule, LessThan(date0301, timestamp0301000001),
      LessThan(Cast(date0301, TimestampType), timestamp0301000001))
  }

  test("cast WindowFrame boundaries to the type they operate upon") {
    // Can cast frame boundaries to order dataType.
    ruleTest(WindowFrameCoercion,
      windowSpec(
        Seq(UnresolvedAttribute("a")),
        Seq(SortOrder(Literal(1L), Ascending)),
        SpecifiedWindowFrame(RangeFrame, Literal(3), Literal(2147483648L))),
      windowSpec(
        Seq(UnresolvedAttribute("a")),
        Seq(SortOrder(Literal(1L), Ascending)),
        SpecifiedWindowFrame(RangeFrame, Cast(3, LongType), Literal(2147483648L)))
    )
    // Cannot cast frame boundaries to order dataType.
    ruleTest(WindowFrameCoercion,
      windowSpec(
        Seq(UnresolvedAttribute("a")),
        Seq(SortOrder(Literal.default(DateType), Ascending)),
        SpecifiedWindowFrame(RangeFrame, Literal(10.0), Literal(2147483648L))),
      windowSpec(
        Seq(UnresolvedAttribute("a")),
        Seq(SortOrder(Literal.default(DateType), Ascending)),
        SpecifiedWindowFrame(RangeFrame, Literal(10.0), Literal(2147483648L)))
    )
    // Should not cast SpecialFrameBoundary.
    ruleTest(WindowFrameCoercion,
      windowSpec(
        Seq(UnresolvedAttribute("a")),
        Seq(SortOrder(Literal(1L), Ascending)),
        SpecifiedWindowFrame(RangeFrame, CurrentRow, UnboundedFollowing)),
      windowSpec(
        Seq(UnresolvedAttribute("a")),
        Seq(SortOrder(Literal(1L), Ascending)),
        SpecifiedWindowFrame(RangeFrame, CurrentRow, UnboundedFollowing))
    )
  }

  test("SPARK-29000: skip to handle decimals in ImplicitTypeCasts") {
    ruleTest(TypeCoercion.ImplicitTypeCasts,
      Multiply(CaseWhen(Seq((EqualTo(1, 2), Cast(1, DecimalType(34, 24)))),
        Cast(100, DecimalType(34, 24))), Literal(1)),
      Multiply(CaseWhen(Seq((EqualTo(1, 2), Cast(1, DecimalType(34, 24)))),
        Cast(100, DecimalType(34, 24))), Literal(1)))

    ruleTest(TypeCoercion.ImplicitTypeCasts,
      Multiply(CaseWhen(Seq((EqualTo(1, 2), Cast(1, DecimalType(34, 24)))),
        Cast(100, DecimalType(34, 24))), Cast(1, IntegerType)),
      Multiply(CaseWhen(Seq((EqualTo(1, 2), Cast(1, DecimalType(34, 24)))),
        Cast(100, DecimalType(34, 24))), Cast(1, IntegerType)))
  }

  test("SPARK-31468: null types should be casted to decimal types in ImplicitTypeCasts") {
    Seq(AnyTypeBinaryOperator(_, _), NumericTypeBinaryOperator(_, _)).foreach { binaryOp =>
      // binaryOp(decimal, null) case
      ruleTest(TypeCoercion.ImplicitTypeCasts,
        binaryOp(Literal.create(null, DecimalType.SYSTEM_DEFAULT),
          Literal.create(null, NullType)),
        binaryOp(Literal.create(null, DecimalType.SYSTEM_DEFAULT),
          Cast(Literal.create(null, NullType), DecimalType.SYSTEM_DEFAULT)))

      // binaryOp(null, decimal) case
      ruleTest(TypeCoercion.ImplicitTypeCasts,
        binaryOp(Literal.create(null, NullType),
          Literal.create(null, DecimalType.SYSTEM_DEFAULT)),
        binaryOp(Cast(Literal.create(null, NullType), DecimalType.SYSTEM_DEFAULT),
          Literal.create(null, DecimalType.SYSTEM_DEFAULT)))
    }
  }

  test("SPARK-31761: byte, short and int should be cast to long for IntegralDivide's datatype") {
    val rules = Seq(FunctionArgumentConversion, Division, ImplicitTypeCasts)
    // Casts Byte to Long
    ruleTest(TypeCoercion.IntegralDivision, IntegralDivide(2.toByte, 1.toByte),
      IntegralDivide(Cast(2.toByte, LongType), Cast(1.toByte, LongType)))
    // Casts Short to Long
    ruleTest(TypeCoercion.IntegralDivision, IntegralDivide(2.toShort, 1.toShort),
      IntegralDivide(Cast(2.toShort, LongType), Cast(1.toShort, LongType)))
    // Casts Integer to Long
    ruleTest(TypeCoercion.IntegralDivision, IntegralDivide(2, 1),
      IntegralDivide(Cast(2, LongType), Cast(1, LongType)))
    // should not be any change for Long data types
    ruleTest(TypeCoercion.IntegralDivision, IntegralDivide(2L, 1L), IntegralDivide(2L, 1L))
    // one of the operand is byte
    ruleTest(TypeCoercion.IntegralDivision, IntegralDivide(2L, 1.toByte),
      IntegralDivide(2L, Cast(1.toByte, LongType)))
    // one of the operand is short
    ruleTest(TypeCoercion.IntegralDivision, IntegralDivide(2.toShort, 1L),
      IntegralDivide(Cast(2.toShort, LongType), 1L))
    // one of the operand is int
    ruleTest(TypeCoercion.IntegralDivision, IntegralDivide(2, 1L),
      IntegralDivide(Cast(2, LongType), 1L))
  }
}

object TypeCoercionSuite {

  val integralTypes: Seq[DataType] =
    Seq(ByteType, ShortType, IntegerType, LongType)
  val fractionalTypes: Seq[DataType] =
    Seq(DoubleType, FloatType, DecimalType.SYSTEM_DEFAULT, DecimalType(10, 2))
  val numericTypes: Seq[DataType] = integralTypes ++ fractionalTypes
  val atomicTypes: Seq[DataType] =
    numericTypes ++ Seq(BinaryType, BooleanType, StringType, DateType, TimestampType)
  val complexTypes: Seq[DataType] =
    Seq(ArrayType(IntegerType),
      ArrayType(StringType),
      MapType(StringType, StringType),
      new StructType().add("a1", StringType),
      new StructType().add("a1", StringType).add("a2", IntegerType))
  val allTypes: Seq[DataType] =
    atomicTypes ++ complexTypes ++ Seq(NullType, CalendarIntervalType)

  case class AnyTypeUnaryExpression(child: Expression)
    extends UnaryExpression with ExpectsInputTypes with Unevaluable {
    override def inputTypes: Seq[AbstractDataType] = Seq(AnyDataType)
    override def dataType: DataType = NullType
  }

  case class NumericTypeUnaryExpression(child: Expression)
    extends UnaryExpression with ExpectsInputTypes with Unevaluable {
    override def inputTypes: Seq[AbstractDataType] = Seq(NumericType)
    override def dataType: DataType = NullType
  }

  case class AnyTypeBinaryOperator(left: Expression, right: Expression)
    extends BinaryOperator with Unevaluable {
    override def dataType: DataType = NullType
    override def inputType: AbstractDataType = AnyDataType
    override def symbol: String = "anytype"
  }

  case class NumericTypeBinaryOperator(left: Expression, right: Expression)
    extends BinaryOperator with Unevaluable {
    override def dataType: DataType = NullType
    override def inputType: AbstractDataType = NumericType
    override def symbol: String = "numerictype"
  }
}


text:
TrumpCare is the new Trump University
Dick Polman
President Donald Trump speaks during a meeting with his Cabinet in the Cabinet Room of the White House in Washington, Monday, March 13, 2017. From left are, Health and Human Services Secretary Tom Price, Interior Secretary Ryan Zinke, Secretary of State Rex Tillerson, the president, Defense Secretary Jim Mattis, and Secretary of Commerce Wilbur Ross. (Andrew Harnik/AP Photo)
Donald Trump, January 15, 2017: "We're going to have insurance for everybody."
The nonpartisan Congressional Budget Office, March 13, 2017: The House Republican health plan (hailed by Donald Trump as "wonderful") will cause 14 million Americans to lose their insurance in 2018, and 24 million to lose their insurance by 2026.
One of these days or weeks or months, the suckered Trump electorate will hopefully wake up to the indisputable fact that it has been royally conned, just like the students of Trump University. But unless or until that realization sinks in, we denizens of the real world are tasked with tracking the lies and injustices that pass for governance in this shameful era. The current health care phantasmagoria is Exhibit A.
I never thought it'd be possible to write a dramatic story about the Congressional Budget Office, but these are not normal times. The CBO – headed by an economist who was handpicked by House Republicans (including two House Republicans who've joined the Trump team, budget director Mike Mulvaney and health secretary Tom Price) – shredded the Republican health care plan yesterday in a dryly devastating report that tracked the human casualties clear into the next decade. Which sounds about right, since we're talking here about a Republican health plan.
The CBO's projections jibe with analyses undertaken by the free and independent press. The gist is that the GOP's replace-Obamacare plan would make coverage too costly for a broad swath of modest-income and downscale Americans (most notably red-state Trump voters) who get coverage now. That's because the GOP's first priority is to spend a heckuva lot less money by slashing the current federal subsidies and Medicaid outlays.
In 2018 alone, says the CBO, "14 million more people would be uninsured under the legislation than under current (Obamacare) law." Yesterday, The Wall Street Journal crunched the Republican numbers and showed us why. The Journal found that a 62-year-old Nebraskan earning $18,000 a year and currently paying $760 out of pocket for annual coverage would instead be compelled to pay $20,000 out of pocket for annual coverage.
The Trump team clearly sensed that bad publicity was in the offing. Over the weekend, in a lame effort to preempt the impending CBO report, health secretary Price insisted: "We don't believe that individuals will lose coverage at all." Budget director Mulvaney even contended that the CBO shouldn't get involved at all, that "estimating the impact of a bill of this size probably isn't the best use of their time" – which was hilarious, because gauging the cost and consequences of a major bill is precisely what the CBO is mandated to do.
Then, yesterday, after the CBO concluded that tens of millions would lose coverage, Price insisted that the report was all wrong. But given this lying regime's addiction to alternative facts, I'm siding with the CBO.
Speaking of lies, Mulvaney actually said over the weekend that Obamacare was passed in 2009 without a single House hearing. (Praising the Republican bill, he said: "We already had two committee hearings, which I believe is two more than Obamacare had in the House.") That's a whopper worthy of Kellyanne Conway. Truth is, Obamacare weathered roughly 20 hearings in the House.
As an old friend of mine said the other day, "The whole country is now Trump University, except this time the victims can't sue." True that. But they can still vote.
Seriously, folks: If the Democrats can't expose this blatant con to the '18 midterm electorate, if they're incapable of selling themselves as the party that protects people's health coverage, then they should simply disband.
Trump and the Republicans have basically boxed themselves in. If they don't deliver the long-promised death of Obamacare to their Obama-hating base – if, for instance, the more moderate Republican Senate stiffs the House ideologues and leaves Obamacare in place – then the Republicans base could stay home en masse in the '18 midterms and precipitate a pro-Democratic "bloodbath." (Trump uses that word, and Paul Ryan concurs.)
On the other hand, if Republicans do somehow enact a replacement law, signed by Trump, that causes millions of people to lose their coverage, an infuriated coalition of mobilized liberals and coverage-deprived Trumpkins could still precipitate a pro-Democratic bloodbath.
But wait, there's a third scenario: If House Republicans vote to pass their draconian health plan, and it's subsequently killed in the Senate (highly likely), House Republicans would still be forced to defend their votes in the '18 midterms. Republican Senator Tom Cotton warned over the weekend: "If they vote for this bill, they're going to put the House majority at risk…I would say to my friends in the House of Representatives with whom I serve, do not walk the plank and vote for a bill that cannot pass the Senate and then have to face the consequences of that vote."
By the way, Trump tweets: "We are making great progress with healthcare…Republicans coming together to get job done." We are shocked that Trump's assessment doesn't mesh with reality.
Follow me on Twitter, @dickpolman1, and on Facebook.
Brought to you by National Interest
Dick Polman's column appears weekly on WHYY.org/News. He also contributes to The Atlantic online and is a writer-in-residence at the University of Pennsylvania.
About Dick Polman
@dickpolman1 nationalinterest@whyy.org

redpajama_set_name: RedPajamaCommonCrawl


Today 12/16/2012 there was threatening comments toward the Bristol Central High School by a juvenile student there. No one was in any danger but the threat was taken seriously by the police department and a juvenile was arrested for the charges of threatening and breach of peace. Two additional officers were added to assist the School Resource Officers due to recent events. At this time there is no other information being released.


6 Million Mortgages in Spain.
Cost of the mortgage floor: 1,000 on average per year per family.
We would like to provide you with some key issues and answers to most of the questions arising from the recently introduced new Energy Efficiency Certificates.
In compliance with the EU Directive 2010/31/EU, the new law is regulated by the Royal Decree 235/2013 which has revoked the previous Royal Decree 47/2007.
RESIDENT OR FISCAL RESIDENT. YOU MUST REPORT YOUR ASSETS ABROAD BY 30TH APRIL 2013 DEADLINE.
This new obligation for residents to report their assets abroad valued over 50,000 must be declared by 30th April this year, (31st March on following years). This highlights the importance of ascertaining your Fiscal Residency. Your Residency dictates whether or not you have a requirement to complete this new tax form.
The Spanish Tax Office has recently introduced a new tax form concerning fraud and money laundering.
This is the tax form no. 720 and it must to be completed by all tax residents in Spain. All below mentioned assets held as of the 31.12.2012 with a value of more than 50,000 Euros must be submitted on this form between March and April 2013.
WHY SHOULD I BUY PROPERTY IN SPAIN BEFORE 2013?
Reading this article could help you decide to buy a property in Spain before the year 2012 comes to an end.
Are you entrepreneur? Spain is still one of the most popular places for people to launch their own business start-up.
Perhaps your SIP card has been cancelled recently or you need to issue one. The following information may result of your interest as there is lots of confusion on this matter.
Non resident Income Tax must be completed by all non residents as a consequence of owning property in Spain.
For a tax purpose you are considered Spanish Resident if you stay in our country more than 183 days within a year period.
SPANISH VAT RATES. THE IVA ISSUE.
The Spanish Government has approved a Spanish VAT (IVA) increase as part of sweeping austerity economic measures. We may find three different rates affecting to different goods and services.
PERSONAL INCOME TAX FOR RESIDENTS (MOD.100). 2012 HIGHLIGHTS.
Personal Income Tax (commonly known as IRPF. Mod 100) must be completed by all residents even when there is no tax to pay on their income. Many residents are ignoring their responsibility of paying taxes even if they are only receiving a pension from the UK.
HAVE YOU LOST THE DEPOSIT YOU PUT DOWN ON A PROPERTY YOU PURCHASED OFF PLAN?
It's time for UK taxpayers to declare offshore assets and income!
DO YOU OWN PROPERTY IN SPAIN AND HAVE RECEIVED THE LETTER FROM THE TAX OFFICE?
Our team is formed of highly experienced lawyers, solicitors and tax consultants with an expertise in Spanish law. We help individuals and companies alike, who are operating in or relocating to Spain, by providing legal, tax and financial assistance.


text:
Deseret NewsInDepth
Everyone has a role in suicide prevention. Here's how you can help
By Lois M. Collins @loisco
Published: June 8, 2018 3:02 pm
Richard Shotwell, Invision
FILE - In this Sept. 11, 2016 file photo, Anthony Bourdain arrives at night two of the Creative Arts Emmy Awards at the Microsoft Theater in Los Angeles. Bourdain has been found dead in his hotel room in France, Friday, June 8, 2018, while working on his CNN series on culinary traditions around the world. (Photo by Richard Shotwell/Invision/AP, File)
Suicide rates up across U.S., but mental health may not play primary role
Twitter reacts to Anthony Bourdain's death
Kate Spade's husband says she suffered from depression
SALT LAKE CITY — When it comes to saving the life of a friend or loved one contemplating suicide, there's only one thing you can do wrong — nothing.
That's the message of experts nationwide, who say that while suicide is a public health and community challenge that requires improved access to mental health services, it also requires small steps anyone can make.
Individuals have the power to save lives, asserts Joel Dvoskin, a clinical assistant professor of psychology at the University of Arizona Medical School in Tucson. He says many suicides are not a surprise to friends and acquaintances; many people who die by suicide have made explicit or ambiguous threats. Others have "observable signs of intolerable psychological pain," like expressing or showing inability to think about the future, disconnectedness or anger, among others.
Because people take their own lives "to stop that pain, if there's a nonsuicidal option that's attractive, they might not kill themselves."
Brent N. Clarke, Invision
FILE - In this Oct. 5, 2017 file photo, Executive Producer and narrator chef Anthony Bourdain attends the premiere of "Wasted! The Story of Food Waste" at the Alamo Drafthouse Cinema in New York. Bourdain has been found dead in his hotel room in France, Friday, June 8, 2018, while working on his CNN series on culinary traditions around the world. (Photo by Brent N. Clarke/Invision/AP, File)
Friends, family and even first responders may be able to help ease the pain and find paths to help, from mental health care to spiritual care to a good friendship. Sometimes a friend who will sit with them can be the difference between life and death.
Numerous experts consulted for this story said the first step is a direct, respectfully asked question: Are you thinking of harming yourself?
Express empathy and concern, then listen without judgment, says Craig Bryan, the executive director of the National Center for Veterans Studies and an associate professor of psychology at the University of Utah. He suggests offering something along the lines of "I'm sorry you're having to go through this right now."
Experts used to say never mention suicide, but now agree that approach was harmful. Ask — and be direct.
The trick is to bring up suicide risk in a way that makes it OK to answer "yes," says Bryan. For example, "A lot of people in this situation often find themselves wishing they were dead or thinking about suicide. 'Have you been having thoughts like this, even if only a little bit?'"
FILE - In this Sept. 11, 2016 file photo, Anthony Bourdain winner of the award for outstanding informational series or special for "Anthony Bourdain: Parts Unknown" attends the Governors Ball during night two of the Creative Arts Emmy Awards at the Microsoft Theater in Los Angeles. Bourdain has been found dead in his hotel room in France, Friday, June 8, 2018, while working on his CNN series on culinary traditions around the world. (Photo by Richard Shotwell/Invision/AP, File)
If someone says he's going to kill himself, Dvoskin says to call 911. "He might be mad at you, but he won't be dead. If he says something that raises the hair on the back of your neck, ask questions. Show concern."
Bryan suggests that "talking about the person's reasons for living and sense of purpose and meaning in life can offset emotional distress and reduce suicidal intent." He recommends something along this line: "With all of this going on, what are some things that help keep you going each day? What provides you with a sense of purpose or meaning?"
"What can I do to help?" is a simple way to offer support and assistance. Then follow through and provide that help.
People in despair may lack the ability to make much effort to "get help," Dvoskin says. Make it easy for them. "Would you like me to make an appointment for you? Can I drive you there?" That requires just a head nod. "I statements are better than a you statement: 'I am worried about you,' not 'you seem depressed.'"
If one is not close to the person believed to be contemplating suicide, or if one is worried about his or her own safety, call 911, as well, he noted.
If one suspects a loved one is considering suicide, experts say to make sure guns are not easily accessible. Bryan says they need to be locked away, the ammunition stored separately, in such cases.
Andy Kropa, Invision
FILE - In this Nov. 2, 2016, file photo, Anthony Bourdain participates in the BUILD Speaker Series to discuss the online film series "Raw Craft" at AOL Studios in New York. Bourdain has been found dead in his hotel room in France, Friday, June 8, 2018, while working on his CNN series on culinary traditions around the world. (Photo by Andy Kropa/Invision/AP, File)
Taking that step is "akin to taking someone's car keys when they've been drinking. Although it's not certain that someone who has been drinking will get into a car crash, risk is increased and it's just not safe," he says, "so safety precautions are best. Likewise, it's not certain that someone who is distressed will attempt suicide, but risk is increased and it's not safe for them to have easy access to firearms. Enacting gun safety measures is actually the most important thing people can do. They should also enact medication safety procedures to limit access to meds during periods of distress," says Bryan.
Though slightly more than half of people who die by suicide had not been diagnosed with or treated for a mental illness, Dr. Soroya Bacchus, a Los Angeles board-certified psychiatrist, says increased rates of depression and other mood disorders create increased risk.
"Depression and other mood disorders are often suffered silently. At some point, a trigger can set off an episode that can lead to suicide. The prevalence for depression is about 7 percent of the population," she wrote in an email. "Most people who suffer don't get the treatment they need."
Depression and suicidal ideology are treatable, she says.
It's helpful to know warning signs, including withdrawing from family and friends, losing interest in once-enjoyed activities or talking about feeling hopeless and helpless. Be aware people display different signs and an apparent improvement may be another indication one needs to check in and make sure that individual is OK.
Along with the dos, experts offer don'ts, including don't trivialize someone's feelings or blow it off as attention-seeking. They all say to avoid "judging" the distressed person's feelings. Instead, focus on listening.
Experts are divided on whether "contagion," in which one suicide prompts others, is a signficant concern. But recent high-proflie suicides do make it important to pay attention, says Bacchus. "We do worry about copycat suicides. It's not that people are mimicking the behavior, but to me it's that people are focusing on their own feelings of loneliness and depression when things like this are happening. Copycat suicide is the result of people starting to focus on those feelings in themselves."
She emphasizes that "thoughts of suicide are NEVER normal. The best thing for them to do is reach out if they're noticing any of these feelings. If they start thinking about suicide they need to call a help line, or look through their insurance to find a psychiatrist. Especially if they live alone."
Saving a life sometimes boils down to telling someone or doing something. "If you'll sit with someone and help figure out the next step, you can help save their life," says Dvoskin.
Utah health officials emphasize the importance of knowing about and using resources:
National Hotline: 1-800-273-8255 (TALK)
UNI Crisis line: 801-587-3000
Suicide Prevention Resource Center: www.sprc.org
National Alliance of Mental Illness (NAMI): www.nami.org
Parent Resource Program:www.jasonfoundation.com/community/
The SafeUT app will connect someone directly with a crisis line, as well.
National Suicide Prevention Lifeline: www.suicidepreventionlifeline.org/
Lois M. Collins Lois M. Collins is a reporter and columnist for the Deseret News.
@loisco
lois@deseretnews.com
InDepth July 11, 2019 Asleep at the wheel: How a secret study on Utah's roads could be putting drivers at risk
InDepth July 9, 2019 FBI, ICE are using facial recognition to scan millions of Americans' faces without consent. Is your face one of them?
InDepth July 5, 2019 A case for trade school: Could it fix higher education?
April 9, 2019 QUIZ: How much do you know about FanX 2019 celebrities?
InDepth July 7, 2019 As Seattle cracks down on McMansions, a question lingers: Are huge homes morally wrong?
InDepth July 10, 2019 The Apollo 11 moon landing brought America together. Could that happen again today?


text:
The Rooster
Behind The Scenes of Shawn Mendes: Shawn Mendes In Wonder Movie Review
The Cleveland Indians change their long-lived name
Lights, camera, holiday action!
Fun activities to stay safe this winter
Old Salem Moravian Candle Tea: A timeless, treasured tradition
New album "Wonder" leaves listeners lost in Wonderland
"Ocean at Bodega Bay" by prayerfriends is licensed under CC BY-NC-ND 2.0
Tori Suhre, Opinions Editor
The popular Canadian singer Shawn Mendes released his album "Wonder" on Dec. 4, 2020. The album was teased a few months prior by the release of a single that shared the same name as its album, which was released on Oct. 2, 2020. "Wonder" is also Mendes' fourth No. 1 on the Billboard 200 chart.
Many fans speculate that the songs contained in the album are meant for his girlfriend, Camila Cabello. The song "Always been you" references Mendes' confession that his songs released in the past were all about her.
With a total of 14 songs, Mendes covers topics such as his love for Cabello, his anxiety and insecurities, as well as his thoughts about fame. His collaboration with Justin Beiber in the song "Monster" describes how society puts celebrities on a pedestal and watches them fall after making a single mistake. Mendes expresses his anxiety and fear of the same thing happening to himself, if he were to make a bad decision and taint his reputation.
"You put me on a pedestal and tell me I'm the best. … Fill me up with confidence, I say what's in my chest / Spill my words and tear me down until there's nothing left."
The first song written for the album was "Song For No One," which was written three years prior to the album's first conception. Contrary to the title, the song is in fact about someone.
"The song is about Camila," Mendes said. "But I guess it was a love song for no one. This is a song for nobody, 'cause I was like living on my own in Toronto and I kinda really had these feelings for Camila that I wasn't willing to accept or admit and there's a real loneliness in the vocal."
The song "Monster" was also written the same week three years ago as "Song For No One." They were both written because he was feeling lonely and vulnerable. "Monster" was written from the perspective of both Beiber and Mendes about being young and famous.
"We are blessed," Mendes said about Beiber. "Truly something I often forget and he helps me to remember."
Almost all of the lyric videos in the album have a unique animation style, reminiscent of the music video for "Take On Me" by A-ha. The animations are all hand drawn and charmingly simplistic, alternating between a few sketches to give a sense of life and motion.
Personally, I have been listening to the album every day since the day of its release. It is 100% worth giving a listen to, whether or not you like pop music. My personal favorite from the album is "Teach Me How To Love," but the fan-favorite appears to be "Dream."
"This album freed me from a demon that was trying to control my life," Mendes said. "Freed me from expectations… I felt like a new person at the end of this process."
Howdy, my name is Tori Suhre. I am a senior and the Opinions Editor for the Reagan Rooster. I am on the English Academic Team and I am the president of...
What is the best show on Netflix?
"Outer Banks"
"Criminal Minds"
"Grey's Anatomy"
"Supernatural"
"Umbrella Academy"
"Riverdale"
"The Flash"
"Gossip Girl"
RWRHS
Jan 13 / Varsity Men's Basketball
Dec 1 / Varsity Volleyball
West Forsyth
Nov 24 / Varsity Volleyball
"Clouds" is a crying film for sure
2020 People's Choice Awards
Mixed reactions to "Rebecca" remake on Netflix
"Hubie Halloween" makes for Halloween fun
Mulan released to Disney Plus
Are movie remakes bad?
COIN's "Dreamland" is a dream
Star Wars: 'The Rise of Skywalker' receives mixed reviews from fans and critics
Mendes and Cabello: Is their relationship fake?
The Student News Site of Ronald W. Reagan High School


OnRPG Shotgun News 4/17: Prime World, Darkfall, Guns of Icarus, and Much More!
District 187: Sin Streets (D187), an urban combat FPS published by CJ Games Global, unleashes its biggest update ever, "Winter of Wars."
This games was the best game i've ever played. It was perfect. Why you don't lauch it again in steam? Pleaseeeeeee.
what reason did you end the game? Please open!


text:
Извори () је насеље у општини Звечан, Косово и Метохија, Република Србија.

Демографија
Према проценама из 2009. године које су коришћене за попис на Косову 2011. године, ово насеље је имало 7 становника, већина Срби.

Референце

Насељена места општине Звечан
Насељена места на Косову и Метохији
Википројект географија/Насеља у Србији

redpajama_set_name: RedPajamaWikipedia


In celebration of the companies 100th anniversary Sun-Maid is giving away free downloadable copies of their 100th Anniversary Book. This will be right up the alley of dried-fruit and raisin enthusiasts as the book lets readers in on the deep history of the food items. In addition to giving readers an insight to the history of the products the Sun-Maid Raisins and Dried Fruits 100th anniversary Book will allow enthusiasts to peak into the world of how these delicious products are marketed. On top of that the book will take readers on a journey to discover how the fruits are grown and processed throughout the world.
The book is sorted into 6 unique chapters. The first chapters entitled The Basics will take readers deep into the Sun-Maid time-line beginning in 1900 all the way through present day. This chapter also touches on important topics such as how raisins and dried fruits are used, festivals and holidays involving the fruits, how the fresh fruit is turned into dried fruit, and how to make sense of those tricky serving sizes.
Chapter two is going to take readers straight into the background story of the fruits. The chapter covers a wide variety of topics including the use of the fruits in ancient times, the impact of the fruits as they relate to California, irrigation water, and the Thompson Seedless story.
Chapter three is all about the company. This chapter focuses on Sun-Maid advertising, raisins in pop-culture, the Sun-Maid girl, and cooperatives.
The next chapter focuses on the growing process. In chapter four readers will come to understand ideas such as sustainable harvesting, hand harvesting, and mechanical harvesting.
Finally in chapters five and six readers will gain insight into important facts about raisins and dried fruits and will also be given plenty of tasty recipes. Some of the recipe categories include breakfast, breads, desserts, and appetizers.