<commit_before>package com.github.lstephen.ootp.ai.stats;

import com.github.lstephen.ai.search.HillClimbing;
import com.github.lstephen.ai.search.action.Action;
import com.github.lstephen.ai.search.action.SequencedAction;
import com.github.lstephen.ootp.ai.io.Printable;
import com.github.lstephen.ootp.ai.ootp5.site.LeagueBatting;
import com.github.lstephen.ootp.ai.site.Site;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Ordering;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class BaseRunsCoefficients {

  // http://gosu02.tripod.com/id108.html
  private static final Coefficients GOSU = new Coefficients(0.78, 2.34, 3.9, 2.34, 0.039);

  private static final Coefficients SIMPLE = new Coefficients(0.8, 2.1, 3.4, 1.8, 0.1);

  private static final Coefficients TANGO = new Coefficients(0.726, 1.948, 3.134, 1.694, 0.052);

  private static Coefficients current = GOSU;

  public static double apply(double... stats) {
    return current.apply(stats);
  }

  public static double apply(BattingStats stats) {
    return current.apply(stats);
  }

  public static void calculate(Site site) {
    // B = (R - D)*C/(A - R + D)
    // run regression given that formula for B
    current = calculateHillClimb(site);

    BattingStats stats = site.getLeagueBatting();

    double a = (double) stats.getHits() + stats.getWalks() - stats.getHomeRuns();

    double b = BaseRunsCoefficients.apply(stats);

    double c = (double) stats.getOuts();
    double d = (double) stats.getHomeRuns();

    double bPrime = (stats.getRuns() - d) * c / (a - stats.getRuns() + d);

    current = current.withFactor(bPrime / b);
  }

  public static Coefficients calculateHillClimb(Site site) {
    LeagueBatting lb = new LeagueBatting(site, site.getDefinition().getLeague());

    List<BattingStats> history =
        IntStream.range(0, 5)
            .mapToObj(lb::extractYearsAgo)
            .filter(Optional::isPresent)
            .map(Optional::get)
            .filter(h -> h.getRuns() > 0)
            .collect(Collectors.toList());

    Function<BattingStats, Double> actualB =
        bs -> {
          double a = (double) bs.getHits() + bs.getWalks() - bs.getHomeRuns();
          double c = (double) bs.getOuts();
          double d = (double) bs.getHomeRuns();
          double r = (double) bs.getRuns();
          return (r - d) * c / (a - r + d);
        };

    Function<double[], Double> rsme =
        ds ->
            Math.sqrt(
                history
                        .stream()
                        .map(
                            h ->
                                actualB.apply(h)
                                    - new Coefficients(
                                            ds[0] * 0.726,
                                            ds[0] * 1.948,
                                            ds[0] * 3.134,
                                            ds[1],
                                            ds[2])
                                        .apply(h))
                        .map(e -> e * e)
                        .mapToDouble(Double::doubleValue)
                        .sum()
                    / history.size());

    HillClimbing<double[]> hc =
        HillClimbing.<double[]>builder()
            .validator(ds -> ds.length == 3 && Arrays.stream(ds).allMatch(d -> d > 0.0 && d < 10.0))
            .heuristic(Ordering.natural().onResultOf(rsme).reverse())
            .actionGenerator(
                ds -> {
                  Collection<Action<double[]>> addTenths = addToAllIndexes(0.01, ds.length);
                  Collection<Action<double[]>> subTenths = addToAllIndexes(-0.01, ds.length);

                  Stream<Action<double[]>> tenths =
                      Stream.concat(
                          Stream.concat(subTenths.stream(), addTenths.stream()),
                          Stream.concat(
                              SequencedAction.allPairs(addTenths),
                              SequencedAction.allPairs(subTenths)));

                  return Stream.concat(
                      tenths,
                      Stream.concat(
                          addToAllIndexes(0.001, ds.length).stream(),
                          addToAllIndexes(-0.001, ds.length).stream()));
                })
            .build();

    double[] result = hc.search(new double[] {1.0, 1.694, 0.052});

    return new Coefficients(<commit_msg>Allow negative coefficients in B in BaseRuns<commit_after>package com.github.lstephen.ootp.ai.stats;

import com.github.lstephen.ai.search.HillClimbing;
import com.github.lstephen.ai.search.action.Action;
import com.github.lstephen.ai.search.action.SequencedAction;
import com.github.lstephen.ootp.ai.io.Printable;
import com.github.lstephen.ootp.ai.ootp5.site.LeagueBatting;
import com.github.lstephen.ootp.ai.site.Site;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Ordering;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class BaseRunsCoefficients {

  // http://gosu02.tripod.com/id108.html
  private static final Coefficients GOSU = new Coefficients(0.78, 2.34, 3.9, 2.34, 0.039);

  private static final Coefficients SIMPLE = new Coefficients(0.8, 2.1, 3.4, 1.8, 0.1);

  private static final Coefficients TANGO = new Coefficients(0.726, 1.948, 3.134, 1.694, 0.052);

  private static Coefficients current = GOSU;

  public static double apply(double... stats) {
    return current.apply(stats);
  }

  public static double apply(BattingStats stats) {
    return current.apply(stats);
  }

  public static void calculate(Site site) {
    // B = (R - D)*C/(A - R + D)
    // run regression given that formula for B
    current = calculateHillClimb(site);

    BattingStats stats = site.getLeagueBatting();

    double a = (double) stats.getHits() + stats.getWalks() - stats.getHomeRuns();

    double b = BaseRunsCoefficients.apply(stats);

    double c = (double) stats.getOuts();
    double d = (double) stats.getHomeRuns();

    double bPrime = (stats.getRuns() - d) * c / (a - stats.getRuns() + d);

    current = current.withFactor(bPrime / b);
  }

  public static Coefficients calculateHillClimb(Site site) {
    LeagueBatting lb = new LeagueBatting(site, site.getDefinition().getLeague());

    List<BattingStats> history =
        IntStream.range(0, 5)
            .mapToObj(lb::extractYearsAgo)
            .filter(Optional::isPresent)
            .map(Optional::get)
            .filter(h -> h.getRuns() > 0)
            .collect(Collectors.toList());

    Function<BattingStats, Double> actualB =
        bs -> {
          double a = (double) bs.getHits() + bs.getWalks() - bs.getHomeRuns();
          double c = (double) bs.getOuts();
          double d = (double) bs.getHomeRuns();
          double r = (double) bs.getRuns();
          return (r - d) * c / (a - r + d);
        };

    Function<double[], Double> rsme =
        ds ->
            Math.sqrt(
                history
                        .stream()
                        .map(
                            h ->
                                actualB.apply(h)
                                    - new Coefficients(
                                            ds[0] * 0.726,
                                            ds[0] * 1.948,
                                            ds[0] * 3.134,
                                            ds[1],
                                            ds[2])
                                        .apply(h))
                        .map(e -> e * e)
                        .mapToDouble(Double::doubleValue)
                        .sum()
                    / history.size());

    HillClimbing<double[]> hc =
        HillClimbing.<double[]>builder()
            .validator(ds -> ds.length == 3 && Arrays.stream(ds).allMatch(d -> d > -10.0 && d < 10.0))
            .heuristic(Ordering.natural().onResultOf(rsme).reverse())
            .actionGenerator(
                ds -> {
                  Collection<Action<double[]>> addTenths = addToAllIndexes(0.01, ds.length);
                  Collection<Action<double[]>> subTenths = addToAllIndexes(-0.01, ds.length);

                  Stream<Action<double[]>> tenths =
                      Stream.concat(
                          Stream.concat(subTenths.stream(), addTenths.stream()),
                          Stream.concat(
                              SequencedAction.allPairs(addTenths),
                              SequencedAction.allPairs(subTenths)));

                  return Stream.concat(
                      tenths,
                      Stream.concat(
                          addToAllIndexes(0.001, ds.length).stream(),
                          addToAllIndexes(-0.001, ds.length).stream()));
                })
            .build();

    double[] result = hc.search(new double[] {1.0, 1.694, 0.052});

    return new Coefficients(
{'fraction_non_alphanumeric': 0.12461025696161702, 'fraction_numerical': 0.021825610149446294, 'mean_word_length': 1.6915509259259258, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2017395', 'n_tokens_mistral': 2908, 'n_tokens_neox': 2682, 'n_words': 537}