/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/
 */
package net.ranides.assira.collection.map;

import com.google.caliper.Param;
import com.google.caliper.SimpleBenchmark;
import com.google.caliper.runner.InvalidBenchmarkException;
import com.google.caliper.util.InvalidCommandException;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import net.ranides.assira.BenchmarkRunner;
import net.ranides.assira.collection.Grid;
import net.ranides.assira.math.Randomizer;

/**
 * Comparison of performance #get methods
 * 
 * RESULTS:
 * 
 *  Integer:
 *     SIZE  DOMAIN   1/HIT  NAME    TREE  SWITCH  HASH   RATIO
 *       32      1K      30   TT       55     55    40     73%
 *      256      1K       4   SS       79     82    41     50%
 *      256    100K     400   SM       79     89    39     43%
 *      256  1 000M      4M   SL       81     84    39     46%
 *   10 000  1 000M    100K   MM      156    160    49     30%
 *   10 000     10K       1   ED      139    154    55     35%
 *  100 000  1 000M     10K   LL      392    361    90     25%
 * 
 *   String:
 *     SIZE  DOMAIN   1/HIT  NAME    TREE  SWITCH  HASH   RATIO
 *       32      1K      30   TT       95     96    69     71%
 *      256      1K       4   SS      123    133    72     54%
 *      256    100K     400   SM      128    135    73     54%
 *      256  1 000M      4M   SL      146    152    78     51%
 *   10 000  1 000M    100K   MM      292    316    98     31%
 *   10 000     10K       1   ED      249    261   113     43%
 *  100 000  1 000M     10K   LL      753    723   169     23%
 *     
 * 
 * SUMMARY:
 * 
 *                 TREE  SWITCH  HASH   RATIO
 *      Integer     981    980   353     34%
 *      String     1786   1816   672     36%
 * 
 * CONCLUSION:
 *      it is classical O(logN) performance identical to TreeMap
 *      it is better in terms of memory usage but slower than HashMap
 * 
 *      difference is observable for every N (even for N=32 hash is 30% better)
 *      its acceptable choice anyway
 * 
 * @author ranides
 */
public class SwitchMapBenchmark extends SimpleBenchmark {
    
    @BenchmarkRunner.ParamGrid({
        "Int_hash", "Int_tree", "Int_smap",
        "Str_hash", "Str_tree", "Str_smap"
    })
    private static final Grid<Integer> PARAMS = new Grid<>()
        .header("param", "n", "domain")
        .add(0,      32,         1_000 )
        .add(1,     256,         1_000 )
        .add(2,     256,       100_000 )
        .add(3,     256, 1_000_000_000 )
        .add(4,  10_000,        10_000 )
        .add(5,  10_000, 1_000_000_000 )
        .add(6, 100_000, 1_000_000_000 );
  
    @Param
    private int param;
    
    private IntParams iparams;
    private StrParams sparams;
    
    @Override
    public void setUp() {
        int n = PARAMS.rows().get(param).get("n");
        int d = PARAMS.rows().get(param).get("domain");
        iparams = new IntParams(n, d);
        sparams = new StrParams(n, d);
    }
    
    private static final class IntParams {
        
        public final int n;
        public final int domain;
        
        public final SortedMap<Integer,Character> tree;
        public final Map<Integer,Character> smap;
        public final Map<Integer,Character> hash;

        public IntParams(int n, int domain) {
            this.n = n;
            this.domain = domain;
            
            tree = new TreeMap<>();
            for(int i=0; i<n; i++) {
                int key = Randomizer.number(domain);
                char val = Randomizer.character();
                tree.put(key, val);
            }
            smap = new SwitchMap<>(tree);
            hash = new HashMap<>(tree);
        }
        
    }
    
    private static final class StrParams {
        
        public final int n;
        public final int domain;
        
        public final SortedMap<String,Character> tree;
        public final Map<String,Character> smap;
        public final Map<String,Character> hash;

        public StrParams(int n, int domain) {
            this.n = n;
            this.domain = domain;
            
            tree = new TreeMap<>();
            for(int i=0; i<n; i++) {
                String key = Integer.toHexString(Randomizer.number(domain));
                char val = Randomizer.character();
                this.tree.put(key, val);
            }
            smap = new SwitchMap<>(tree);
            hash = new HashMap<>(tree);
        }
        
    }
    
    private int itimeIntMap(int reps, int domain, Map<Integer, Character> map) {
        int c = 0;
        for(int i=0; i<reps; i++) {
            int key = Randomizer.number(domain);
            Character val = map.get(key);
            c += (null == val) ? 1 : 2;
        }
        return c;
    }
    
    private int itimeStrMap(int reps, int domain, Map<String, Character> map) {
        int c = 0;
        for(int i=0; i<reps; i++) {
            String key = Long.toHexString(Randomizer.number(domain));
            Character val = map.get(key);
            c += (null == val) ? 1 : 2;
        }
        return c;
    }
    
    public int timeInt_smap(int reps) {
        return itimeIntMap(reps, iparams.domain, iparams.smap);
    }
    
    public int timeInt_tree(int reps) {
        return itimeIntMap(reps, iparams.domain, iparams.tree);
    }
    
    public int timeInt_hash(int reps) {
        return itimeIntMap(reps, iparams.domain, iparams.hash);
    }
    
    public int timeStr_smap(int reps) {
        return itimeStrMap(reps, iparams.domain, sparams.smap);
    }
    
    public int timeStr_tree(int reps) {
        return itimeStrMap(reps, iparams.domain, sparams.tree);
    }
    
    public int timeStr_hash(int reps) {
        return itimeStrMap(reps, iparams.domain, sparams.hash);
    }
    
    public static void main(String[] args) throws InvalidCommandException, InvalidBenchmarkException {
        BenchmarkRunner.run();
    }
   
}
