#!/usr/bin/env perl
#
# Determine how well the ratios in arbitrary Scala scale files fit to
# those from a given Scala scale file. Lower numbers are a better fit;
# where there are unequal number of intervals in the scale files, the
# closest interval to the interval under consideration will be used.
#
# For example, this checks the scala scale directory from
# http://www.huygens-fokker.org/docs/scales.zip
# against a 53-note overtone-based scale for the worst fits:
#
#   perl fitness sparschuh_53in13lim.scl \
#     | grep '1200$' | sort -nr | sed 22q
#
# The worst fits are various gamelan temperaments; these have their own
# scale systems independent of Western tradition. Fiddle with the
# fortunes of history, and gamelan scholars might instead ask why
# Western scale systems so poorly fit gamelan theories.

use strict;
use warnings;
use feature qw/say/;

use File::Spec ();
use List::Util 1.26.1 qw/min sum0/;
use Music::Scala ();

my $reference_scala = shift or die "Usage: $0 reference.scl [scala-dir]\n";
my $dir = shift || '.';

my $s = Music::Scala->new( file => $reference_scala );

my @ref_cents = $s->get_cents;

FILE: for my $file ( glob(File::Spec->catfile( $dir, '*.scl' ))) {
    my @cents        = $s->read_scala($file)->get_cents;
    my $note_count   = @cents;
    my $ratio_ultima = sprintf "%.0f", $cents[-1];

    my @fits;
    my $prev_c = 0;
    for my $c (@cents) {
        # some scales have negative cents--skip these, want ascending-
        # only scales (nor subharmonic or whatever scales that repeat)
        next FILE if $c < 0 or $c <= $prev_c;

        # TODO figure out the square-vs-absolute value thing in stats
        # one of these years
        #push @fits, min map { ( $c - $_ )**2 } @ref_cents;
        push @fits, min map { abs( $c - $_ ) } @ref_cents;

        $prev_c = $c;
    }

    my $sum = sum0 @fits;

    # Mean delta (might also look at deeper stats, see which has the
    # worst outliers, histogram things up, etc.)
    say sprintf( "%.2f", $sum / @fits ),
      " $file notes $note_count ur $ratio_ultima";
}
