text: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  #include <cstdint>
  #include <cstdio>
  #include <functional>
  #include <memory>
  #include <string>
  #include <vector>
  
  #include <gmock/gmock.h>
  #include <gtest/gtest.h>
  
  #include "arrow/array.h"
  #include "arrow/array/builder_decimal.h"
  #include "arrow/buffer.h"
  #include "arrow/chunked_array.h"
  #include "arrow/extension_type.h"
  #include "arrow/status.h"
  #include "arrow/testing/extension_type.h"
  #include "arrow/testing/gtest_common.h"
  #include "arrow/testing/gtest_util.h"
  #include "arrow/testing/random.h"
  #include "arrow/type.h"
  #include "arrow/type_fwd.h"
  #include "arrow/type_traits.h"
  #include "arrow/util/bitmap.h"
  #include "arrow/util/checked_cast.h"
  #include "arrow/util/decimal.h"
  
  #include "arrow/compute/api_vector.h"
  #include "arrow/compute/cast.h"
  #include "arrow/compute/kernel.h"
  #include "arrow/compute/kernels/codegen_internal.h"
  #include "arrow/compute/kernels/test_util.h"
  
  namespace arrow {
  
  using internal::checked_cast;
  using internal::checked_pointer_cast;
  
  namespace compute {
  
  static std::shared_ptr<Array> InvalidUtf8(std::shared_ptr<DataType> type) {
    return ArrayFromJSON(type,
                         "["
                         R"(
                         "Hi",
                         "olá mundo",
                         "你好世界",
                         "",
                         )"
                         "\"\xa0\xa1\""
                         "]");
  }
  
  static std::shared_ptr<Array> FixedSizeInvalidUtf8(std::shared_ptr<DataType> type) {
    if (type->id() == Type::FIXED_SIZE_BINARY) {
      // Assume a particular width for testing
      EXPECT_EQ(3, checked_cast<const FixedSizeBinaryType&>(*type).byte_width());
    }
    return ArrayFromJSON(type,
                         "["
                         R"(
                         "Hi!",
                         "lá",
                         "你",
                         "   ",
                         )"
                         "\"\xa0\xa1\xa2\""
                         "]");
  }
  
  static std::vector<std::shared_ptr<DataType>> kNumericTypes = {
      uint8(), int8(),   uint16(), int16(),   uint32(),
      int32(), uint64(), int64(),  float32(), float64()};
  
  static std::vector<std::shared_ptr<DataType>> kIntegerTypes = {
      int8(), uint8(), int16(), uint16(), int32(), uint32(), int64(), uint64()};
  
  static std::vector<std::shared_ptr<DataType>> kDictionaryIndexTypes = kIntegerTypes;
  
  static std::vector<std::shared_ptr<DataType>> kBaseBinaryTypes = {
      binary(), utf8(), large_binary(), large_utf8()};
  
  static void AssertBufferSame(const Array& left, const Array& right, int buffer_index) {
    ASSERT_EQ(left.data()->buffers[buffer_index].get(),
              right.data()->buffers[buffer_index].get());
  }
  
  static void CheckCast(std::shared_ptr<Array> input, std::shared_ptr<Array> expected,
                        CastOptions options = CastOptions{}) {
    options.to_type = expected->type();
    CheckScalarUnary("cast", input, expected, &options);
  }
  
  static void CheckCastFails(std::shared_ptr<Array> input, CastOptions options) {
    ASSERT_RAISES(Invalid, Cast(input, options))
        << "\n  to_type:   " << options.to_type->ToString()
        << "\n  from_type: " << input->type()->ToString()
        << "\n  input:     " << input->ToString();
  
    // For the scalars, check that at least one of the input fails (since many
    // of the tests contains a mix of passing and failing values). In some
    // cases we will want to check more precisely
    int64_t num_failing = 0;
    for (int64_t i = 0; i < input->length(); ++i) {
      ASSERT_OK_AND_ASSIGN(auto scalar, input->GetScalar(i));
      num_failing += static_cast<int>(Cast(scalar, options).status().IsInvalid());
    }
    ASSERT_GT(num_failing, 0);
  }
  
  static void CheckCastZeroCopy(std::shared_ptr<Array> input,
                                std::shared_ptr<DataType> to_type,
                                CastOptions options = CastOptions::Safe()) {
    ASSERT_OK_AND_ASSIGN(auto converted, Cast(*input, to_type, options));
    ValidateOutput(*converted);
  
    ASSERT_EQ(input->data()->buffers.size(), converted->data()->buffers.size());
    for (size_t i = 0; i < input->data()->buffers.size(); ++i) {
      AssertBufferSame(*input, *converted, static_cast<int>(i));
    }
  }
  
  static std::shared_ptr<Array> MaskArrayWithNullsAt(std::shared_ptr<Array> input,
                                                     std::vector<int> indices_to_mask) {
    auto masked = input->data()->Copy();
    masked->buffers[0] = *AllocateEmptyBitmap(input->length());
    masked->null_count = kUnknownNullCount;
  
    using arrow::internal::Bitmap;
    Bitmap is_valid(masked->buffers[0], 0, input->length());
    if (auto original = input->null_bitmap()) {
      is_valid.CopyFrom(Bitmap(original, input->offset(), input->length()));
    } else {
      is_valid.SetBitsTo(true);
    }
  
    for (int i : indices_to_mask) {
      is_valid.SetBitTo(i, false);
    }
    return MakeArray(masked);
  }
  
  TEST(Cast, CanCast) {
    auto ExpectCanCast = [](std::shared_ptr<DataType> from,
                            std::vector<std::shared_ptr<DataType>> to_set,
                            bool expected = true) {
      for (auto to : to_set) {
        EXPECT_EQ(CanCast(*from, *to), expected) << "  from: " << from->ToString() << "\n"
                                                 << "    to: " << to->ToString();
      }
    };
  
    auto ExpectCannotCast = [ExpectCanCast](std::shared_ptr<DataType> from,
                                            std::vector<std::shared_ptr<DataType>> to_set) {
      ExpectCanCast(from, to_set, /*expected=*/false);
    };
  
    ExpectCanCast(null(), {boolean()});
    ExpectCanCast(null(), kNumericTypes);
    ExpectCanCast(null(), kBaseBinaryTypes);
    ExpectCanCast(
        null(), {date32(), date64(), time32(TimeUnit::MILLI), timestamp(TimeUnit::SECOND)});
    ExpectCanCast(dictionary(uint16(), null()), {null()});
  
    ExpectCanCast(boolean(), {boolean()});
    ExpectCanCast(boolean(), kNumericTypes);
    ExpectCanCast(boolean(), {utf8(), large_utf8()});
    ExpectCanCast(dictionary(int32(), boolean()), {boolean()});
  
    ExpectCannotCast(boolean(), {null()});
    ExpectCannotCast(boolean(), {binary(), large_binary()});
    ExpectCannotCast(boolean(), {date32(), date64(), time32(TimeUnit::MILLI),
                                 timestamp(TimeUnit::SECOND)});
  
    for (auto from_numeric : kNumericTypes) {
      ExpectCanCast(from_numeric, {boolean()});
      ExpectCanCast(from_numeric, kNumericTypes);
      ExpectCanCast(from_numeric, {utf8(), large_utf8()});
      ExpectCanCast(dictionary(int32(), from_numeric), {from_numeric});
  
      ExpectCannotCast(from_numeric, {null()});
    }
  
    for (auto from_base_binary : kBaseBinaryTypes) {
      ExpectCanCast(from_base_binary, {boolean()});
      ExpectCanCast(from_base_binary, kNumericTypes);
      ExpectCanCast(from_base_binary, kBaseBinaryTypes);
      ExpectCanCast(dictionary(int64(), from_base_binary), {from_base_binary});
  
      // any cast which is valid for the dictionary is valid for the DictionaryArray
      ExpectCanCast(dictionary(uint32(), from_base_binary), kBaseBinaryTypes);
      ExpectCanCast(dictionary(int16(), from_base_binary), kNumericTypes);
  
      ExpectCannotCast(from_base_binary, {null()});
    }
  
    ExpectCanCast(utf8(), {timestamp(TimeUnit::MILLI)});
    ExpectCanCast(large_utf8(), {timestamp(TimeUnit::NANO)});
    ExpectCannotCast(timestamp(TimeUnit::MICRO),
                     {binary(), large_binary()});  // no formatting supported
  
    ExpectCanCast(fixed_size_binary(3),
                  {binary(), utf8(), large_binary(), large_utf8(), fixed_size_binary(3)});
    // Doesn't fail since a kernel exists (but it will return an error when executed)
    // ExpectCannotCast(fixed_size_binary(3), {fixed_size_binary(5)});
  
    ExtensionTypeGuard smallint_guard(smallint());
    ExpectCanCast(smallint(), {int16()});  // cast storage
    ExpectCanCast(smallint(),
                  kNumericTypes);  // any cast which is valid for storage is supported
    ExpectCannotCast(null(), {smallint()});  // FIXME missing common cast from null
  
    ExpectCanCast(date32(), {utf8(), large_utf8()});
    ExpectCanCast(date64(), {utf8(), large_utf8()});
    ExpectCanCast(timestamp(TimeUnit::NANO), {utf8(), large_utf8()});
    ExpectCanCast(timestamp(TimeUnit::MICRO), {utf8(), large_utf8()});
    ExpectCanCast(time32(TimeUnit::MILLI), {utf8(), large_utf8()});
    ExpectCanCast(time64(TimeUnit::NANO), {utf8(), large_utf8()});
  }
  
  TEST(Cast, SameTypeZeroCopy) {
    std::shared_ptr<Array> arr = ArrayFromJSON(int32(), "[0, null, 2, 3, 4]");
    ASSERT_OK_AND_ASSIGN(std::shared_ptr<Array> result, Cast(*arr, int32()));
  
    AssertBufferSame(*arr, *result, 0);
    AssertBufferSame(*arr, *result, 1);
  }
  
  TEST(Cast, ZeroChunks) {
    auto chunked_i32 = std::make_shared<ChunkedArray>(ArrayVector{}, int32());
    ASSERT_OK_AND_ASSIGN(Datum result, Cast(chunked_i32, utf8()));
  
    ASSERT_EQ(result.kind(), Datum::CHUNKED_ARRAY);
    AssertChunkedEqual(*result.chunked_array(), ChunkedArray({}, utf8()));
  }
  
  TEST(Cast, CastDoesNotProvideDefaultOptions) {
    std::shared_ptr<Array> arr = ArrayFromJSON(int32(), "[0, null, 2, 3, 4]");
    ASSERT_RAISES(Invalid, CallFunction("cast", {arr}));
  }
  
  TEST(Cast, FromBoolean) {
    std::string vals = "[1, 0, null, 1, 0, 1, 1, null, 0, 0, 1]";
    CheckCast(ArrayFromJSON(boolean(), vals), ArrayFromJSON(int32(), vals));
  }
  
  TEST(Cast, ToBoolean) {
    for (auto type : kNumericTypes) {
      CheckCast(ArrayFromJSON(type, "[0, null, 127, 1, 0]"),
                ArrayFromJSON(boolean(), "[false, null, true, true, false]"));
    }
  
    // Check negative numbers
    for (auto type : {int8(), float64()}) {
      CheckCast(ArrayFromJSON(type, "[0, null, 127, -1, 0]"),
                ArrayFromJSON(boolean(), "[false, null, true, true, false]"));
    }
  }
  
  TEST(Cast, ToIntUpcast) {
    std::vector<bool> is_valid = {true, false, true, true, true};
  
    // int8 to int32
    CheckCast(ArrayFromJSON(int8(), "[0, null, 127, -1, 0]"),
              ArrayFromJSON(int32(), "[0, null, 127, -1, 0]"));
  
    // uint8 to int16, no overflow/underrun
    CheckCast(ArrayFromJSON(uint8(), "[0, 100, 200, 255, 0]"),
              ArrayFromJSON(int16(), "[0, 100, 200, 255, 0]"));
  }
  
  TEST(Cast, OverflowInNullSlot) {
    CheckCast(
        MaskArrayWithNullsAt(ArrayFromJSON(int32(), "[0, 87654321, 2000, 1000, 0]"), {1}),
        ArrayFromJSON(int16(), "[0, null, 2000, 1000, 0]"));
  }
  
  TEST(Cast, ToIntDowncastSafe) {
    // int16 to uint8, no overflow/underflow
    CheckCast(ArrayFromJSON(int16(), "[0, null, 200, 1, 2]"),
              ArrayFromJSON(uint8(), "[0, null, 200, 1, 2]"));
  
    // int16 to uint8, overflow
    CheckCastFails(ArrayFromJSON(int16(), "[0, null, 256, 0, 0]"),
                   CastOptions::Safe(uint8()));
    // ... and underflow
    CheckCastFails(ArrayFromJSON(int16(), "[0, null, -1, 0, 0]"),
                   CastOptions::Safe(uint8()));
  
    // int32 to int16, no overflow/underflow
    CheckCast(ArrayFromJSON(int32(), "[0, null, 2000, 1, 2]"),
              ArrayFromJSON(int16(), "[0, null, 2000, 1, 2]"));
  
    // int32 to int16, overflow
    CheckCastFails(ArrayFromJSON(int32(), "[0, null, 2000, 70000, 2]"),
                   CastOptions::Safe(int16()));
  
    // ... and underflow
    CheckCastFails(ArrayFromJSON(int32(), "[0, null, 2000, -70000, 2]"),
                   CastOptions::Safe(int16()));
  
    CheckCastFails(ArrayFromJSON(int32(), "[0, null, 2000, -70000, 2]"),
                   CastOptions::Safe(uint8()));
  }
  
  TEST(Cast, IntegerSignedToUnsigned) {
    auto i32s = ArrayFromJSON(int32(), "[-2147483648, null, -1, 65535, 2147483647]");
    // Same width
    CheckCastFails(i32s, CastOptions::Safe(uint32()));
    // Wider
    CheckCastFails(i32s, CastOptions::Safe(uint64()));
    // Narrower
    CheckCastFails(i32s, CastOptions::Safe(uint16()));
  
    CastOptions options;
    options.allow_int_overflow = true;
  
    CheckCast(i32s,
              ArrayFromJSON(uint32(), "[2147483648, null, 4294967295, 65535, 2147483647]"),
              options);
    CheckCast(i32s,
              ArrayFromJSON(
                  uint64(),
                  "[18446744071562067968, null, 18446744073709551615, 65535, 2147483647]"),
              options);
    CheckCast(i32s, ArrayFromJSON(uint16(), "[0, null, 65535, 65535, 65535]"), options);
  
    // Fail because of overflow (instead of underflow).
    i32s = ArrayFromJSON(int32(), "[0, null, 0, 65536, 2147483647]");
    CheckCastFails(i32s, CastOptions::Safe(uint16()));
  
    CheckCast(i32s, ArrayFromJSON(uint16(), "[0, null, 0, 0, 65535]"), options);
  }
  
  TEST(Cast, IntegerUnsignedToSigned) {
    auto u32s = ArrayFromJSON(uint32(), "[4294967295, null, 0, 32768]");
    // Same width
    CheckCastFails(u32s, CastOptions::Safe(int32()));
  
    // Narrower
    CheckCastFails(u32s, CastOptions::Safe(int16()));
    CheckCastFails(u32s->Slice(1), CastOptions::Safe(int16()));
  
    CastOptions options;
    options.allow_int_overflow = true;
  
    CheckCast(u32s, ArrayFromJSON(int32(), "[-1, null, 0, 32768]"), options);
    CheckCast(u32s, ArrayFromJSON(int64(), "[4294967295, null, 0, 32768]"), options);
    CheckCast(u32s, ArrayFromJSON(int16(), "[-1, null, 0, -32768]"), options);
  }
  
  TEST(Cast, ToIntDowncastUnsafe) {
    CastOptions options;
    options.allow_int_overflow = true;
  
    // int16 to uint8, no overflow/underflow
    CheckCast(ArrayFromJSON(int16(), "[0, null, 200, 1, 2]"),
              ArrayFromJSON(uint8(), "[0, null, 200, 1, 2]"), options);
  
    // int16 to uint8, with overflow/underflow
    CheckCast(ArrayFromJSON(int16(), "[0, null, 256, 1, 2, -1]"),
              ArrayFromJSON(uint8(), "[0, null, 0, 1, 2, 255]"), options);
  
    // int32 to int16, no overflow/underflow
    CheckCast(ArrayFromJSON(int32(), "[0, null, 2000, 1, 2, -1]"),
              ArrayFromJSON(int16(), "[0, null, 2000, 1, 2, -1]"), options);
  
    // int32 to int16, with overflow/underflow
    CheckCast(ArrayFromJSON(int32(), "[0, null, 2000, 70000, -70000]"),
              ArrayFromJSON(int16(), "[0, null, 2000, 4464, -4464]"), options);
  }
  
  TEST(Cast, FloatingToInt) {
    for (auto from : {float32(), float64()}) {
      for (auto to : {int32(), int64()}) {
        // float to int no truncation
        CheckCast(ArrayFromJSON(from, "[1.0, null, 0.0, -1.0, 5.0]"),
                  ArrayFromJSON(to, "[1, null, 0, -1, 5]"));
  
        // float to int truncate error
        auto opts = CastOptions::Safe(to);
        CheckCastFails(ArrayFromJSON(from, "[1.5, 0.0, null, 0.5, -1.5, 5.5]"), opts);
  
        // float to int truncate allowed
        opts.allow_float_truncate = true;
        CheckCast(ArrayFromJSON(from, "[1.5, 0.0, null, 0.5, -1.5, 5.5]"),
                  ArrayFromJSON(to, "[1, 0, null, 0, -1, 5]"), opts);
      }
    }
  }
  
  TEST(Cast, IntToFloating) {
    for (auto from : {uint32(), int32()}) {
      std::string two_24 = "[16777216, 16777217]";
  
      CheckCastFails(ArrayFromJSON(from, two_24), CastOptions::Safe(float32()));
  
      CheckCast(ArrayFromJSON(from, two_24)->Slice(0, 1),
                ArrayFromJSON(float32(), two_24)->Slice(0, 1));
    }
  
    auto i64s = ArrayFromJSON(int64(),
                              "[-9223372036854775808, -9223372036854775807, 0,"
                              "  9223372036854775806,  9223372036854775807]");
    CheckCastFails(i64s, CastOptions::Safe(float64()));
  
    // Masking those values with nulls makes this safe
    CheckCast(MaskArrayWithNullsAt(i64s, {0, 1, 3, 4}),
              ArrayFromJSON(float64(), "[null, null, 0, null, null]"));
  
    CheckCastFails(ArrayFromJSON(uint64(), "[9007199254740992, 9007199254740993]"),
                   CastOptions::Safe(float64()));
  }
  
  TEST(Cast, Decimal128ToInt) {
    auto options = CastOptions::Safe(int64());
  
    for (bool allow_int_overflow : {false, true}) {
      for (bool allow_decimal_truncate : {false, true}) {
        options.allow_int_overflow = allow_int_overflow;
        options.allow_decimal_truncate = allow_decimal_truncate;
  
        auto no_overflow_no_truncation = ArrayFromJSON(decimal(38, 10), R"([
            "02.0000000000",
           "-11.0000000000",
            "22.0000000000",
          "-121.0000000000",
          null])");
        CheckCast(no_overflow_no_truncation,
                  ArrayFromJSON(int64(), "[2, -11, 22, -121, null]"), options);
      }
    }
  
    for (bool allow_int_overflow : {false, true}) {
      options.allow_int_overflow = allow_int_overflow;
      auto truncation_but_no_overflow = ArrayFromJSON(decimal(38, 10), R"([
            "02.1000000000",
           "-11.0000004500",
            "22.0000004500",
          "-121.1210000000",
          null])");
  
      options.allow_decimal_truncate = true;
      CheckCast(truncation_but_no_overflow,
                ArrayFromJSON(int64(), "[2, -11, 22, -121, null]"), options);
  
      options.allow_decimal_truncate = false;
      CheckCastFails(truncation_but_no_overflow, options);
    }
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      auto overflow_no_truncation = ArrayFromJSON(decimal(38, 10), R"([
          "12345678901234567890000.0000000000",
          "99999999999999999999999.0000000000",
          null])");
  
      options.allow_int_overflow = true;
      CheckCast(
          overflow_no_truncation,
          ArrayFromJSON(int64(),
                        // 12345678901234567890000 % 2**64, 99999999999999999999999 % 2**64
                        "[4807115922877858896, 200376420520689663, null]"),
          options);
  
      options.allow_int_overflow = false;
      CheckCastFails(overflow_no_truncation, options);
    }
  
    for (bool allow_int_overflow : {false, true}) {
      for (bool allow_decimal_truncate : {false, true}) {
        options.allow_int_overflow = allow_int_overflow;
        options.allow_decimal_truncate = allow_decimal_truncate;
  
        auto overflow_and_truncation = ArrayFromJSON(decimal(38, 10), R"([
          "12345678901234567890000.0045345000",
          "99999999999999999999999.0000344300",
          null])");
  
        if (options.allow_int_overflow && options.allow_decimal_truncate) {
          CheckCast(overflow_and_truncation,
                    ArrayFromJSON(
                        int64(),
                        // 12345678901234567890000 % 2**64, 99999999999999999999999 % 2**64
                        "[4807115922877858896, 200376420520689663, null]"),
                    options);
        } else {
          CheckCastFails(overflow_and_truncation, options);
        }
      }
    }
  
    Decimal128Builder builder(decimal(38, -4));
    for (auto d : {Decimal128("1234567890000."), Decimal128("-120000.")}) {
      ASSERT_OK_AND_ASSIGN(d, d.Rescale(0, -4));
      ASSERT_OK(builder.Append(d));
    }
    ASSERT_OK_AND_ASSIGN(auto negative_scale, builder.Finish());
    options.allow_int_overflow = true;
    options.allow_decimal_truncate = true;
    CheckCast(negative_scale, ArrayFromJSON(int64(), "[1234567890000, -120000]"), options);
  }
  
  TEST(Cast, Decimal256ToInt) {
    auto options = CastOptions::Safe(int64());
  
    for (bool allow_int_overflow : {false, true}) {
      for (bool allow_decimal_truncate : {false, true}) {
        options.allow_int_overflow = allow_int_overflow;
        options.allow_decimal_truncate = allow_decimal_truncate;
  
        auto no_overflow_no_truncation = ArrayFromJSON(decimal256(40, 10), R"([
            "02.0000000000",
           "-11.0000000000",
            "22.0000000000",
          "-121.0000000000",
          null])");
        CheckCast(no_overflow_no_truncation,
                  ArrayFromJSON(int64(), "[2, -11, 22, -121, null]"), options);
      }
    }
  
    for (bool allow_int_overflow : {false, true}) {
      options.allow_int_overflow = allow_int_overflow;
      auto truncation_but_no_overflow = ArrayFromJSON(decimal256(40, 10), R"([
            "02.1000000000",
           "-11.0000004500",
            "22.0000004500",
          "-121.1210000000",
          null])");
  
      options.allow_decimal_truncate = true;
      CheckCast(truncation_but_no_overflow,
                ArrayFromJSON(int64(), "[2, -11, 22, -121, null]"), options);
  
      options.allow_decimal_truncate = false;
      CheckCastFails(truncation_but_no_overflow, options);
    }
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      auto overflow_no_truncation = ArrayFromJSON(decimal256(40, 10), R"([
          "1234567890123456789000000.0000000000",
          "9999999999999999999999999.0000000000",
          null])");
  
      options.allow_int_overflow = true;
      CheckCast(overflow_no_truncation,
                ArrayFromJSON(
                    int64(),
                    // 1234567890123456789000000 % 2**64, 9999999999999999999999999 % 2**64
                    "[1096246371337547584, 1590897978359414783, null]"),
                options);
  
      options.allow_int_overflow = false;
      CheckCastFails(overflow_no_truncation, options);
    }
  
    for (bool allow_int_overflow : {false, true}) {
      for (bool allow_decimal_truncate : {false, true}) {
        options.allow_int_overflow = allow_int_overflow;
        options.allow_decimal_truncate = allow_decimal_truncate;
  
        auto overflow_and_truncation = ArrayFromJSON(decimal256(40, 10), R"([
          "1234567890123456789000000.0045345000",
          "9999999999999999999999999.0000344300",
          null])");
  
        if (options.allow_int_overflow && options.allow_decimal_truncate) {
          CheckCast(
              overflow_and_truncation,
              ArrayFromJSON(
                  int64(),
                  // 1234567890123456789000000 % 2**64, 9999999999999999999999999 % 2**64
                  "[1096246371337547584, 1590897978359414783, null]"),
              options);
        } else {
          CheckCastFails(overflow_and_truncation, options);
        }
      }
    }
  
    Decimal256Builder builder(decimal256(40, -4));
    for (auto d : {Decimal256("1234567890000."), Decimal256("-120000.")}) {
      ASSERT_OK_AND_ASSIGN(d, d.Rescale(0, -4));
      ASSERT_OK(builder.Append(d));
    }
    ASSERT_OK_AND_ASSIGN(auto negative_scale, builder.Finish());
    options.allow_int_overflow = true;
    options.allow_decimal_truncate = true;
    CheckCast(negative_scale, ArrayFromJSON(int64(), "[1234567890000, -120000]"), options);
  }
  
  TEST(Cast, IntegerToDecimal) {
    for (auto decimal_type : {decimal128(21, 2), decimal256(21, 2)}) {
      for (auto integer_type : kIntegerTypes) {
        CheckCast(
            ArrayFromJSON(integer_type, "[0, 7, null, 100, 99]"),
            ArrayFromJSON(decimal_type, R"(["0.00", "7.00", null, "100.00", "99.00"])"));
      }
    }
  
    // extreme value
    for (auto decimal_type : {decimal128(19, 0), decimal256(19, 0)}) {
      CheckCast(ArrayFromJSON(int64(), "[-9223372036854775808, 9223372036854775807]"),
                ArrayFromJSON(decimal_type,
                              R"(["-9223372036854775808", "9223372036854775807"])"));
      CheckCast(ArrayFromJSON(uint64(), "[0, 18446744073709551615]"),
                ArrayFromJSON(decimal_type, R"(["0", "18446744073709551615"])"));
    }
  
    // insufficient output precision
    {
      CastOptions options;
  
      options.to_type = decimal128(5, 3);
      CheckCastFails(ArrayFromJSON(int8(), "[0]"), options);
  
      options.to_type = decimal256(76, 67);
      CheckCastFails(ArrayFromJSON(int32(), "[0]"), options);
    }
  }
  
  TEST(Cast, Decimal128ToDecimal128) {
    CastOptions options;
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      auto no_truncation = ArrayFromJSON(decimal(38, 10), R"([
            "02.0000000000",
            "30.0000000000",
            "22.0000000000",
          "-121.0000000000",
          null])");
      auto expected = ArrayFromJSON(decimal(28, 0), R"([
            "02.",
            "30.",
            "22.",
          "-121.",
          null])");
  
      CheckCast(no_truncation, expected, options);
      CheckCast(expected, no_truncation, options);
    }
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      // Same scale, different precision
      auto d_5_2 = ArrayFromJSON(decimal(5, 2), R"([
            "12.34",
             "0.56"])");
      auto d_4_2 = ArrayFromJSON(decimal(4, 2), R"([
            "12.34",
             "0.56"])");
  
      CheckCast(d_5_2, d_4_2, options);
      CheckCast(d_4_2, d_5_2, options);
    }
  
    auto d_38_10 = ArrayFromJSON(decimal(38, 10), R"([
        "-02.1234567890",
         "30.1234567890",
        null])");
  
    auto d_28_0 = ArrayFromJSON(decimal(28, 0), R"([
        "-02.",
         "30.",
        null])");
  
    auto d_38_10_roundtripped = ArrayFromJSON(decimal(38, 10), R"([
        "-02.0000000000",
         "30.0000000000",
        null])");
  
    // Rescale which leads to truncation
    options.allow_decimal_truncate = true;
    CheckCast(d_38_10, d_28_0, options);
    CheckCast(d_28_0, d_38_10_roundtripped, options);
  
    options.allow_decimal_truncate = false;
    options.to_type = d_28_0->type();
    CheckCastFails(d_38_10, options);
    CheckCast(d_28_0, d_38_10_roundtripped, options);
  
    // Precision loss without rescale leads to truncation
    auto d_4_2 = ArrayFromJSON(decimal(4, 2), R"(["12.34"])");
    for (auto expected : {
             ArrayFromJSON(decimal(3, 2), R"(["12.34"])"),
             ArrayFromJSON(decimal(4, 3), R"(["12.340"])"),
             ArrayFromJSON(decimal(2, 1), R"(["12.3"])"),
         }) {
      options.allow_decimal_truncate = true;
      CheckCast(d_4_2, expected, options);
  
      options.allow_decimal_truncate = false;
      options.to_type = expected->type();
      CheckCastFails(d_4_2, options);
    }
  }
  
  TEST(Cast, Decimal256ToDecimal256) {
    CastOptions options;
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      auto no_truncation = ArrayFromJSON(decimal256(38, 10), R"([
            "02.0000000000",
            "30.0000000000",
            "22.0000000000",
          "-121.0000000000",
          null])");
      auto expected = ArrayFromJSON(decimal256(28, 0), R"([
            "02.",
            "30.",
            "22.",
          "-121.",
          null])");
  
      CheckCast(no_truncation, expected, options);
      CheckCast(expected, no_truncation, options);
    }
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      // Same scale, different precision
      auto d_5_2 = ArrayFromJSON(decimal256(5, 2), R"([
            "12.34",
             "0.56"])");
      auto d_4_2 = ArrayFromJSON(decimal256(4, 2), R"([
            "12.34",
             "0.56"])");
  
      CheckCast(d_5_2, d_4_2, options);
      CheckCast(d_4_2, d_5_2, options);
    }
  
    auto d_38_10 = ArrayFromJSON(decimal256(38, 10), R"([
        "-02.1234567890",
         "30.1234567890",
        null])");
  
    auto d_28_0 = ArrayFromJSON(decimal256(28, 0), R"([
        "-02.",
         "30.",
        null])");
  
    auto d_38_10_roundtripped = ArrayFromJSON(decimal256(38, 10), R"([
        "-02.0000000000",
         "30.0000000000",
        null])");
  
    // Rescale which leads to truncation
    options.allow_decimal_truncate = true;
    CheckCast(d_38_10, d_28_0, options);
    CheckCast(d_28_0, d_38_10_roundtripped, options);
  
    options.allow_decimal_truncate = false;
    options.to_type = d_28_0->type();
    CheckCastFails(d_38_10, options);
    CheckCast(d_28_0, d_38_10_roundtripped, options);
  
    // Precision loss without rescale leads to truncation
    auto d_4_2 = ArrayFromJSON(decimal256(4, 2), R"(["12.34"])");
    for (auto expected : {
             ArrayFromJSON(decimal256(3, 2), R"(["12.34"])"),
             ArrayFromJSON(decimal256(4, 3), R"(["12.340"])"),
             ArrayFromJSON(decimal256(2, 1), R"(["12.3"])"),
         }) {
      options.allow_decimal_truncate = true;
      CheckCast(d_4_2, expected, options);
  
      options.allow_decimal_truncate = false;
      options.to_type = expected->type();
      CheckCastFails(d_4_2, options);
    }
  }
  
  TEST(Cast, Decimal128ToDecimal256) {
    CastOptions options;
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      auto no_truncation = ArrayFromJSON(decimal(38, 10), R"([
            "02.0000000000",
            "30.0000000000",
            "22.0000000000",
          "-121.0000000000",
          null])");
      auto expected = ArrayFromJSON(decimal256(48, 0), R"([
            "02.",
            "30.",
            "22.",
          "-121.",
          null])");
  
      CheckCast(no_truncation, expected, options);
    }
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      // Same scale, different precision
      auto d_5_2 = ArrayFromJSON(decimal(5, 2), R"([
            "12.34",
             "0.56"])");
      auto d_4_2 = ArrayFromJSON(decimal256(4, 2), R"([
            "12.34",
             "0.56"])");
      auto d_40_2 = ArrayFromJSON(decimal256(40, 2), R"([
            "12.34",
             "0.56"])");
  
      CheckCast(d_5_2, d_4_2, options);
      CheckCast(d_5_2, d_40_2, options);
    }
  
    auto d128_38_10 = ArrayFromJSON(decimal(38, 10), R"([
        "-02.1234567890",
         "30.1234567890",
        null])");
  
    auto d128_28_0 = ArrayFromJSON(decimal(28, 0), R"([
        "-02.",
         "30.",
        null])");
  
    auto d256_28_0 = ArrayFromJSON(decimal256(28, 0), R"([
        "-02.",
         "30.",
        null])");
  
    auto d256_38_10_roundtripped = ArrayFromJSON(decimal256(38, 10), R"([
        "-02.0000000000",
         "30.0000000000",
        null])");
  
    // Rescale which leads to truncation
    options.allow_decimal_truncate = true;
    CheckCast(d128_38_10, d256_28_0, options);
    CheckCast(d128_28_0, d256_38_10_roundtripped, options);
  
    options.allow_decimal_truncate = false;
    options.to_type = d256_28_0->type();
    CheckCastFails(d128_38_10, options);
    CheckCast(d128_28_0, d256_38_10_roundtripped, options);
  
    // Precision loss without rescale leads to truncation
    auto d128_4_2 = ArrayFromJSON(decimal(4, 2), R"(["12.34"])");
    for (auto expected : {
             ArrayFromJSON(decimal256(3, 2), R"(["12.34"])"),
             ArrayFromJSON(decimal256(4, 3), R"(["12.340"])"),
             ArrayFromJSON(decimal256(2, 1), R"(["12.3"])"),
         }) {
      options.allow_decimal_truncate = true;
      CheckCast(d128_4_2, expected, options);
  
      options.allow_decimal_truncate = false;
      options.to_type = expected->type();
      CheckCastFails(d128_4_2, options);
    }
  }
  
  TEST(Cast, Decimal256ToDecimal128) {
    CastOptions options;
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      auto no_truncation = ArrayFromJSON(decimal256(42, 10), R"([
            "02.0000000000",
            "30.0000000000",
            "22.0000000000",
          "-121.0000000000",
          null])");
      auto expected = ArrayFromJSON(decimal(28, 0), R"([
            "02.",
            "30.",
            "22.",
          "-121.",
          null])");
  
      CheckCast(no_truncation, expected, options);
    }
  
    for (bool allow_decimal_truncate : {false, true}) {
      options.allow_decimal_truncate = allow_decimal_truncate;
  
      // Same scale, different precision
      auto d_5_2 = ArrayFromJSON(decimal256(42, 2), R"([
            "12.34",
             "0.56"])");
      auto d_4_2 = ArrayFromJSON(decimal(4, 2), R"([
            "12.34",
             "0.56"])");
  
      CheckCast(d_5_2, d_4_2, options);
    }
  
    auto d256_52_10 = ArrayFromJSON(decimal256(52, 10), R"([
        "-02.1234567890",
         "30.1234567890",
        null])");
  
    auto d256_42_0 = ArrayFromJSON(decimal256(42, 0), R"([
        "-02.",
         "30.",
        null])");
  
    auto d128_28_0 = ArrayFromJSON(decimal(28, 0), R"([
        "-02.",
         "30.",
        null])");
  
    auto d128_38_10_roundtripped = ArrayFromJSON(decimal(38, 10), R"([
        "-02.0000000000",
         "30.0000000000",
        null])");
  
    // Rescale which leads to truncation
    options.allow_decimal_truncate = true;
    CheckCast(d256_52_10, d128_28_0, options);
    CheckCast(d256_42_0, d128_38_10_roundtripped, options);
  
    options.allow_decimal_truncate = false;
    options.to_type = d128_28_0->type();
    CheckCastFails(d256_52_10, options);
    CheckCast(d256_42_0, d128_38_10_roundtripped, options);
  
    // Precision loss without rescale leads to truncation
    auto d256_4_2 = ArrayFromJSON(decimal256(4, 2), R"(["12.34"])");
    for (auto expected : {
             ArrayFromJSON(decimal(3, 2), R"(["12.34"])"),
             ArrayFromJSON(decimal(4, 3), R"(["12.340"])"),
             ArrayFromJSON(decimal(2, 1), R"(["12.3"])"),
         }) {
      options.allow_decimal_truncate = true;
      CheckCast(d256_4_2, expected, options);
  
      options.allow_decimal_truncate = false;
      options.to_type = expected->type();
      CheckCastFails(d256_4_2, options);
    }
  }
  
  TEST(Cast, FloatingToDecimal) {
    for (auto float_type : {float32(), float64()}) {
      for (auto decimal_type : {decimal(5, 2), decimal256(5, 2)}) {
        CheckCast(
            ArrayFromJSON(float_type, "[0.0, null, 123.45, 123.456, 999.994]"),
            ArrayFromJSON(decimal_type, R"(["0.00", null, "123.45", "123.46", "999.99"])"));
  
        // Overflow
        CastOptions options;
        options.to_type = decimal_type;
        CheckCastFails(ArrayFromJSON(float_type, "[999.996]"), options);
  
        options.allow_decimal_truncate = true;
        CheckCast(
            ArrayFromJSON(float_type, "[0.0, null, 999.996, 123.45, 999.994]"),
            ArrayFromJSON(decimal_type, R"(["0.00", null, "0.00", "123.45", "999.99"])"),
            options);
      }
    }
  
    for (auto decimal_type : {decimal128, decimal256}) {
      // 2**64 + 2**41 (exactly representable as a float)
      CheckCast(ArrayFromJSON(float32(), "[1.8446746e+19, -1.8446746e+19]"),
                ArrayFromJSON(decimal_type(20, 0),
                              R"(["18446746272732807168", "-18446746272732807168"])"));
  
      CheckCast(
          ArrayFromJSON(float64(), "[1.8446744073709556e+19, -1.8446744073709556e+19]"),
          ArrayFromJSON(decimal_type(20, 0),
                        R"(["18446744073709555712", "-18446744073709555712"])"));
  
      CheckCast(ArrayFromJSON(float32(), "[1.8446746e+15, -1.8446746e+15]"),
                ArrayFromJSON(decimal_type(20, 4),
                              R"(["1844674627273280.7168", "-1844674627273280.7168"])"));
  
      CheckCast(
          ArrayFromJSON(float64(), "[1.8446744073709556e+15, -1.8446744073709556e+15]"),
          ArrayFromJSON(decimal_type(20, 4),
                        R"(["1844674407370955.5712", "-1844674407370955.5712"])"));
  
      // Edge cases are tested for Decimal128::FromReal() and Decimal256::FromReal
    }
  }
  
  TEST(Cast, DecimalToFloating) {
    for (auto float_type : {float32(), float64()}) {
      for (auto decimal_type : {decimal(5, 2), decimal256(5, 2)}) {
        CheckCast(ArrayFromJSON(decimal_type, R"(["0.00", null, "123.45", "999.99"])"),
                  ArrayFromJSON(float_type, "[0.0, null, 123.45, 999.99]"));
      }
    }
  
    // Edge cases are tested for Decimal128::ToReal() and Decimal256::ToReal()
  }
  
  TEST(Cast, TimestampToTimestamp) {
    struct TimestampTypePair {
      std::shared_ptr<DataType> coarse, fine;
    };
  
    CastOptions options;
  
    for (auto types : {
             TimestampTypePair{timestamp(TimeUnit::SECOND), timestamp(TimeUnit::MILLI)},
             TimestampTypePair{timestamp(TimeUnit::MILLI), timestamp(TimeUnit::MICRO)},
             TimestampTypePair{timestamp(TimeUnit::MICRO), timestamp(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted = ArrayFromJSON(types.fine, "[0, null, 200000, 1000, 2000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated = ArrayFromJSON(types.fine, "[0, null, 200456, 1123, 2456]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  
    for (auto types : {
             TimestampTypePair{timestamp(TimeUnit::SECOND), timestamp(TimeUnit::MICRO)},
             TimestampTypePair{timestamp(TimeUnit::MILLI), timestamp(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted = ArrayFromJSON(types.fine, "[0, null, 200000000, 1000000, 2000000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated =
          ArrayFromJSON(types.fine, "[0, null, 200456000, 1123000, 2456000]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  
    for (auto types : {
             TimestampTypePair{timestamp(TimeUnit::SECOND), timestamp(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted =
          ArrayFromJSON(types.fine, "[0, null, 200000000000, 1000000000, 2000000000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated =
          ArrayFromJSON(types.fine, "[0, null, 200456000000, 1123000000, 2456000000]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  }
  
  TEST(Cast, TimestampZeroCopy) {
    for (auto zero_copy_to_type : {
             timestamp(TimeUnit::SECOND),
             int64(),  // ARROW-1773, cast to integer
         }) {
      CheckCastZeroCopy(
          ArrayFromJSON(timestamp(TimeUnit::SECOND), "[0, null, 2000, 1000, 0]"),
          zero_copy_to_type);
    }
    CheckCastZeroCopy(ArrayFromJSON(int64(), "[0, null, 2000, 1000, 0]"),
                      timestamp(TimeUnit::SECOND));
  }
  
  TEST(Cast, TimestampToTimestampMultiplyOverflow) {
    CastOptions options;
    options.to_type = timestamp(TimeUnit::NANO);
    // 1000-01-01, 1800-01-01 , 2000-01-01, 2300-01-01, 3000-01-01
    CheckCastFails(
        ArrayFromJSON(timestamp(TimeUnit::SECOND),
                      "[-30610224000, -5364662400, 946684800, 10413792000, 32503680000]"),
        options);
  }
  
  constexpr char kTimestampJson[] =
      R"(["1970-01-01T00:00:59.123456789","2000-02-29T23:23:23.999999999",
            "1899-01-01T00:59:20.001001001","2033-05-18T03:33:20.000000000",
            "2020-01-01T01:05:05.001", "2019-12-31T02:10:10.002",
            "2019-12-30T03:15:15.003", "2009-12-31T04:20:20.004132",
            "2010-01-01T05:25:25.005321", "2010-01-03T06:30:30.006163",
            "2010-01-04T07:35:35", "2006-01-01T08:40:40", "2005-12-31T09:45:45",
            "2008-12-28", "2008-12-29", "2012-01-01 01:02:03", null])";
  constexpr char kTimestampSecondsJson[] =
      R"(["1970-01-01T00:00:59","2000-02-29T23:23:23",
            "1899-01-01T00:59:20","2033-05-18T03:33:20",
            "2020-01-01T01:05:05", "2019-12-31T02:10:10",
            "2019-12-30T03:15:15", "2009-12-31T04:20:20",
            "2010-01-01T05:25:25", "2010-01-03T06:30:30",
            "2010-01-04T07:35:35", "2006-01-01T08:40:40",
            "2005-12-31T09:45:45", "2008-12-28", "2008-12-29",
            "2012-01-01 01:02:03", null])";
  constexpr char kTimestampExtremeJson[] =
      R"(["1677-09-20T00:00:59.123456", "2262-04-13T23:23:23.999999"])";
  
  TEST(Cast, TimestampToDate) {
    // See scalar_temporal_test.cc
    auto timestamps = ArrayFromJSON(timestamp(TimeUnit::NANO), kTimestampJson);
    auto date_32 = ArrayFromJSON(date32(),
                                 R"([
            0, 11016, -25932, 23148,
            18262, 18261, 18260, 14609,
            14610, 14612, 14613, 13149,
            13148, 14241, 14242, 15340, null
        ])");
    auto date_64 = ArrayFromJSON(date64(),
                                 R"([
            0, 951782400000, -2240524800000, 1999987200000,
            1577836800000, 1577750400000, 1577664000000, 1262217600000,
            1262304000000, 1262476800000, 1262563200000, 1136073600000,
            1135987200000, 1230422400000, 1230508800000, 1325376000000, null
        ])");
    // See TestOutsideNanosecondRange in scalar_temporal_test.cc
    auto timestamps_extreme =
        ArrayFromJSON(timestamp(TimeUnit::MICRO),
                      R"(["1677-09-20T00:00:59.123456", "2262-04-13T23:23:23.999999"])");
    auto date_32_extreme = ArrayFromJSON(date32(), "[-106753, 106753]");
    auto date_64_extreme = ArrayFromJSON(date64(), "[-9223459200000, 9223459200000]");
  
    CheckCast(timestamps, date_32);
    CheckCast(timestamps, date_64);
    CheckCast(timestamps_extreme, date_32_extreme);
    CheckCast(timestamps_extreme, date_64_extreme);
    for (auto u : TimeUnit::values()) {
      auto unit = timestamp(u);
      CheckCast(ArrayFromJSON(unit, kTimestampSecondsJson), date_32);
      CheckCast(ArrayFromJSON(unit, kTimestampSecondsJson), date_64);
    }
  }
  
  TEST(Cast, ZonedTimestampToDate) {
  #ifdef _WIN32
    // TODO(ARROW-13168): we lack tzdb on Windows
    GTEST_SKIP() << "ARROW-13168: no access to timezone database on Windows";
  #endif
  
    {
      // See TestZoned in scalar_temporal_test.cc
      auto timestamps =
          ArrayFromJSON(timestamp(TimeUnit::NANO, "Pacific/Marquesas"), kTimestampJson);
      auto date_32 = ArrayFromJSON(date32(),
                                   R"([
            -1, 11016, -25933, 23147,
            18261, 18260, 18259, 14608,
            14609, 14611, 14612, 13148,
            13148, 14240, 14241, 15339, null
        ])");
      auto date_64 = ArrayFromJSON(date64(), R"([
            -86400000, 951782400000, -2240611200000, 1999900800000,
            1577750400000, 1577664000000, 1577577600000, 1262131200000,
            1262217600000, 1262390400000, 1262476800000, 1135987200000,
            1135987200000, 1230336000000, 1230422400000, 1325289600000, null
        ])");
      CheckCast(timestamps, date_32);
      CheckCast(timestamps, date_64);
    }
  
    auto date_32 = ArrayFromJSON(date32(), R"([
            0, 11017, -25932, 23148,
            18262, 18261, 18260, 14609,
            14610, 14612, 14613, 13149,
            13148, 14241, 14242, 15340, null
        ])");
    auto date_64 = ArrayFromJSON(date64(), R"([
            0, 951868800000, -2240524800000, 1999987200000, 1577836800000,
            1577750400000, 1577664000000, 1262217600000, 1262304000000,
            1262476800000, 1262563200000, 1136073600000, 1135987200000,
            1230422400000, 1230508800000, 1325376000000, null
        ])");
  
    for (auto u : TimeUnit::values()) {
      auto timestamps =
          ArrayFromJSON(timestamp(u, "Australia/Broken_Hill"), kTimestampSecondsJson);
      CheckCast(timestamps, date_32);
      CheckCast(timestamps, date_64);
    }
  
    // Invalid timezone
    for (auto u : TimeUnit::values()) {
      auto timestamps =
          ArrayFromJSON(timestamp(u, "Mars/Mariner_Valley"), kTimestampSecondsJson);
      CheckCastFails(timestamps, CastOptions::Unsafe(date32()));
      CheckCastFails(timestamps, CastOptions::Unsafe(date64()));
    }
  }
  
  TEST(Cast, TimestampToTime) {
    // See scalar_temporal_test.cc
    auto timestamps = ArrayFromJSON(timestamp(TimeUnit::NANO), kTimestampJson);
    // See TestOutsideNanosecondRange in scalar_temporal_test.cc
    auto timestamps_extreme =
        ArrayFromJSON(timestamp(TimeUnit::MICRO), kTimestampExtremeJson);
    auto timestamps_us = ArrayFromJSON(timestamp(TimeUnit::MICRO), R"([
            "1970-01-01T00:00:59.123456","2000-02-29T23:23:23.999999",
            "1899-01-01T00:59:20.001001","2033-05-18T03:33:20.000000",
            "2020-01-01T01:05:05.001", "2019-12-31T02:10:10.002",
            "2019-12-30T03:15:15.003", "2009-12-31T04:20:20.004132",
            "2010-01-01T05:25:25.005321", "2010-01-03T06:30:30.006163",
            "2010-01-04T07:35:35", "2006-01-01T08:40:40", "2005-12-31T09:45:45",
            "2008-12-28", "2008-12-29", "2012-01-01 01:02:03", null])");
    auto timestamps_ms = ArrayFromJSON(timestamp(TimeUnit::MILLI), R"([
            "1970-01-01T00:00:59.123","2000-02-29T23:23:23.999",
            "1899-01-01T00:59:20.001","2033-05-18T03:33:20.000",
            "2020-01-01T01:05:05.001", "2019-12-31T02:10:10.002",
            "2019-12-30T03:15:15.003", "2009-12-31T04:20:20.004",
            "2010-01-01T05:25:25.005", "2010-01-03T06:30:30.006",
            "2010-01-04T07:35:35", "2006-01-01T08:40:40", "2005-12-31T09:45:45",
            "2008-12-28", "2008-12-29", "2012-01-01 01:02:03", null])");
    auto timestamps_s = ArrayFromJSON(timestamp(TimeUnit::SECOND), kTimestampSecondsJson);
  
    auto times = ArrayFromJSON(time64(TimeUnit::NANO), R"([
            59123456789, 84203999999999, 3560001001001, 12800000000000,
            3905001000000, 7810002000000, 11715003000000, 15620004132000,
            19525005321000, 23430006163000, 27335000000000, 31240000000000,
            35145000000000, 0, 0, 3723000000000, null
        ])");
    auto times_ns_us = ArrayFromJSON(time64(TimeUnit::MICRO), R"([
            59123456, 84203999999, 3560001001, 12800000000,
            3905001000, 7810002000, 11715003000, 15620004132,
            19525005321, 23430006163, 27335000000, 31240000000,
            35145000000, 0, 0, 3723000000, null
        ])");
    auto times_ns_ms = ArrayFromJSON(time32(TimeUnit::MILLI), R"([
            59123, 84203999, 3560001, 12800000,
            3905001, 7810002, 11715003, 15620004,
            19525005, 23430006, 27335000, 31240000,
            35145000, 0, 0, 3723000, null
        ])");
    auto times_us_ns = ArrayFromJSON(time64(TimeUnit::NANO), R"([
            59123456000, 84203999999000, 3560001001000, 12800000000000,
            3905001000000, 7810002000000, 11715003000000, 15620004132000,
            19525005321000, 23430006163000, 27335000000000, 31240000000000,
            35145000000000, 0, 0, 3723000000000, null
        ])");
    auto times_ms_ns = ArrayFromJSON(time64(TimeUnit::NANO), R"([
            59123000000, 84203999000000, 3560001000000, 12800000000000,
            3905001000000, 7810002000000, 11715003000000, 15620004000000,
            19525005000000, 23430006000000, 27335000000000, 31240000000000,
            35145000000000, 0, 0, 3723000000000, null
        ])");
    auto times_ms_us = ArrayFromJSON(time64(TimeUnit::MICRO), R"([
            59123000, 84203999000, 3560001000, 12800000000,
            3905001000, 7810002000, 11715003000, 15620004000,
            19525005000, 23430006000, 27335000000, 31240000000,
            35145000000, 0, 0, 3723000000, null
        ])");
  
    auto times_extreme = ArrayFromJSON(time64(TimeUnit::MICRO), "[59123456, 84203999999]");
    auto times_s = ArrayFromJSON(time32(TimeUnit::SECOND), R"([
            59, 84203, 3560, 12800,
            3905, 7810, 11715, 15620,
            19525, 23430, 27335, 31240,
            35145, 0, 0, 3723, null
        ])");
    auto times_ms = ArrayFromJSON(time32(TimeUnit::MILLI), R"([
            59000, 84203000, 3560000, 12800000,
            3905000, 7810000, 11715000, 15620000,
            19525000, 23430000, 27335000, 31240000,
            35145000, 0, 0, 3723000, null
        ])");
    auto times_us = ArrayFromJSON(time64(TimeUnit::MICRO), R"([
            59000000, 84203000000, 3560000000, 12800000000,
            3905000000, 7810000000, 11715000000, 15620000000,
            19525000000, 23430000000, 27335000000, 31240000000,
            35145000000, 0, 0, 3723000000, null
        ])");
    auto times_ns = ArrayFromJSON(time64(TimeUnit::NANO), R"([
            59000000000, 84203000000000, 3560000000000, 12800000000000,
            3905000000000, 7810000000000, 11715000000000, 15620000000000,
            19525000000000, 23430000000000, 27335000000000, 31240000000000,
            35145000000000, 0, 0, 3723000000000, null
        ])");
  
    CheckCast(timestamps, times);
    CheckCastFails(timestamps, CastOptions::Safe(time64(TimeUnit::MICRO)));
    CheckCast(timestamps_extreme, times_extreme);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::SECOND), kTimestampSecondsJson), times_s);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::SECOND), kTimestampSecondsJson), times_ms);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MILLI), kTimestampSecondsJson), times_s);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MILLI), kTimestampSecondsJson), times_ms);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MICRO), kTimestampSecondsJson), times_us);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MICRO), kTimestampSecondsJson), times_ns);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MICRO), kTimestampSecondsJson), times_ms);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MICRO), kTimestampSecondsJson), times_s);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::NANO), kTimestampSecondsJson), times_ns);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::NANO), kTimestampSecondsJson), times_us);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::NANO), kTimestampSecondsJson), times_ms);
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::NANO), kTimestampSecondsJson), times_s);
  
    CastOptions truncate = CastOptions::Safe();
    truncate.allow_time_truncate = true;
  
    // Truncation tests
    CheckCastFails(timestamps, CastOptions::Safe(time64(TimeUnit::MICRO)));
    CheckCastFails(timestamps, CastOptions::Safe(time32(TimeUnit::MILLI)));
    CheckCastFails(timestamps, CastOptions::Safe(time32(TimeUnit::SECOND)));
    CheckCastFails(timestamps_us, CastOptions::Safe(time32(TimeUnit::MILLI)));
    CheckCastFails(timestamps_us, CastOptions::Safe(time32(TimeUnit::SECOND)));
    CheckCastFails(timestamps_ms, CastOptions::Safe(time32(TimeUnit::SECOND)));
    CheckCast(timestamps, times_ns_us, truncate);
    CheckCast(timestamps, times_ns_ms, truncate);
    CheckCast(timestamps, times_s, truncate);
    CheckCast(timestamps_us, times_ns_ms, truncate);
    CheckCast(timestamps_us, times_s, truncate);
    CheckCast(timestamps_ms, times_s, truncate);
  
    // Upscaling tests
    CheckCast(timestamps_us, times_us_ns);
    CheckCast(timestamps_ms, times_ms_ns);
    CheckCast(timestamps_ms, times_ms_us);
    CheckCast(timestamps_s, times_ns);
    CheckCast(timestamps_s, times_us);
    CheckCast(timestamps_s, times_ms);
  
    // Invalid timezone
    for (auto u : TimeUnit::values()) {
      auto timestamps =
          ArrayFromJSON(timestamp(u, "Mars/Mariner_Valley"), kTimestampSecondsJson);
      if (u == TimeUnit::SECOND || u == TimeUnit::MILLI) {
        CheckCastFails(timestamps, CastOptions::Unsafe(time32(u)));
      } else {
        CheckCastFails(timestamps, CastOptions::Unsafe(time64(u)));
      }
    }
  }
  
  TEST(Cast, ZonedTimestampToTime) {
  #ifdef _WIN32
    // TODO(ARROW-13168): we lack tzdb on Windows
    GTEST_SKIP() << "ARROW-13168: no access to timezone database on Windows";
  #endif
  
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::NANO, "Pacific/Marquesas"), kTimestampJson),
              ArrayFromJSON(time64(TimeUnit::NANO), R"([
            52259123456789, 50003999999999, 56480001001001, 65000000000000,
            56105001000000, 60010002000000, 63915003000000, 67820004132000,
            71725005321000, 75630006163000, 79535000000000, 83440000000000,
            945000000000, 52200000000000, 52200000000000, 55923000000000, null
        ])"));
  
    auto time_s = R"([
            34259, 35603, 35960, 47000,
            41705, 45610, 49515, 53420,
            57325, 61230, 65135, 69040,
            72945, 37800, 37800, 41523, null
        ])";
    auto time_ms = R"([
            34259000, 35603000, 35960000, 47000000,
            41705000, 45610000, 49515000, 53420000,
            57325000, 61230000, 65135000, 69040000,
            72945000, 37800000, 37800000, 41523000, null
        ])";
    auto time_us = R"([
            34259000000, 35603000000, 35960000000, 47000000000,
            41705000000, 45610000000, 49515000000, 53420000000,
            57325000000, 61230000000, 65135000000, 69040000000,
            72945000000, 37800000000, 37800000000, 41523000000, null
        ])";
    auto time_ns = R"([
            34259000000000, 35603000000000, 35960000000000, 47000000000000,
            41705000000000, 45610000000000, 49515000000000, 53420000000000,
            57325000000000, 61230000000000, 65135000000000, 69040000000000,
            72945000000000, 37800000000000, 37800000000000, 41523000000000, null
        ])";
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::SECOND, "Australia/Broken_Hill"),
                            kTimestampSecondsJson),
              ArrayFromJSON(time32(TimeUnit::SECOND), time_s));
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MILLI, "Australia/Broken_Hill"),
                            kTimestampSecondsJson),
              ArrayFromJSON(time32(TimeUnit::MILLI), time_ms));
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::MICRO, "Australia/Broken_Hill"),
                            kTimestampSecondsJson),
              ArrayFromJSON(time64(TimeUnit::MICRO), time_us));
    CheckCast(ArrayFromJSON(timestamp(TimeUnit::NANO, "Australia/Broken_Hill"),
                            kTimestampSecondsJson),
              ArrayFromJSON(time64(TimeUnit::NANO), time_ns));
  }
  
  TEST(Cast, TimeToTime) {
    struct TimeTypePair {
      std::shared_ptr<DataType> coarse, fine;
    };
  
    CastOptions options;
  
    for (auto types : {
             TimeTypePair{time32(TimeUnit::SECOND), time32(TimeUnit::MILLI)},
             TimeTypePair{time32(TimeUnit::MILLI), time64(TimeUnit::MICRO)},
             TimeTypePair{time64(TimeUnit::MICRO), time64(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted = ArrayFromJSON(types.fine, "[0, null, 200000, 1000, 2000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated = ArrayFromJSON(types.fine, "[0, null, 200456, 1123, 2456]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  
    for (auto types : {
             TimeTypePair{time32(TimeUnit::SECOND), time64(TimeUnit::MICRO)},
             TimeTypePair{time32(TimeUnit::MILLI), time64(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted = ArrayFromJSON(types.fine, "[0, null, 200000000, 1000000, 2000000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated =
          ArrayFromJSON(types.fine, "[0, null, 200456000, 1123000, 2456000]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  
    for (auto types : {
             TimeTypePair{time32(TimeUnit::SECOND), time64(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted =
          ArrayFromJSON(types.fine, "[0, null, 200000000000, 1000000000, 2000000000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated =
          ArrayFromJSON(types.fine, "[0, null, 200456000000, 1123000000, 2456000000]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  }
  
  TEST(Cast, TimeZeroCopy) {
    for (auto zero_copy_to_type : {
             time32(TimeUnit::SECOND),
             int32(),  // ARROW-1773: cast to int32
         }) {
      CheckCastZeroCopy(ArrayFromJSON(time32(TimeUnit::SECOND), "[0, null, 2000, 1000, 0]"),
                        zero_copy_to_type);
    }
    CheckCastZeroCopy(ArrayFromJSON(int32(), "[0, null, 2000, 1000, 0]"),
                      time32(TimeUnit::SECOND));
  
    for (auto zero_copy_to_type : {
             time64(TimeUnit::MICRO),
             int64(),  // ARROW-1773: cast to int64
         }) {
      CheckCastZeroCopy(ArrayFromJSON(time64(TimeUnit::MICRO), "[0, null, 2000, 1000, 0]"),
                        zero_copy_to_type);
    }
    CheckCastZeroCopy(ArrayFromJSON(int64(), "[0, null, 2000, 1000, 0]"),
                      time64(TimeUnit::MICRO));
  }
  
  TEST(Cast, DateToString) {
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(ArrayFromJSON(date32(), "[0, null]"),
                ArrayFromJSON(string_type, R"(["1970-01-01", null])"));
      CheckCast(ArrayFromJSON(date64(), "[86400000, null]"),
                ArrayFromJSON(string_type, R"(["1970-01-02", null])"));
    }
  }
  
  TEST(Cast, TimeToString) {
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(ArrayFromJSON(time32(TimeUnit::SECOND), "[1, 62]"),
                ArrayFromJSON(string_type, R"(["00:00:01", "00:01:02"])"));
      CheckCast(
          ArrayFromJSON(time64(TimeUnit::NANO), "[0, 1]"),
          ArrayFromJSON(string_type, R"(["00:00:00.000000000", "00:00:00.000000001"])"));
    }
  }
  
  TEST(Cast, TimestampToString) {
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(
          ArrayFromJSON(timestamp(TimeUnit::SECOND), "[-30610224000, -5364662400]"),
          ArrayFromJSON(string_type, R"(["1000-01-01 00:00:00", "1800-01-01 00:00:00"])"));
    }
  }
  
  TEST(Cast, DateToDate) {
    auto day_32 = ArrayFromJSON(date32(), "[0, null, 100, 1, 10]");
    auto day_64 = ArrayFromJSON(date64(), R"([
                 0,
              null,
        8640000000,
          86400000,
         864000000])");
  
    // Multiply promotion
    CheckCast(day_32, day_64);
  
    // No truncation
    CheckCast(day_64, day_32);
  
    auto day_64_will_be_truncated = ArrayFromJSON(date64(), R"([
                 0,
              null,
        8640000123,
          86400456,
         864000789])");
  
    // Disallow truncate
    CastOptions options;
    options.to_type = date32();
    CheckCastFails(day_64_will_be_truncated, options);
  
    // Divide, truncate
    options.allow_time_truncate = true;
    CheckCast(day_64_will_be_truncated, day_32, options);
  }
  
  TEST(Cast, DateZeroCopy) {
    for (auto zero_copy_to_type : {
             date32(),
             int32(),  // ARROW-1773: cast to int32
         }) {
      CheckCastZeroCopy(ArrayFromJSON(date32(), "[0, null, 2000, 1000, 0]"),
                        zero_copy_to_type);
    }
    CheckCastZeroCopy(ArrayFromJSON(int32(), "[0, null, 2000, 1000, 0]"), date32());
  
    for (auto zero_copy_to_type : {
             date64(),
             int64(),  // ARROW-1773: cast to int64
         }) {
      CheckCastZeroCopy(ArrayFromJSON(date64(), "[0, null, 2000, 1000, 0]"),
                        zero_copy_to_type);
    }
    CheckCastZeroCopy(ArrayFromJSON(int64(), "[0, null, 2000, 1000, 0]"), date64());
  }
  
  TEST(Cast, DurationToDuration) {
    struct DurationTypePair {
      std::shared_ptr<DataType> coarse, fine;
    };
  
    CastOptions options;
  
    for (auto types : {
             DurationTypePair{duration(TimeUnit::SECOND), duration(TimeUnit::MILLI)},
             DurationTypePair{duration(TimeUnit::MILLI), duration(TimeUnit::MICRO)},
             DurationTypePair{duration(TimeUnit::MICRO), duration(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted = ArrayFromJSON(types.fine, "[0, null, 200000, 1000, 2000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated = ArrayFromJSON(types.fine, "[0, null, 200456, 1123, 2456]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  
    for (auto types : {
             DurationTypePair{duration(TimeUnit::SECOND), duration(TimeUnit::MICRO)},
             DurationTypePair{duration(TimeUnit::MILLI), duration(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted = ArrayFromJSON(types.fine, "[0, null, 200000000, 1000000, 2000000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated =
          ArrayFromJSON(types.fine, "[0, null, 200000456, 1000123, 2000456]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  
    for (auto types : {
             DurationTypePair{duration(TimeUnit::SECOND), duration(TimeUnit::NANO)},
         }) {
      auto coarse = ArrayFromJSON(types.coarse, "[0, null, 200, 1, 2]");
      auto promoted =
          ArrayFromJSON(types.fine, "[0, null, 200000000000, 1000000000, 2000000000]");
  
      // multiply/promote
      CheckCast(coarse, promoted);
  
      auto will_be_truncated =
          ArrayFromJSON(types.fine, "[0, null, 200000000456, 1000000123, 2000000456]");
  
      // with truncation disallowed, fails
      options.allow_time_truncate = false;
      options.to_type = types.coarse;
      CheckCastFails(will_be_truncated, options);
  
      // with truncation allowed, divide/truncate
      options.allow_time_truncate = true;
      CheckCast(will_be_truncated, coarse, options);
    }
  }
  
  TEST(Cast, DurationZeroCopy) {
    for (auto zero_copy_to_type : {
             duration(TimeUnit::SECOND),
             int64(),  // ARROW-1773: cast to int64
         }) {
      CheckCastZeroCopy(
          ArrayFromJSON(duration(TimeUnit::SECOND), "[0, null, 2000, 1000, 0]"),
          zero_copy_to_type);
    }
    CheckCastZeroCopy(ArrayFromJSON(int64(), "[0, null, 2000, 1000, 0]"),
                      duration(TimeUnit::SECOND));
  }
  
  TEST(Cast, DurationToDurationMultiplyOverflow) {
    CastOptions options;
    options.to_type = duration(TimeUnit::NANO);
    CheckCastFails(
        ArrayFromJSON(duration(TimeUnit::SECOND), "[10000000000, 1, 2, 3, 10000000000]"),
        options);
  }
  
  TEST(Cast, MiscToFloating) {
    for (auto to_type : {float32(), float64()}) {
      CheckCast(ArrayFromJSON(int16(), "[0, null, 200, 1, 2]"),
                ArrayFromJSON(to_type, "[0, null, 200, 1, 2]"));
  
      CheckCast(ArrayFromJSON(float32(), "[0, null, 200, 1, 2]"),
                ArrayFromJSON(to_type, "[0, null, 200, 1, 2]"));
  
      CheckCast(ArrayFromJSON(boolean(), "[true, null, false, false, true]"),
                ArrayFromJSON(to_type, "[1, null, 0, 0, 1]"));
    }
  }
  
  TEST(Cast, UnsupportedInputType) {
    // Casting to a supported target type, but with an unsupported input type
    // for the target type.
    const auto arr = ArrayFromJSON(int32(), "[1, 2, 3]");
  
    const auto to_type = list(utf8());
    const char* expected_message = "Unsupported cast from int32 to list";
  
    // Try through concrete API
    EXPECT_RAISES_WITH_MESSAGE_THAT(NotImplemented, ::testing::HasSubstr(expected_message),
                                    Cast(*arr, to_type));
  
    // Try through general kernel API
    CastOptions options;
    options.to_type = to_type;
    EXPECT_RAISES_WITH_MESSAGE_THAT(NotImplemented, ::testing::HasSubstr(expected_message),
                                    CallFunction("cast", {arr}, &options));
  }
  
  TEST(Cast, UnsupportedTargetType) {
    // Casting to an unsupported target type
    const auto arr = ArrayFromJSON(int32(), "[1, 2, 3]");
    const auto to_type = dense_union({field("a", int32())});
  
    // Try through concrete API
    const char* expected_message = "Unsupported cast from int32 to dense_union";
    EXPECT_RAISES_WITH_MESSAGE_THAT(NotImplemented, ::testing::HasSubstr(expected_message),
                                    Cast(*arr, to_type));
  
    // Try through general kernel API
    CastOptions options;
    options.to_type = to_type;
    EXPECT_RAISES_WITH_MESSAGE_THAT(NotImplemented, ::testing::HasSubstr(expected_message),
                                    CallFunction("cast", {arr}, &options));
  }
  
  TEST(Cast, StringToBoolean) {
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(ArrayFromJSON(string_type, R"(["False", null, "true", "True", "false"])"),
                ArrayFromJSON(boolean(), "[false, null, true, true, false]"));
  
      CheckCast(ArrayFromJSON(string_type, R"(["0", null, "1", "1", "0"])"),
                ArrayFromJSON(boolean(), "[false, null, true, true, false]"));
  
      auto options = CastOptions::Safe(boolean());
      CheckCastFails(ArrayFromJSON(string_type, R"(["false "])"), options);
      CheckCastFails(ArrayFromJSON(string_type, R"(["T"])"), options);
    }
  }
  
  TEST(Cast, StringToInt) {
    for (auto string_type : {utf8(), large_utf8()}) {
      for (auto signed_type : {int8(), int16(), int32(), int64()}) {
        CheckCast(
            ArrayFromJSON(string_type, R"(["0", null, "127", "-1", "0", "0x0", "0x7F"])"),
            ArrayFromJSON(signed_type, "[0, null, 127, -1, 0, 0, 127]"));
      }
  
      CheckCast(ArrayFromJSON(string_type, R"(["2147483647", null, "-2147483648", "0",
            "0X0", "0x7FFFFFFF", "0XFFFFfFfF", "0Xf0000000"])"),
                ArrayFromJSON(
                    int32(),
                    "[2147483647, null, -2147483648, 0, 0, 2147483647, -1, -268435456]"));
  
      CheckCast(ArrayFromJSON(string_type,
                              R"(["9223372036854775807", null, "-9223372036854775808", "0",
                      "0x0", "0x7FFFFFFFFFFFFFFf", "0XF000000000000001"])"),
                ArrayFromJSON(int64(),
                              "[9223372036854775807, null, -9223372036854775808, 0, 0, "
                              "9223372036854775807, -1152921504606846975]"));
  
      for (auto unsigned_type : {uint8(), uint16(), uint32(), uint64()}) {
        CheckCast(ArrayFromJSON(string_type,
                                R"(["0", null, "127", "255", "0", "0X0", "0xff", "0x7f"])"),
                  ArrayFromJSON(unsigned_type, "[0, null, 127, 255, 0, 0, 255, 127]"));
      }
  
      CheckCast(
          ArrayFromJSON(string_type, R"(["2147483647", null, "4294967295", "0",
                                      "0x0", "0x7FFFFFFf", "0xFFFFFFFF"])"),
          ArrayFromJSON(uint32(),
                        "[2147483647, null, 4294967295, 0, 0, 2147483647, 4294967295]"));
  
      CheckCast(ArrayFromJSON(string_type,
                              R"(["9223372036854775807", null, "18446744073709551615", "0",
                      "0x0", "0x7FFFFFFFFFFFFFFf", "0xfFFFFFFFFFFFFFFf"])"),
                ArrayFromJSON(uint64(),
                              "[9223372036854775807, null, 18446744073709551615, 0, 0, "
                              "9223372036854775807, 18446744073709551615]"));
  
      for (std::string not_int8 : {
               "z",
               "12 z",
               "128",
               "-129",
               "0.5",
               "0x",
               "0xfff",
               "-0xf0",
           }) {
        auto options = CastOptions::Safe(int8());
        CheckCastFails(ArrayFromJSON(string_type, "[\"" + not_int8 + "\"]"), options);
      }
  
      for (std::string not_uint8 : {"256", "-1", "0.5", "0x", "0x3wa", "0x123"}) {
        auto options = CastOptions::Safe(uint8());
        CheckCastFails(ArrayFromJSON(string_type, "[\"" + not_uint8 + "\"]"), options);
      }
    }
  }
  
  TEST(Cast, StringToFloating) {
    for (auto string_type : {utf8(), large_utf8()}) {
      for (auto float_type : {float32(), float64()}) {
        auto strings =
            ArrayFromJSON(string_type, R"(["0.1", null, "127.3", "1e3", "200.4", "0.5"])");
        auto floats = ArrayFromJSON(float_type, "[0.1, null, 127.3, 1000, 200.4, 0.5]");
        CheckCast(strings, floats);
  
        for (std::string not_float : {
                 "z",
             }) {
          auto options = CastOptions::Safe(float32());
          CheckCastFails(ArrayFromJSON(string_type, "[\"" + not_float + "\"]"), options);
        }
  
  #if !defined(_WIN32) || defined(NDEBUG)
        // Test that casting is locale-independent
        // French locale uses the comma as decimal point
        LocaleGuard locale_guard("fr_FR.UTF-8");
        CheckCast(strings, floats);
  #endif
      }
    }
  }
  
  TEST(Cast, StringToTimestamp) {
    for (auto string_type : {utf8(), large_utf8()}) {
      auto strings = ArrayFromJSON(string_type, R"(["1970-01-01", null, "2000-02-29"])");
  
      CheckCast(strings,
                ArrayFromJSON(timestamp(TimeUnit::SECOND), "[0, null, 951782400]"));
  
      CheckCast(strings,
                ArrayFromJSON(timestamp(TimeUnit::MICRO), "[0, null, 951782400000000]"));
  
      for (auto unit :
           {TimeUnit::SECOND, TimeUnit::MILLI, TimeUnit::MICRO, TimeUnit::NANO}) {
        for (std::string not_ts : {
                 "",
                 "xxx",
             }) {
          auto options = CastOptions::Safe(timestamp(unit));
          CheckCastFails(ArrayFromJSON(string_type, "[\"" + not_ts + "\"]"), options);
        }
      }
  
      // NOTE: timestamp parsing is tested comprehensively in parsing-util-test.cc
    }
  }
  
  static void AssertBinaryZeroCopy(std::shared_ptr<Array> lhs, std::shared_ptr<Array> rhs) {
    // null bitmap and data buffers are always zero-copied
    AssertBufferSame(*lhs, *rhs, 0);
    AssertBufferSame(*lhs, *rhs, 2);
  
    if (offset_bit_width(lhs->type_id()) == offset_bit_width(rhs->type_id())) {
      // offset buffer is zero copied if possible
      AssertBufferSame(*lhs, *rhs, 1);
      return;
    }
  
    // offset buffers are equivalent
    ArrayVector offsets;
    for (auto array : {lhs, rhs}) {
      auto length = array->length();
      auto buffer = array->data()->buffers[1];
      offsets.push_back(offset_bit_width(array->type_id()) == 32
                            ? *Cast(Int32Array(length, buffer), int64())
                            : std::make_shared<Int64Array>(length, buffer));
    }
    AssertArraysEqual(*offsets[0], *offsets[1]);
  }
  
  TEST(Cast, BinaryToString) {
    for (auto bin_type : {binary(), large_binary()}) {
      for (auto string_type : {utf8(), large_utf8()}) {
        // empty -> empty always works
        CheckCast(ArrayFromJSON(bin_type, "[]"), ArrayFromJSON(string_type, "[]"));
  
        auto invalid_utf8 = InvalidUtf8(bin_type);
  
        // invalid utf-8 masked by a null bit is not an error
        CheckCast(MaskArrayWithNullsAt(InvalidUtf8(bin_type), {4}),
                  MaskArrayWithNullsAt(InvalidUtf8(string_type), {4}));
  
        // error: invalid utf-8
        auto options = CastOptions::Safe(string_type);
        CheckCastFails(invalid_utf8, options);
  
        // override utf-8 check
        options.allow_invalid_utf8 = true;
        ASSERT_OK_AND_ASSIGN(auto strings, Cast(*invalid_utf8, string_type, options));
        ASSERT_RAISES(Invalid, strings->ValidateFull());
        AssertBinaryZeroCopy(invalid_utf8, strings);
      }
    }
  
    auto from_type = fixed_size_binary(3);
    auto invalid_utf8 = FixedSizeInvalidUtf8(from_type);
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(ArrayFromJSON(from_type, "[]"), ArrayFromJSON(string_type, "[]"));
  
      // invalid utf-8 masked by a null bit is not an error
      CheckCast(MaskArrayWithNullsAt(invalid_utf8, {4}),
                MaskArrayWithNullsAt(FixedSizeInvalidUtf8(string_type), {4}));
  
      // error: invalid utf-8
      auto options = CastOptions::Safe(string_type);
      CheckCastFails(invalid_utf8, options);
  
      // override utf-8 check
      options.allow_invalid_utf8 = true;
      ASSERT_OK_AND_ASSIGN(auto strings, Cast(*invalid_utf8, string_type, options));
      ASSERT_RAISES(Invalid, strings->ValidateFull());
  
      // N.B. null buffer is not always the same if input sliced
      AssertBufferSame(*invalid_utf8, *strings, 0);
      ASSERT_EQ(invalid_utf8->data()->buffers[1].get(), strings->data()->buffers[2].get());
    }
  }
  
  TEST(Cast, BinaryOrStringToBinary) {
    for (auto from_type : {utf8(), large_utf8(), binary(), large_binary()}) {
      for (auto to_type : {binary(), large_binary()}) {
        // empty -> empty always works
        CheckCast(ArrayFromJSON(from_type, "[]"), ArrayFromJSON(to_type, "[]"));
  
        auto invalid_utf8 = InvalidUtf8(from_type);
  
        // invalid utf-8 is not an error for binary
        ASSERT_OK_AND_ASSIGN(auto strings, Cast(*invalid_utf8, to_type));
        ValidateOutput(*strings);
        AssertBinaryZeroCopy(invalid_utf8, strings);
  
        // invalid utf-8 masked by a null bit is not an error
        CheckCast(MaskArrayWithNullsAt(InvalidUtf8(from_type), {4}),
                  MaskArrayWithNullsAt(InvalidUtf8(to_type), {4}));
      }
    }
  
    auto from_type = fixed_size_binary(3);
    auto invalid_utf8 = FixedSizeInvalidUtf8(from_type);
    CheckCast(invalid_utf8, invalid_utf8);
    CheckCastFails(invalid_utf8, CastOptions::Safe(fixed_size_binary(5)));
    for (auto to_type : {binary(), large_binary()}) {
      CheckCast(ArrayFromJSON(from_type, "[]"), ArrayFromJSON(to_type, "[]"));
      ASSERT_OK_AND_ASSIGN(auto strings, Cast(*invalid_utf8, to_type));
      ValidateOutput(*strings);
  
      // N.B. null buffer is not always the same if input sliced
      AssertBufferSame(*invalid_utf8, *strings, 0);
      ASSERT_EQ(invalid_utf8->data()->buffers[1].get(), strings->data()->buffers[2].get());
  
      // invalid utf-8 masked by a null bit is not an error
      CheckCast(MaskArrayWithNullsAt(invalid_utf8, {4}),
                MaskArrayWithNullsAt(FixedSizeInvalidUtf8(to_type), {4}));
    }
  }
  
  TEST(Cast, StringToString) {
    for (auto from_type : {utf8(), large_utf8()}) {
      for (auto to_type : {utf8(), large_utf8()}) {
        // empty -> empty always works
        CheckCast(ArrayFromJSON(from_type, "[]"), ArrayFromJSON(to_type, "[]"));
  
        auto invalid_utf8 = InvalidUtf8(from_type);
  
        // invalid utf-8 masked by a null bit is not an error
        CheckCast(MaskArrayWithNullsAt(invalid_utf8, {4}),
                  MaskArrayWithNullsAt(InvalidUtf8(to_type), {4}));
  
        // override utf-8 check
        auto options = CastOptions::Safe(to_type);
        options.allow_invalid_utf8 = true;
        // utf-8 is not checked by Cast when the origin guarantees utf-8
        ASSERT_OK_AND_ASSIGN(auto strings, Cast(*invalid_utf8, to_type, options));
        ASSERT_RAISES(Invalid, strings->ValidateFull());
        AssertBinaryZeroCopy(invalid_utf8, strings);
      }
    }
  }
  
  TEST(Cast, IntToString) {
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(ArrayFromJSON(int8(), "[0, 1, 127, -128, null]"),
                ArrayFromJSON(string_type, R"(["0", "1", "127", "-128", null])"));
  
      CheckCast(ArrayFromJSON(uint8(), "[0, 1, 255, null]"),
                ArrayFromJSON(string_type, R"(["0", "1", "255", null])"));
  
      CheckCast(ArrayFromJSON(int16(), "[0, 1, 32767, -32768, null]"),
                ArrayFromJSON(string_type, R"(["0", "1", "32767", "-32768", null])"));
  
      CheckCast(ArrayFromJSON(uint16(), "[0, 1, 65535, null]"),
                ArrayFromJSON(string_type, R"(["0", "1", "65535", null])"));
  
      CheckCast(
          ArrayFromJSON(int32(), "[0, 1, 2147483647, -2147483648, null]"),
          ArrayFromJSON(string_type, R"(["0", "1", "2147483647", "-2147483648", null])"));
  
      CheckCast(ArrayFromJSON(uint32(), "[0, 1, 4294967295, null]"),
                ArrayFromJSON(string_type, R"(["0", "1", "4294967295", null])"));
  
      CheckCast(
          ArrayFromJSON(int64(), "[0, 1, 9223372036854775807, -9223372036854775808, null]"),
          ArrayFromJSON(
              string_type,
              R"(["0", "1", "9223372036854775807", "-9223372036854775808", null])"));
  
      CheckCast(ArrayFromJSON(uint64(), "[0, 1, 18446744073709551615, null]"),
                ArrayFromJSON(string_type, R"(["0", "1", "18446744073709551615", null])"));
    }
  }
  
  TEST(Cast, FloatingToString) {
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(
          ArrayFromJSON(float32(), "[0.0, -0.0, 1.5, -Inf, Inf, NaN, null]"),
          ArrayFromJSON(string_type, R"(["0", "-0", "1.5", "-inf", "inf", "nan", null])"));
  
      CheckCast(
          ArrayFromJSON(float64(), "[0.0, -0.0, 1.5, -Inf, Inf, NaN, null]"),
          ArrayFromJSON(string_type, R"(["0", "-0", "1.5", "-inf", "inf", "nan", null])"));
    }
  }
  
  TEST(Cast, BooleanToString) {
    for (auto string_type : {utf8(), large_utf8()}) {
      CheckCast(ArrayFromJSON(boolean(), "[true, true, false, null]"),
                ArrayFromJSON(string_type, R"(["true", "true", "false", null])"));
    }
  }
  
  TEST(Cast, ListToPrimitive) {
    ASSERT_RAISES(NotImplemented,
                  Cast(*ArrayFromJSON(list(int8()), "[[1, 2], [3, 4]]"), uint8()));
  
    ASSERT_RAISES(
        NotImplemented,
        Cast(*ArrayFromJSON(list(binary()), R"([["1", "2"], ["3", "4"]])"), utf8()));
  }
  
  using make_list_t = std::shared_ptr<DataType>(const std::shared_ptr<DataType>&);
  
  static const auto list_factories = std::vector<make_list_t*>{&list, &large_list};
  
  static void CheckListToList(const std::vector<std::shared_ptr<DataType>>& value_types,
                              const std::string& json_data) {
    for (auto make_src_list : list_factories) {
      for (auto make_dest_list : list_factories) {
        for (const auto& src_value_type : value_types) {
          for (const auto& dest_value_type : value_types) {
            const auto src_type = make_src_list(src_value_type);
            const auto dest_type = make_dest_list(dest_value_type);
            ARROW_SCOPED_TRACE("src_type = ", src_type->ToString(),
                               ", dest_type = ", dest_type->ToString());
            CheckCast(ArrayFromJSON(src_type, json_data),
                      ArrayFromJSON(dest_type, json_data));
          }
        }
      }
    }
  }
  
  TEST(Cast, ListToList) {
    CheckListToList({int32(), float32(), int64()},
                    "[[0], [1], null, [2, 3, 4], [5, 6], null, [], [7], [8, 9]]");
  }
  
  TEST(Cast, ListToListNoNulls) {
    // ARROW-12568
    CheckListToList({int32(), float32(), int64()},
                    "[[0], [1], [2, 3, 4], [5, 6], [], [7], [8, 9]]");
  }
  
  TEST(Cast, ListToListOptionsPassthru) {
    for (auto make_src_list : list_factories) {
      for (auto make_dest_list : list_factories) {
        auto list_int32 = ArrayFromJSON(make_src_list(int32()), "[[87654321]]");
  
        auto options = CastOptions::Safe(make_dest_list(int16()));
        CheckCastFails(list_int32, options);
  
        options.allow_int_overflow = true;
        CheckCast(list_int32, ArrayFromJSON(make_dest_list(int16()), "[[32689]]"), options);
      }
    }
  }
  
  TEST(Cast, IdentityCasts) {
    // ARROW-4102
    auto CheckIdentityCast = [](std::shared_ptr<DataType> type, const std::string& json) {
      CheckCastZeroCopy(ArrayFromJSON(type, json), type);
    };
  
    CheckIdentityCast(null(), "[null, null, null]");
    CheckIdentityCast(boolean(), "[false, true, null, false]");
  
    for (auto type : kNumericTypes) {
      CheckIdentityCast(type, "[1, 2, null, 4]");
    }
    CheckIdentityCast(binary(), R"(["foo", "bar"])");
    CheckIdentityCast(utf8(), R"(["foo", "bar"])");
    CheckIdentityCast(fixed_size_binary(3), R"(["foo", "bar"])");
  
    CheckIdentityCast(list(int8()), "[[1, 2], [null], [], [3]]");
  
    CheckIdentityCast(time32(TimeUnit::MILLI), "[1, 2, 3, 4]");
    CheckIdentityCast(time64(TimeUnit::MICRO), "[1, 2, 3, 4]");
    CheckIdentityCast(date32(), "[1, 2, 3, 4]");
    CheckIdentityCast(date64(), "[86400000, 0]");
    CheckIdentityCast(timestamp(TimeUnit::SECOND), "[1, 2, 3, 4]");
  
    CheckIdentityCast(dictionary(int8(), int8()), "[1, 2, 3, 1, null, 3]");
  }
  
  TEST(Cast, EmptyCasts) {
    // ARROW-4766: 0-length arrays should not segfault
    auto CheckCastEmpty = [](std::shared_ptr<DataType> from, std::shared_ptr<DataType> to) {
      // Python creates array with nullptr instead of 0-length (valid) buffers.
      auto data = ArrayData::Make(from, /* length */ 0, /* buffers */ {nullptr, nullptr});
      CheckCast(MakeArray(data), ArrayFromJSON(to, "[]"));
    };
  
    for (auto numeric : kNumericTypes) {
      CheckCastEmpty(boolean(), numeric);
      CheckCastEmpty(numeric, boolean());
    }
  }
  
  TEST(Cast, CastWithNoValidityBitmapButUnknownNullCount) {
    // ARROW-12672 segfault when casting slightly malformed array
    // (no validity bitmap but atomic null count non-zero)
    auto values = ArrayFromJSON(boolean(), "[true, true, false]");
  
    ASSERT_OK_AND_ASSIGN(auto expected, Cast(*values, int8()));
  
    ASSERT_EQ(values->data()->buffers[0], NULLPTR);
    values->data()->null_count = kUnknownNullCount;
    ASSERT_OK_AND_ASSIGN(auto result, Cast(*values, int8()));
  
    AssertArraysEqual(*expected, *result);
  }
  
  // ----------------------------------------------------------------------
  // Test casting from NullType
  
  TEST(Cast, FromNull) {
    for (auto to_type : {
             null(),
             uint8(),
             int8(),
             uint16(),
             int16(),
             uint32(),
             int32(),
             uint64(),
             int64(),
             float32(),
             float64(),
             date32(),
             date64(),
             fixed_size_binary(10),
             binary(),
             utf8(),
         }) {
      ASSERT_OK_AND_ASSIGN(auto expected, MakeArrayOfNull(to_type, 10));
      CheckCast(std::make_shared<NullArray>(10), expected);
    }
  }
  
  TEST(Cast, FromNullToDictionary) {
    auto from = std::make_shared<NullArray>(10);
    auto to_type = dictionary(int8(), boolean());
  
    ASSERT_OK_AND_ASSIGN(auto expected, MakeArrayOfNull(to_type, 10));
    CheckCast(from, expected);
  }
  
  // ----------------------------------------------------------------------
  // Test casting from DictionaryType
  
  TEST(Cast, FromDictionary) {
    ArrayVector dictionaries;
    dictionaries.push_back(std::make_shared<NullArray>(5));
  
    for (auto num_type : kNumericTypes) {
      dictionaries.push_back(ArrayFromJSON(num_type, "[23, 12, 45, 12, null]"));
    }
  
    for (auto string_type : kBaseBinaryTypes) {
      dictionaries.push_back(
          ArrayFromJSON(string_type, R"(["foo", "bar", "baz", "foo", null])"));
    }
  
    for (auto dict : dictionaries) {
      for (auto index_type : kDictionaryIndexTypes) {
        auto indices = ArrayFromJSON(index_type, "[4, 0, 1, 2, 0, 4, null, 2]");
        ASSERT_OK_AND_ASSIGN(auto expected, Take(*dict, *indices));
  
        ASSERT_OK_AND_ASSIGN(
            auto dict_arr, DictionaryArray::FromArrays(dictionary(index_type, dict->type()),
                                                       indices, dict));
        CheckCast(dict_arr, expected);
      }
    }
  
    for (auto dict : dictionaries) {
      if (dict->type_id() == Type::NA) continue;
  
      // Test with a nullptr bitmap buffer (ARROW-3208)
      auto indices = ArrayFromJSON(int8(), "[0, 0, 1, 2, 0, 3, 3, 2]");
      ASSERT_OK_AND_ASSIGN(auto no_nulls, Take(*dict, *indices));
      ASSERT_EQ(no_nulls->null_count(), 0);
  
      ASSERT_OK_AND_ASSIGN(Datum encoded, DictionaryEncode(no_nulls));
  
      // Make a new dict array with nullptr bitmap buffer
      auto data = encoded.array()->Copy();
      data->buffers[0] = nullptr;
      data->null_count = 0;
      std::shared_ptr<Array> dict_array = std::make_shared<DictionaryArray>(data);
      ValidateOutput(*dict_array);
  
      CheckCast(dict_array, no_nulls);
    }
  }
  
  std::shared_ptr<Array> SmallintArrayFromJSON(const std::string& json_data) {
    auto arr = ArrayFromJSON(int16(), json_data);
    auto ext_data = arr->data()->Copy();
    ext_data->type = smallint();
    return MakeArray(ext_data);
  }
  
  TEST(Cast, ExtensionTypeToIntDowncast) {
    auto smallint = std::make_shared<SmallintType>();
    ExtensionTypeGuard smallint_guard(smallint);
  
    std::shared_ptr<Array> result;
    std::vector<bool> is_valid = {true, false, true, true, true};
  
    // Smallint(int16) to int16
    CheckCastZeroCopy(SmallintArrayFromJSON("[0, 100, 200, 1, 2]"), int16());
  
    // Smallint(int16) to uint8, no overflow/underrun
    CheckCast(SmallintArrayFromJSON("[0, 100, 200, 1, 2]"),
              ArrayFromJSON(uint8(), "[0, 100, 200, 1, 2]"));
  
    // Smallint(int16) to uint8, with overflow
    {
      CastOptions options;
      options.to_type = uint8();
      CheckCastFails(SmallintArrayFromJSON("[0, null, 256, 1, 3]"), options);
  
      options.allow_int_overflow = true;
      CheckCast(SmallintArrayFromJSON("[0, null, 256, 1, 3]"),
                ArrayFromJSON(uint8(), "[0, null, 0, 1, 3]"), options);
    }
  
    // Smallint(int16) to uint8, with underflow
    {
      CastOptions options;
      options.to_type = uint8();
      CheckCastFails(SmallintArrayFromJSON("[0, null, -1, 1, 3]"), options);
  
      options.allow_int_overflow = true;
      CheckCast(SmallintArrayFromJSON("[0, null, -1, 1, 3]"),
                ArrayFromJSON(uint8(), "[0, null, 255, 1, 3]"), options);
    }
  }
  
  TEST(Cast, DictTypeToAnotherDict) {
    auto check_cast = [&](const std::shared_ptr<DataType>& in_type,
                          const std::shared_ptr<DataType>& out_type,
                          const std::string& json_str,
                          const CastOptions& options = CastOptions()) {
      auto arr = ArrayFromJSON(in_type, json_str);
      auto exp = in_type->Equals(out_type) ? arr : ArrayFromJSON(out_type, json_str);
      // this checks for scalars as well
      CheckCast(arr, exp, options);
    };
  
    //    check same type passed on to casting
    check_cast(dictionary(int8(), int16()), dictionary(int8(), int16()),
               "[1, 2, 3, 1, null, 3]");
    check_cast(dictionary(int8(), int16()), dictionary(int32(), int64()),
               "[1, 2, 3, 1, null, 3]");
    check_cast(dictionary(int8(), int16()), dictionary(int32(), float64()),
               "[1, 2, 3, 1, null, 3]");
    check_cast(dictionary(int32(), utf8()), dictionary(int8(), utf8()),
               R"(["a", "b", "a", null])");
  
    auto arr = ArrayFromJSON(dictionary(int32(), int32()), "[1, 1000]");
    // check casting unsafe values (checking for unsafe indices is unnecessary, because it
    // would create an invalid index array which results in a ValidateOutput failure)
    ASSERT_OK_AND_ASSIGN(auto casted,
                         Cast(arr, dictionary(int8(), int8()), CastOptions::Unsafe()));
    ValidateOutput(casted);
  
    // check safe casting values
    EXPECT_RAISES_WITH_MESSAGE_THAT(
        Invalid, testing::HasSubstr("Integer value 1000 not in range"),
        Cast(arr, dictionary(int8(), int8()), CastOptions::Safe()));
  }
  
  }  // namespace compute
  }  // namespace arrow
  