/*
 * 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.
 */

package org.apache.arrow.vector;

import static org.apache.arrow.vector.TestUtils.newVector;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.Period;

import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.types.Types.MinorType;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/*
 * Tested field types:
 *
 * NullableInt
 * NullableBigInt
 * NullableFloat4
 * NullableFloat8
 * NullableBit
 * NullableDecimal
 * NullableIntervalDay
 * NullableIntervalYear
 * NullableSmallInt
 * NullableTinyInt
 * NullableVarChar
 * NullableTimeMicro
 * NullableTimeMilli
 * NullableTimeStamp*
 */

public class TestCopyFrom {

  private static final String EMPTY_SCHEMA_PATH = "";

  private BufferAllocator allocator;

  @Before
  public void init() {
    allocator = new RootAllocator(Long.MAX_VALUE);
  }

  @After
  public void terminate() throws Exception {
    allocator.close();
  }

  @Test /* NullableVarChar */
  public void testCopyFromWithNulls() {
    try (final VarCharVector vector =
            newVector(VarCharVector.class, EMPTY_SCHEMA_PATH, MinorType.VARCHAR, allocator);
        final VarCharVector vector2 =
            newVector(VarCharVector.class, EMPTY_SCHEMA_PATH, MinorType.VARCHAR, allocator)) {
      vector.allocateNew();
      assertTrue(vector.getValueCapacity() >= 1);
      assertEquals(0, vector.getValueCount());
      int initialCapacity = vector.getValueCapacity();

      for (int i = 0; i < initialCapacity; i++) {
        if (i % 3 == 0) {
          continue;
        }
        byte[] b = Integer.toString(i).getBytes();
        vector.setSafe(i, b, 0, b.length);
      }

      /* NO reAlloc() should have happened in setSafe() */
      int capacity = vector.getValueCapacity();
      assertEquals(initialCapacity, capacity);

      vector.setValueCount(initialCapacity);

      for (int i = 0; i < initialCapacity; i++) {
        if (i % 3 == 0) {
          assertNull(vector.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i,
              Integer.toString(i),
              vector.getObject(i).toString());
        }
      }

      vector2.setInitialCapacity(initialCapacity);
      vector2.allocateNew();
      capacity = vector2.getValueCapacity();
      assertEquals(initialCapacity, capacity);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector);
        if (i % 3 == 0) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i,
              Integer.toString(i),
              vector2.getObject(i).toString());
        }
      }

      /* NO reAlloc() should have happened in copyFrom */
      capacity = vector2.getValueCapacity();
      assertEquals(initialCapacity, capacity);

      vector2.setValueCount(initialCapacity);

      for (int i = 0; i < initialCapacity; i++) {
        if (i % 3 == 0) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i,
              Integer.toString(i),
              vector2.getObject(i).toString());
        }
      }
    }
  }

  @Test /* NullableVarChar */
  public void testCopyFromWithNulls1() {
    try (final VarCharVector vector =
            newVector(VarCharVector.class, EMPTY_SCHEMA_PATH, MinorType.VARCHAR, allocator);
        final VarCharVector vector2 =
            newVector(VarCharVector.class, EMPTY_SCHEMA_PATH, MinorType.VARCHAR, allocator)) {
      vector.allocateNew();
      assertTrue(vector.getValueCapacity() >= 1);
      assertEquals(0, vector.getValueCount());
      int initialCapacity = vector.getValueCapacity();

      for (int i = 0; i < initialCapacity; i++) {
        if (i % 3 == 0) {
          continue;
        }
        byte[] b = Integer.toString(i).getBytes();
        vector.setSafe(i, b, 0, b.length);
      }

      /* NO reAlloc() should have happened in setSafe() */
      int capacity = vector.getValueCapacity();
      assertEquals(initialCapacity, capacity);

      vector.setValueCount(initialCapacity);

      for (int i = 0; i < initialCapacity; i++) {
        if (i % 3 == 0) {
          assertNull(vector.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i,
              Integer.toString(i),
              vector.getObject(i).toString());
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew((initialCapacity / 4) * 10, initialCapacity / 4);

      capacity = vector2.getValueCapacity();
      assertTrue(capacity >= initialCapacity / 4);
      assertTrue(capacity < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector);
        if (i % 3 == 0) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i,
              Integer.toString(i),
              vector2.getObject(i).toString());
        }
      }

      /* 2 reAllocs should have happened in copyFromSafe() */
      capacity = vector2.getValueCapacity();
      assertTrue(capacity >= initialCapacity);

      vector2.setValueCount(initialCapacity);

      for (int i = 0; i < initialCapacity; i++) {
        if (i % 3 == 0) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i,
              Integer.toString(i),
              vector2.getObject(i).toString());
        }
      }
    }
  }

  @Test /* IntVector */
  public void testCopyFromWithNulls2() {
    try (final IntVector vector1 = new IntVector(EMPTY_SCHEMA_PATH, allocator);
        final IntVector vector2 = new IntVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, 1000 + i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, 1000 + i, vector1.get(i));
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, 1000 + i, vector2.get(i));
        }
      }
    }
  }

  @Test /* BigIntVector */
  public void testCopyFromWithNulls3() {
    try (final BigIntVector vector1 = new BigIntVector(EMPTY_SCHEMA_PATH, allocator);
        final BigIntVector vector2 = new BigIntVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, 10000000000L + (long) i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, 10000000000L + (long) i, vector1.get(i));
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, 10000000000L + (long) i, vector2.get(i));
        }
      }
    }
  }

  @Test /* BitVector */
  public void testCopyFromWithNulls4() {
    try (final BitVector vector1 = new BitVector(EMPTY_SCHEMA_PATH, allocator);
        final BitVector vector2 = new BitVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.setInitialCapacity(4096);
      vector1.allocateNew();
      assertEquals(4096, vector1.getValueCapacity());
      assertEquals(0, vector1.getValueCount());

      int counter = 0;
      for (int i = 0; i < 4096; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        if ((counter & 1) == 0) {
          vector1.setSafe(i, 1);
        } else {
          vector1.setSafe(i, 0);
        }
        counter++;
      }

      vector1.setValueCount(4096);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(4096, vector1.getValueCapacity());
      assertEquals(4096, vector1.getValueCount());

      counter = 0;
      for (int i = 0; i < 4096; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          if ((counter & 1) == 0) {
            assertTrue(vector1.getObject(i));
          } else {
            assertFalse(vector1.getObject(i));
          }
          counter++;
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(1024);
      assertEquals(1024, vector2.getValueCapacity());

      for (int i = 0; i < 4096; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertEquals(4096, vector2.getValueCapacity());
      vector2.setValueCount(8192);
      /* setValueCount() should have done another realloc */
      assertEquals(8192, vector2.getValueCount());
      assertEquals(8192, vector2.getValueCapacity());

      /* check vector data after copy and realloc */
      counter = 0;
      for (int i = 0; i < 8192; i++) {
        if (((i & 1) == 0) || (i >= 4096)) {
          assertNull(vector2.getObject(i));
        } else {
          if ((counter & 1) == 0) {
            assertTrue(vector2.getObject(i));
          } else {
            assertFalse(vector2.getObject(i));
          }
          counter++;
        }
      }
    }
  }

  @Test /* Float4Vector */
  public void testCopyFromWithNulls5() {
    try (final Float4Vector vector1 = new Float4Vector(EMPTY_SCHEMA_PATH, allocator);
        final Float4Vector vector2 = new Float4Vector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, 100.25f + (float) i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, 100.25f + (float) i, vector1.get(i), 0);
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, 100.25f + i * 1.0f, vector2.get(i), 0);
        }
      }
    }
  }

  @Test /* Float8Vector */
  public void testCopyFromWithNulls6() {
    try (final Float8Vector vector1 = new Float8Vector(EMPTY_SCHEMA_PATH, allocator);
        final Float8Vector vector2 = new Float8Vector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, 123456.7865 + (double) i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i, 123456.7865 + (double) i, vector1.get(i), 0);
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals(
              "unexpected value at index: " + i, 123456.7865 + (double) i, vector2.get(i), 0);
        }
      }
    }
  }

  @Test /* IntervalDayVector */
  public void testCopyFromWithNulls7() {
    try (final IntervalDayVector vector1 = new IntervalDayVector(EMPTY_SCHEMA_PATH, allocator);
        final IntervalDayVector vector2 = new IntervalDayVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      final int days = 10;
      final int milliseconds = 10000;
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, days + i, milliseconds + i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          final Duration d = vector1.getObject(i);
          assertEquals(days + i, d.toDays());
          assertEquals(milliseconds + i, d.minusDays(days + i).toMillis());
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          final Duration d = vector2.getObject(i);
          assertEquals(days + i, d.toDays());
          assertEquals(milliseconds + i, d.minusDays(days + i).toMillis());
        }
      }
    }
  }

  @Test /* IntervalYearVector */
  public void testCopyFromWithNulls8() {
    try (final IntervalYearVector vector1 = new IntervalYearVector(EMPTY_SCHEMA_PATH, allocator);
        final IntervalYearVector vector2 = new IntervalYearVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      final int interval = 30; /* 2 years 6 months */
      final Period[] periods = new Period[4096];
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, interval + i);
        final int years = (interval + i) / org.apache.arrow.vector.util.DateUtility.yearsToMonths;
        final int months = (interval + i) % org.apache.arrow.vector.util.DateUtility.yearsToMonths;
        periods[i] = Period.ofYears(years).plusMonths(months).normalized();
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          final Period p = vector1.getObject(i).normalized();
          assertEquals(interval + i, vector1.get(i));
          assertEquals(periods[i], p);
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          final Period p = vector2.getObject(i).normalized();
          assertEquals(periods[i], p);
        }
      }
    }
  }

  @Test /* SmallIntVector */
  public void testCopyFromWithNulls9() {
    try (final SmallIntVector vector1 = new SmallIntVector(EMPTY_SCHEMA_PATH, allocator);
        final SmallIntVector vector2 = new SmallIntVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      final short val = 1000;
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, val + (short) i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + (short) i, vector1.get(i));
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + (short) i, vector2.get(i));
        }
      }
    }
  }

  @Test /* TimeMicroVector */
  public void testCopyFromWithNulls10() {
    try (final TimeMicroVector vector1 = new TimeMicroVector(EMPTY_SCHEMA_PATH, allocator);
        final TimeMicroVector vector2 = new TimeMicroVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      final long val = 100485765432L;
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, val + (long) i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + (long) i, vector1.get(i));
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + (long) i, vector2.get(i));
        }
      }
    }
  }

  @Test /* TimeMilliVector */
  public void testCopyFromWithNulls11() {
    try (final TimeMilliVector vector1 = new TimeMilliVector(EMPTY_SCHEMA_PATH, allocator);
        final TimeMilliVector vector2 = new TimeMilliVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      final int val = 1000;
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, val + i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + i, vector1.get(i));
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + i, vector2.get(i));
        }
      }
    }
  }

  @Test /* TinyIntVector */
  public void testCopyFromWithNulls12() {
    try (final TinyIntVector vector1 = new TinyIntVector(EMPTY_SCHEMA_PATH, allocator);
        final TinyIntVector vector2 = new TinyIntVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      byte val = -128;
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, val);
        val++;
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      val = -128;
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val, vector1.get(i));
          val++;
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      val = -128;
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val, vector2.get(i));
          val++;
        }
      }
    }
  }

  @Test /* DecimalVector */
  public void testCopyFromWithNulls13() {
    try (final DecimalVector vector1 = new DecimalVector(EMPTY_SCHEMA_PATH, allocator, 30, 16);
        final DecimalVector vector2 = new DecimalVector(EMPTY_SCHEMA_PATH, allocator, 30, 16)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      final double baseValue = 104567897654.876543654;
      final BigDecimal[] decimals = new BigDecimal[4096];
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        BigDecimal decimal = new BigDecimal(baseValue + (double) i);
        vector1.setSafe(i, decimal);
        decimals[i] = decimal;
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          final BigDecimal decimal = vector1.getObject(i);
          assertEquals(decimals[i], decimal);
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          final BigDecimal decimal = vector2.getObject(i);
          assertEquals(decimals[i], decimal);
        }
      }
    }
  }

  @Test /* TimeStampVector */
  public void testCopyFromWithNulls14() {
    try (final TimeStampVector vector1 = new TimeStampMicroVector(EMPTY_SCHEMA_PATH, allocator);
        final TimeStampVector vector2 = new TimeStampMicroVector(EMPTY_SCHEMA_PATH, allocator)) {

      vector1.allocateNew();
      assertTrue(vector1.getValueCapacity() >= vector1.INITIAL_VALUE_ALLOCATION);
      assertEquals(0, vector1.getValueCount());
      int initialCapacity = vector1.getValueCapacity();

      final long val = 20145678912L;
      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          continue;
        }
        vector1.setSafe(i, val + (long) i);
      }

      vector1.setValueCount(initialCapacity);

      /* No realloc should have happened in setSafe or
       * setValueCount
       */
      assertEquals(initialCapacity, vector1.getValueCapacity());
      assertEquals(initialCapacity, vector1.getValueCount());

      for (int i = 0; i < initialCapacity; i++) {
        if ((i & 1) == 0) {
          assertNull(vector1.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + (long) i, vector1.get(i));
        }
      }

      /* set lesser initial capacity than actually needed
       * to trigger reallocs in copyFromSafe()
       */
      vector2.allocateNew(initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() >= initialCapacity / 4);
      assertTrue(vector2.getValueCapacity() < initialCapacity / 2);

      for (int i = 0; i < initialCapacity; i++) {
        vector2.copyFromSafe(i, i, vector1);
      }

      /* 2 realloc should have happened in copyFromSafe() */
      assertTrue(vector2.getValueCapacity() >= initialCapacity);
      vector2.setValueCount(initialCapacity * 2);
      /* setValueCount() should have done another realloc */
      assertEquals(initialCapacity * 2, vector2.getValueCount());
      assertTrue(vector2.getValueCapacity() >= initialCapacity * 2);

      /* check vector data after copy and realloc */
      for (int i = 0; i < initialCapacity * 2; i++) {
        if (((i & 1) == 0) || (i >= initialCapacity)) {
          assertNull(vector2.getObject(i));
        } else {
          assertEquals("unexpected value at index: " + i, val + (long) i, vector2.get(i));
        }
      }
    }
  }

  @Test //https://issues.apache.org/jira/browse/ARROW-7837
  public void testCopySafeArrow7837() {
    // this test exposes a bug in `handleSafe` where
    // it reads a stale index and as a result missed a required resize of the value vector.
    try (VarCharVector vc1 = new VarCharVector("vc1", allocator);
         VarCharVector vc2 = new VarCharVector("vc2", allocator);
    ) {
      //initial size is carefully set in order to force the second 'copyFromSafe' operation
      // to trigger a reallocation of the vector.
      vc2.setInitialCapacity(/*valueCount*/20, /*density*/0.5);

      vc1.setSafe(0, "1234567890".getBytes(Charset.forName("utf-8")));
      assertFalse(vc1.isNull(0));
      assertEquals(vc1.getObject(0).toString(), "1234567890");

      vc2.copyFromSafe(0, 0, vc1);
      assertFalse(vc2.isNull(0));
      assertEquals(vc2.getObject(0).toString(), "1234567890");

      vc2.copyFromSafe(0, 5, vc1);
      assertTrue(vc2.isNull(1));
      assertTrue(vc2.isNull(2));
      assertTrue(vc2.isNull(3));
      assertTrue(vc2.isNull(4));
      assertFalse(vc2.isNull(5));
      assertEquals(vc2.getObject(5).toString(), "1234567890");
    }
  }


}
