/*
 * Copyright (C) 2017 The Guava Authors
 *
 * Licensed 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 com.google.common.collect;

import static com.google.common.base.Functions.toStringFunction;
import static com.google.common.collect.Maps.uniqueIndex;

import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;
import com.google.caliper.api.Footprint;
import com.google.common.collect.BenchmarkHelpers.BiMapImpl;
import com.google.common.collect.BenchmarkHelpers.MapImpl;
import com.google.common.collect.BenchmarkHelpers.MapsImplEnum;
import com.google.common.collect.BenchmarkHelpers.SortedMapImpl;
import com.google.common.collect.CollectionBenchmarkSampleData.Element;
import java.util.Arrays;
import java.util.Map;

/** Benchmarks for memory consumption of map implementations. */
public class MapsMemoryBenchmark {
  static final Map<String, MapsImplEnum> mapEnums =
      uniqueIndex(
          Iterables.<MapsImplEnum>concat(
              Arrays.asList(MapImpl.values()),
              Arrays.asList(SortedMapImpl.values()),
              Arrays.asList(BiMapImpl.values())),
          toStringFunction());

  @Param({
    "HashMapImpl",
    "LinkedHashMapImpl",
    "ConcurrentHashMapImpl",
    "ImmutableMapImpl",
    "TreeMapImpl",
    "ImmutableSortedMapImpl",
    "MapMakerWeakKeysWeakValues",
    "MapMakerWeakKeysStrongValues",
    "MapMakerStrongKeysWeakValues",
    "MapMakerStrongKeysStrongValues",
    "HashBiMapImpl",
    "ImmutableBiMapImpl"
  })
  String implName;

  MapsImplEnum mapsImpl;

  /**
   * A map of contents pre-created before experiment starts to only measure map creation cost. The
   * implementation for the creation of contents is independent and could be different from that of
   * the map under test.
   */
  Map<Element, Element> contents;

  /** Map pre-created before experiment starts to only measure iteration cost during experiment. */
  Map<Element, Element> map;

  CollectionBenchmarkSampleData elems;

  @Param({"0", "1", "100", "10000"})
  int elements;

  @BeforeExperiment
  public void prepareContents() throws Exception {
    mapsImpl = mapEnums.get(implName);
    elems = new CollectionBenchmarkSampleData(elements);
    contents = Maps.newHashMap();
    for (Element key : elems.getValuesInSet()) {
      contents.put(key, key);
    }
    map = mapsImpl.create(contents);
  }

  @Benchmark
  @Footprint(exclude = Element.class)
  public Map<Element, Element> create() throws Exception {
    return mapsImpl.create(contents);
  }

  @Benchmark
  public int iterate() {
    long retVal = 0;
    for (Object entry : map.entrySet()) {
      retVal += entry.hashCode();
    }
    return (int) retVal;
  }

  @Benchmark
  public int keyIterate() {
    long retVal = 0;
    for (Object key : map.keySet()) {
      retVal += key.hashCode();
    }
    return (int) retVal;
  }
}
