SyntheticRealComparisonReport.java

package org.hammer.audio.experimental.acoustic.feature.comparison;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Comparison report between synthetic and real wingbeat feature distributions.
 *
 * <p>Contains one {@link FeatureDifference} per feature and exposes a {@link
 * #generatorWeaknesses()} accessor that returns the subset of features where the relative mean
 * difference exceeds a configurable threshold.
 *
 * @param differences per-feature differences in extraction order; must not be empty
 * @param weaknessThreshold relative-difference threshold above which a feature is considered a
 *     generator weakness; must be finite and {@code > 0}
 */
public record SyntheticRealComparisonReport(
    List<FeatureDifference> differences, double weaknessThreshold) {

  /** Default relative-difference threshold used by {@link SyntheticRealComparison}. */
  public static final double DEFAULT_WEAKNESS_THRESHOLD = 0.20;

  /** Validate and defensively copy fields. */
  public SyntheticRealComparisonReport {
    Objects.requireNonNull(differences, "differences");
    if (differences.isEmpty()) {
      throw new IllegalArgumentException("differences must not be empty");
    }
    if (!Double.isFinite(weaknessThreshold) || weaknessThreshold <= 0.0) {
      throw new IllegalArgumentException("weaknessThreshold must be finite and > 0");
    }
    differences = List.copyOf(differences);
  }

  /**
   * Return features where {@link FeatureDifference#relativeDifference()} exceeds the configured
   * {@link #weaknessThreshold()}.
   *
   * @return list of generator weaknesses; never {@code null}, may be empty
   */
  public List<FeatureDifference> generatorWeaknesses() {
    List<FeatureDifference> weak = new ArrayList<>();
    for (FeatureDifference diff : differences) {
      if (diff.relativeDifference() > weaknessThreshold) {
        weak.add(diff);
      }
    }
    return List.copyOf(weak);
  }
}