gt:                 double new_dps = this.get_dps();
context: package calcs;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  
  import rogue.Settings;
  import classes.Buffs;
  import classes.GearBuffs;
  import classes.Glyphs;
  import classes.Proc;
  import classes.ProcsList;
  import classes.Race;
  import classes.Stats;
  import classes.Talents;
  import classes.Weapon;
  import core.InvalidInputException;
  import core.util;
  
  /**
   * This class holds the general interface for a damage calculator - the sorts of
   * parameters and calculated values that will be need by many (or most) classes
   * if they implement a damage calculator using this framework. Not saying that
   * will happen, but I want to leave my options open. Any calculations that are
   * specific to a particular class should go in
   * calcs.<class>.<Class>DamageCalculator instead - for an example, see
   * calcs.rogue.RogueDamageCalculator
   */
  public class DamageCalculator {
  
      // If someone wants to have the constructor take a target level as well and
      // use it to initialize these to a level-dependent value, they're welcome
      // to. At the moment I'm hard-coding them to level 85 values.
      protected static final float TARGET_BASE_ARMOR =       (float) 11977.;
      protected static final float BASE_ONE_HAND_MISS_RATE = (float) .08;
      protected static final float BASE_DW_MISS_RATE =       (float) .27;
      protected static final float BASE_SPELL_MISS_RATE =    (float) .17;
      protected static final float BASE_DODGE_CHANCE =       (float) .065;
      protected static final float BASE_PARRY_CHANCE =       (float) .14;
      protected static final float GLANCE_RATE =             (float) .24;
      protected static final float GLANCE_MULTIPLIER =       (float) .75;
  
      private Set<String> default_ep_stats = new HashSet<String>();
      private String normalize_ep_stat = null;  // use the setter to initialize in your subclass.
      private int level = 85;
      private Stats stats;
      private Talents talents;
      private Glyphs glyphs;
      private Buffs buffs;
      private Race race;
      private Settings settings;
      private float armor_mitigation_parameter;
      private boolean dodgeable = true;
      private boolean parryable = false;
      private String ep_flag = "";  // EP helpers makes use of this variable.
  
      /**
       * Constructor. Initializes values and caches values for the information
       * every modeler will need to know about a character. Sets level and
       * eventually propagates it to every object.
       * 
       * @param stats
       *            Stats object; contains gear related stats: numerical stats,
       *            weapons, procs and gear boosts.
       * @param talents
       *            ClassTalent object; contains cached values for talents in the
       *            current game class.
       * @param glyphs
       *            Glyphs object; contains cached values for glyphs in the
       *            current game class.
       * @param buffs
       *            Buffs object; contains raid buffs, food and flasks.
       * @param race
       *            Race object; contains race related stats.
       * @param settings
       *            Settings object; contains fight settings.
       * @param level
       *            Player level.
       */
      public DamageCalculator(Stats stats, Talents talents, Glyphs glyphs, Buffs buffs, Race race, Settings settings, int level) {
          this.stats = stats;
          this.talents = talents;
          this.glyphs = glyphs;
          this.buffs = buffs;
          this.race = race;
          this.settings = settings;
          this.set_level(level);
          if (this.stats.gear_buffs().exists_buff("mixology") && this.buffs.get("agi_flask"))
              this.stats.set("agi", this.stats.get_num_stat("agi") + 80);
          if (this.stats.gear_buffs().exists_buff("master_of_anatomy"))
              this.stats.set("agi", this.stats.get_num_stat("crit") + 80);
      }
  
      /**
       * This is the general method to change the player level in level dependent
       * classes. Will call _set_constants_for_level().
       * 
       * @param level
       *            Player level.
       */
      public void set_level(int level) {
          this.level = level;
          this._set_constants_for_level();
      }
  
      public int level() {
          return this.level;
      }
  
      /**
       * Calculates and caches armor mitigation according to level. Propagates
       * level to every level dependent object.
       */
      protected void _set_constants_for_level() {
          this.buffs.set_level(this.level);
          this.stats.set_level(this.level);
          this.race.set_level(this.level);
          this.armor_mitigation_parameter = ArmorMitigation.parameter(this.level);
      }
  
      /**
       * Call this in your class specific subclass to list appropriate stats.
       * Allowed values are agi, str, spi, int, white_hit, spell_hit, yellow_hit,
       * haste, crit, mastery, dodge_exp, parry_exp, oh_dodge_exp, mh_dodge_exp,
       * oh_parry_exp, mh_parry_exp
       */
      public void set_default_ep_stats(Set<String> args) {
          this.default_ep_stats = args;
      }
  
      /**
       * normalize_ep_stat is the stat with value 1 EP, call in your subclass.
       */
      public void set_normalize_ep_stat(String stat) {
          this.normalize_ep_stat = stat;
      }
  
      /**
       * Most attacks by DPS aren't parryable due to positional negation. But if
       * you ever want to attacking from the front, you can just set that to True.
       */
      public void set_parryable(boolean arg) {
          this.parryable = arg;
      }
  
      public void set_dodgeable(boolean arg) {
          this.dodgeable = arg;
      }
  
      // getters
      public Stats stats() {
          return this.stats;
      }
  
      public Talents talents() {
          return this.talents;
      }
  
      public Glyphs glyphs() {
          return this.glyphs;
      }
  
      public Buffs buffs() {
          return this.buffs;
      }
  
      public Race race() {
          return this.race;
      }
  
      public Settings settings() {
          return this.settings;
      }
  
      // //////////////////////
      // EP functions.
      // //////////////////////
  
      /**
       * The EP methods use this function to bypass capped stats. If we are
       * checking a non capped stat it will add and subtract 1 to the stat and
       * output dps with the changed stat.
       * 
       * @param stat
       * @return Modified dps.
       */
      protected double ep_helper(String stat) {
          Set<String> cap_stats = util.mkSet("dodge_exp", "white_hit",
                  "spell_hit", "yellow_hit", "parry_exp", "mh_dodge_exp",
                  "oh_dodge_exp", "mh_parry_exp", "oh_parry_exp");
          if (!cap_stats.contains(stat))
              this.stats.set(stat, this.stats.get_num_stat(stat) + 1);
          else
              this.ep_flag = stat;
          double dps = get_dps();
          if (!cap_stats.contains(stat))
              this.stats.set(stat, this.stats.get_num_stat(stat) - 1);
          else
              this.ep_flag = "";
  
          return dps;
      }
  
      /**
       * The method to retrieve EP is to modify stats by a small amount and check
       * the dps variance. It deals with numeric stats using ep_helper to modify
       * them by 1. Overloads are provided for different overrides.
       * 
       * @param ep_stats
       *            A set with stats to check; can be defaulted.
       * @param normalize_ep_stat
       *            The stat everything is checked against.
       * @return A hash with EP for numeric stats.
       */
      public Map<String, Double> get_ep(Set<String> ep_stats, String normalize_ep_stat) {
          if (normalize_ep_stat == null)
              normalize_ep_stat = this.normalize_ep_stat;
          if (ep_stats == null)
              ep_stats = this.default_ep_stats;
  
          double baseline_dps = this.get_dps();
          double normalize_dps = this.ep_helper(normalize_ep_stat);
          double normalize_dps_difference = normalize_dps - baseline_dps;
          Map<String, Double> ep_values = new HashMap<String, Double>();
  
          for (String stat : ep_stats) {
              double dps = this.ep_helper(stat);
              double ep = Math.abs(dps - baseline_dps) / normalize_dps_difference;
              ep_values.put(stat, ep);
          }
  
          return ep_values;
      }
  
      /**
       * get_ep() overload. If you overwrite the default stats, this is the
       * method callers should point to.
       */
      public Map<String, Double> get_ep() {
          return get_ep(null, null);
      }
  
      /**
       * get_ep() overload. Call this if default stats were not overwritten.
       */
      public Map<String, Double> get_ep(Set<String> ep_stats) {
          return get_ep(ep_stats, null);
      }
  
      // //////////////////////
      // Weapon EP functions.
      // //////////////////////
  
      /**
       * Changes the weapon dps by 1 and checks dps difference.
       * 
       * @param normalize_ep_stat
       *            The stat everything is checked against.
       * @return A List with two items: mh dps EP and oh dps EP
       */
      public double get_weapon_dps_ep(String hand, String normalize_ep_stat) {
          if (normalize_ep_stat == null)
              normalize_ep_stat = this.normalize_ep_stat;
  
          double baseline_dps = this.get_dps();
          double normalize_dps = this.ep_helper(normalize_ep_stat);
  
          double old_weapon_dps = this.stats.weapon(hand).weapon_dps();
          this.stats.weapon(hand).set_weapon_dps(old_weapon_dps + 1);
          double new_dps = this.get_dps();
          double dps_ep = Math.abs(new_dps - baseline_dps) / (normalize_dps - baseline_dps);
          this.stats.weapon(hand).set_weapon_dps(old_weapon_dps);
  
          return dps_ep;
      }
  
      /**
       * get_weapon_dps_ep(...) overload. Callers will point at this if
       * normalize_ep_stat was overridden.
       */
      public Double get_weapon_dps_ep(String hand) {
          return this.get_weapon_dps_ep(hand, null);
      }
  
      /**
       * Changes the weapon speed and checks dps difference. At some point we
       * should provide a default for these speeds according to class. For the
       * time being callers will need to provide them. Note, however, that speed
       * EP is prone to big variances so most callers won't be using it.
       * 
       * @param speeds A set with different speeds to check.
       * @param normalize_ep_stat The stat everything is checked against.
       * @return A List with two hashes: mh speed EPs and oh speed EPs
       */
      public HashMap<String, Double> get_weapon_speed_ep(String hand, Set<Double> speeds, String normalize_ep_stat) {
          if (normalize_ep_stat == null)
              normalize_ep_stat = this.normalize_ep_stat;
  
          double baseline_dps = this.get_dps();
          double normalize_dps = this.ep_helper(normalize_ep_stat);
          HashMap<String, Double> speed_ep_values = new HashMap<String, Double>();
  
          double old_weapon_speed = this.stats.weapon(hand).speed();
          for (double speed : speeds) {
              this.stats.weapon(hand).set_speed((float) speed);
              double new_dps = this.get_dps();
              double ep = (new_dps - baseline_dps) / (normalize_dps - baseline_dps);
              if (Math.abs(ep) < 0.0001)
                  ep = 0;
              speed_ep_values.put(hand + "_" + speed, ep);
          }
          this.stats.weapon(hand).set_speed(old_weapon_speed);
          return speed_ep_values;
      }
  
      /**
       * get_weapon_speed_ep overload. Callers should point to this if the
       * normalize stat was overridden.
       * 
       * @param speeds A set with different speeds to check.
       * @return A List with two hashes: mh speed EPs and oh speed EPs
       */
      public HashMap<String, Double> get_weapon_speed_ep(String hand, Set<Double> speeds) {
          return this.get_weapon_speed_ep(hand, speeds, null);
      }
  
      public HashMap<String, Double> get_weapon_speed_ep(String hand, double... speeds) {
          Set<Double> set = new HashSet<Double>();
          for (double speed : speeds) {
              set.add(speed);
          }
          return this.get_weapon_speed_ep(hand, set);
      }
  
      /**
       * Checks the dps difference of every melee enchant available. To do so it
       * will del/set allowed enchants from the weapon.
       * 
       * @param normalize_ep_stat The stat everything is checked against.
       * @param hand The weapon we are checking.
       * @return A hash with enchant EPs.
       */
      public HashMap<String, Double> get_weapon_enchant_ep(String hand, String normalize_ep_stat) {
          if (normalize_ep_stat == null)
              normalize_ep_stat = this.normalize_ep_stat;
  
          HashMap<String, Double> enchants_ep_values = new HashMap<String, Double>();
  
          String old_enchant = this.stats.weapon(hand).enchant_name();
          this.stats.weapon(hand);
          for (String enchant : Weapon.allowed_melee_enchants().keySet()) {
              this.stats.weapon(hand).del_enchant();
              double no_enchant_dps = this.get_dps();
              double no_enchant_normalize_dps = this.ep_helper(normalize_ep_stat);
              this.stats.weapon(hand).set_enchant(enchant);
              double new_dps = this.get_dps();
              if (new_dps != no_enchant_dps) {
                  double ep = Math.abs(new_dps - no_enchant_dps) / (no_enchant_normalize_dps - no_enchant_dps);
                  enchants_ep_values.put(enchant, ep);
              }
              this.stats.weapon(hand).set_enchant(old_enchant);
          }
          return enchants_ep_values;
      }
  
      /**
       * get_weapon_enchant_ep overload. Callers should point to this if the
       * normalize stat was overridden.
       * 
       * @return A Hash with enchant EPs.
       */
      public HashMap<String, Double> get_weapon_enchant_ep(String hand) {
          return get_weapon_enchant_ep(hand, null);
      }
  
      // //////////////////////
      // Other EP functions.
      // //////////////////////
  
      /**
       * Checks the dps difference of every boost/proc passed. To do so it will
       * del/set boosts and procs from the appropriate object. Note that activated
       * abilities like trinkets, potions, or engineering gizmos are handled as
       * gear buffs by the engine.
       * 
       * @param args
       *            A set with procs/boosts to check.
       * @param normalize_ep_stat
       *            The stat everything is checked against.
       * @return A hash containing the EP for each boost or proc
       */
      public Map<String, Double> get_other_ep(Set<String> args, String normalize_ep_stat) {
          if (normalize_ep_stat.equals(""))
              normalize_ep_stat = this.normalize_ep_stat;
  
          double baseline_dps = this.get_dps();
          double normalize_dps = this.ep_helper(normalize_ep_stat);
          Set<String> proc_set = new HashSet<String>();
          Set<String> boost_set = new HashSet<String>();
          Map<String, Double> ep_values = new HashMap<String, Double>();
  
          for (String i : args) {
              if (ProcsList.allowed_procs().contains(i))
                  proc_set.add(i);
              else if (GearBuffs.allowed_buffs().contains(i))
                  boost_set.add(i);
              else
                  ep_values.put(i, -1.);  // not allowed
          }
  
          for (String i : boost_set) {
              this.stats.gear_buffs().switch_buff(i);
              double new_dps = this.get_dps();
              double ep = Math.abs(new_dps - baseline_dps) / (normalize_dps - baseline_dps);
              ep_values.put(i, ep);
              this.stats.gear_buffs().switch_buff(i);
          }
  
          for (String i : proc_set) {
              try {
                  if (this.stats.procs().exists_proc(i))
                      this.stats.procs().del_proc(i);
                  else
                      this.stats.procs().append_proc(i);
  