content: /*
   * 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 accord.utils;
  
  import java.lang.reflect.Array;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.List;
  import java.util.NavigableMap;
  import java.util.NavigableSet;
  import java.util.Set;
  import java.util.SortedMap;
  import java.util.SortedSet;
  import java.util.TreeMap;
  import java.util.TreeSet;
  import java.util.function.BiFunction;
  import java.util.function.IntFunction;
  import java.util.function.Predicate;
  
  import com.google.common.collect.ImmutableSet;
  import com.google.common.collect.ImmutableSortedMap;
  import com.google.common.collect.ImmutableSortedSet;
  
  public class Utils
  {
      // reimplements Collection#toArray
      public static <T> T[] toArray(Collection<T> src, IntFunction<T[]> factory)
      {
          T[] dst = factory.apply(src.size());
          int i = 0;
          for (T item : src)
              dst[i++] = item;
          return dst;
      }
  
      // reimplements Collection#toArray
      public static <T> T[] toArray(List<T> src, IntFunction<T[]> factory)
      {
          T[] dst = factory.apply(src.size());
          for (int i=0; i<dst.length; i++)
              dst[i] = src.get(i);
          return dst;
      }
  
      // reimplements List#of
      public static <T> List<T> listOf(T... src)
      {
          List<T> dst = new ArrayList<>(src.length);
          for (int i=0; i<src.length; i++)
              dst.add(src[i]);
          return dst;
      }
  
      public static <T> ImmutableSortedSet<T> ensureSortedImmutable(SortedSet<T> set)
      {
          if (set == null || set.isEmpty())
              return ImmutableSortedSet.of();
          return set instanceof ImmutableSortedSet ? (ImmutableSortedSet<T>) set : ImmutableSortedSet.copyOf(set);
      }
  
      public static <K, V> ImmutableSortedMap<K, V> ensureSortedImmutable(SortedMap<K, V> map)
      {
          if (map == null || map.isEmpty())
              return ImmutableSortedMap.of();
          return map instanceof ImmutableSortedMap ? (ImmutableSortedMap<K, V>) map : ImmutableSortedMap.copyOf(map);
      }
  
      public static <T> ImmutableSet<T> ensureImmutable(java.util.Set<T> set)
      {
          if (set == null || set.isEmpty())
              return ImmutableSet.of();
          return set instanceof ImmutableSet ? (ImmutableSet<T>) set : ImmutableSet.copyOf(set);
      }
  
      public static <T extends Comparable<? super T>> NavigableSet<T> ensureSortedMutable(NavigableSet<T> set)
      {
          if (set == null)
              return new TreeSet<>();
          return set instanceof ImmutableSortedSet ? new TreeSet<>(set) : set;
      }
  
      public static <K extends Comparable<K>, V> NavigableMap<K, V> ensureSortedMutable(NavigableMap<K, V> map)
      {
          if (map == null)
              return new TreeMap<>();
          return map instanceof ImmutableSortedMap ? new TreeMap<>(map) : map;
      }
  
      public static <T> Set<T> ensureMutable(Set<T> set)
      {
          if (set == null)
              return new HashSet<>();
          return set instanceof ImmutableSet ? new HashSet<>(set) : set;
      }
  
      public static LargeBitSet ensureMutable(LargeBitSet set)
      {
          if (set == null) return null;
          return set instanceof ImmutableBitSet ? new LargeBitSet(set) : set;
      }
  
      public static ImmutableBitSet ensureImmutable(LargeBitSet set)
      {
          if (set == null) return null;
          return set instanceof ImmutableBitSet ? (ImmutableBitSet) set : new ImmutableBitSet(set, true);
      }
  
      public static <T> T[] addAll(T[] first, T[] second)
      {
          T[] array = (T[]) Array.newInstance(first.getClass().getComponentType(), first.length + second.length);
          System.arraycopy(first, 0, array, 0, first.length);
          System.arraycopy(second, 0, array, first.length, second.length);
          return array;
      }
  
      public static void reverse(int[] array)
      {
          for (int i = 0; i < array.length / 2; i++)
          {
              int tmp = array[i];
              array[i] = array[array.length- 1 - i];
              array[array.length - 1 - i] = tmp;
          }
      }
  
      public static void shuffle(int[] array, RandomSource rs)
      {
          for (int i = array.length; i > 1; i--)
          {
              int j = i - 1;
              int k = rs.nextInt(j);
              int tmp = array[j];
              array[j] = array[k];
              array[k] = tmp;
          }
      }
  
      public static void shuffle(Object[] array, RandomSource rs)
      {
          for (int i = array.length; i > 1; i--)
          {
              int j = i - 1;
              int k = rs.nextInt(j);
              Object tmp = array[j];
              array[j] = array[k];
              array[k] = tmp;
          }
      }
  
      public static <A, B> B reduce(B zero,
                                    Iterable<A> input,
                                    Predicate<A> filter,
                                    BiFunction<B, ? super A, B> reducer)
      {
          B result = zero;
          for (A a : input)
          {
              if (filter.test(a))
                  result = reducer.apply(result, a);
          }
          return result;
      }
  }
  