/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.   *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2011                *
 * by the Xiph.Org Foundation http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************

  function: mode selection code
  last mod: $Id$

 ********************************************************************/
#include <stdio.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#include "collect.h"

#if defined(OC_COLLECT_METRICS)

int              OC_HAS_MODE_METRICS;
double           OC_MODE_RD_WEIGHT_SATD[OC_LOGQ_BINS][3][2][OC_COMP_BINS];
double           OC_MODE_RD_WEIGHT_SAD[OC_LOGQ_BINS][3][2][OC_COMP_BINS];
oc_mode_metrics  OC_MODE_METRICS_SATD[OC_LOGQ_BINS-1][3][2][OC_COMP_BINS];
oc_mode_metrics  OC_MODE_METRICS_SAD[OC_LOGQ_BINS-1][3][2][OC_COMP_BINS];
const char      *OC_MODE_METRICS_FILENAME="modedec.stats";

void oc_mode_metrics_add(oc_mode_metrics *_metrics,
 double _w,int _s,int _q,int _r,double _d){
  if(_metrics->w>0){
    double ds;
    double dq;
    double dr;
    double dd;
    double ds2;
    double dq2;
    double s2;
    double sq;
    double q2;
    double sr;
    double qr;
    double sd;
    double qd;
    double s2q;
    double sq2;
    double w;
    double wa;
    double rwa;
    double rwa2;
    double rwb;
    double rwb2;
    double rw2;
    double rw3;
    double rw4;
    wa=_metrics->w;
    ds=_s-_metrics->s/wa;
    dq=_q-_metrics->q/wa;
    dr=_r-_metrics->r/wa;
    dd=_d-_metrics->d/wa;
    ds2=ds*ds;
    dq2=dq*dq;
    s2=_metrics->s2;
    sq=_metrics->sq;
    q2=_metrics->q2;
    sr=_metrics->sr;
    qr=_metrics->qr;
    sd=_metrics->sd;
    qd=_metrics->qd;
    s2q=_metrics->s2q;
    sq2=_metrics->sq2;
    w=wa+_w;
    rwa=wa/w;
    rwb=_w/w;
    rwa2=rwa*rwa;
    rwb2=rwb*rwb;
    rw2=wa*rwb;
    rw3=rw2*(rwa2-rwb2);
    rw4=_w*rwa2*rwa2+wa*rwb2*rwb2;
    _metrics->s2q2+=-2*(ds*sq2+dq*s2q)*rwb
     +(ds2*q2+4*ds*dq*sq+dq2*s2)*rwb2+ds2*dq2*rw4;
    _metrics->s2q+=(-2*ds*sq-dq*s2)*rwb+ds2*dq*rw3;
    _metrics->sq2+=(-ds*q2-2*dq*sq)*rwb+ds*dq2*rw3;
    _metrics->sqr+=(-ds*qr-dq*sr-dr*sq)*rwb+ds*dq*dr*rw3;
    _metrics->sqd+=(-ds*qd-dq*sd-dd*sq)*rwb+ds*dq*dd*rw3;
    _metrics->s2+=ds2*rw2;
    _metrics->sq+=ds*dq*rw2;
    _metrics->q2+=dq2*rw2;
    _metrics->sr+=ds*dr*rw2;
    _metrics->qr+=dq*dr*rw2;
    _metrics->r2+=dr*dr*rw2;
    _metrics->sd+=ds*dd*rw2;
    _metrics->qd+=dq*dd*rw2;
    _metrics->d2+=dd*dd*rw2;
  }
  _metrics->w+=_w;
  _metrics->s+=_s*_w;
  _metrics->q+=_q*_w;
  _metrics->r+=_r*_w;
  _metrics->d+=_d*_w;
}

void oc_mode_metrics_merge(oc_mode_metrics *_dst,
 const oc_mode_metrics *_src,int _n){
  int i;
  /*Find a non-empty set of metrics.*/
  for(i=0;i<_n&&_src[i].w==0;i++);
  if(i>=_n){
    memset(_dst,0,sizeof(*_dst));
    return;
  }
  memcpy(_dst,_src+i,sizeof(*_dst));
  /*And iterate over the remaining non-empty sets of metrics.*/
  for(i++;i<_n;i++)if(_src[i].w!=0){
    double ds;
    double dq;
    double dr;
    double dd;
    double ds2;
    double dq2;
    double s2a;
    double s2b;
    double sqa;
    double sqb;
    double q2a;
    double q2b;
    double sra;
    double srb;
    double qra;
    double qrb;
    double sda;
    double sdb;
    double qda;
    double qdb;
    double s2qa;
    double s2qb;
    double sq2a;
    double sq2b;
    double w;
    double wa;
    double wb;
    double rwa;
    double rwb;
    double rwa2;
    double rwb2;
    double rw2;
    double rw3;
    double rw4;
    wa=_dst->w;
    wb=_src[i].w;
    ds=_src[i].s/wb-_dst->s/wa;
    dq=_src[i].q/wb-_dst->q/wa;
    dr=_src[i].r/wb-_dst->r/wa;
    dd=_src[i].d/wb-_dst->d/wa;
    ds2=ds*ds;
    dq2=dq*dq;
    s2a=_dst->s2;
    sqa=_dst->sq;
    q2a=_dst->q2;
    sra=_dst->sr;
    qra=_dst->qr;
    sda=_dst->sd;
    qda=_dst->qd;
    s2qa=_dst->s2q;
    sq2a=_dst->sq2;
    s2b=_src[i].s2;
    sqb=_src[i].sq;
    q2b=_src[i].q2;
    srb=_src[i].sr;
    qrb=_src[i].qr;
    sdb=_src[i].sd;
    qdb=_src[i].qd;
    s2qb=_src[i].s2q;
    sq2b=_src[i].sq2;
    w=wa+wb;
    if(w==0)rwa=rwb=0;
    else{
      rwa=wa/w;
      rwb=wb/w;
    }
    rwa2=rwa*rwa;
    rwb2=rwb*rwb;
    rw2=wa*rwb;
    rw3=rw2*(rwa2-rwb2);
    rw4=wb*rwa2*rwa2+wa*rwb2*rwb2;
    /*
    (1,1,1) ->
     (0,0,0)#
     (1,0,0) C(1,1)*C(1,0)*C(1,0)->  d^{1,0,0}*(rwa*B_{0,1,1}-rwb*A_{0,1,1})
     (0,1,0) C(1,0)*C(1,1)*C(1,0)->  d^{0,1,0}*(rwa*B_{1,0,1}-rwb*A_{1,0,1})
     (0,0,1) C(1,0)*C(1,0)*C(1,1)->  d^{0,0,1}*(rwa*B_{1,1,0}-rwb*A_{1,1,0})
     (1,1,0)*
     (1,0,1)*
     (0,1,1)*
     (1,1,1) C(1,1)*C(1,1)*C(1,1)->  d^{1,1,1}*(rwa^3*wb-rwb^3*wa)
    (2,1) ->
     (0,0)#
     (1,0) C(2,1)*C(1,1)->2*d^{1,0}*(rwa*B_{1,1}-rwb*A_{1,1})
     (0,1) C(2,0)*C(1,1)->  d^{0,1}*(rwa*B_{2,0}-rwb*A_{2,0})
     (2,0)*
     (1,1)*
     (2,1) C(2,2)*C(1,1)->  d^{2,1}*(rwa^3*wb-rwb^3*wa)
    (2,2) ->
     (0,0)#
     (1,0) C(2,1)*C(2,0)->2*d^{1,0}*(rwa*B_{1,2}-rwb*A_{1,2})
     (0,1) C(2,0)*C(2,1)->2*d^{0,1}*(rwa*B_{2,1}-rwb*A_{2,1})
     (2,0) C(2,2)*C(2,0)->  d^{2,0}*(rwa^2*B_{0,2}+rwb^2*A_{0,2})
     (1,1) C(2,1)*C(2,1)->4*d^{1,1}*(rwa^2*B_{1,1}+rwb^2*A_{1,1})
     (0,2) C(2,0)*C(2,2)->  d^{0,2}*(rwa^2*B_{2,0}+rwb^2*A_{2,0})
     (1,2)*
     (2,1)*
     (2,2) C(2,2)*C(2,2)*d^{2,2}*(rwa^4*wb+rwb^4*wa)
    */
    _dst->s2q2+=_src[i].s2q2+2*(ds*(rwa*sq2b-rwb*sq2a)+dq*(rwa*s2qb-rwb*s2qa))
     +ds2*(rwa2*q2b+rwb2*q2a)+4*ds*dq*(rwa2*sqb+rwb2*sqa)
     +dq2*(rwa2*s2b+rwb2*s2a)+ds2*dq2*rw4;
    _dst->s2q+=_src[i].s2q+2*ds*(rwa*sqb-rwb*sqa)
     +dq*(rwa*s2b-rwb*s2a)+ds2*dq*rw3;
    _dst->sq2+=_src[i].sq2+ds*(rwa*q2b-rwb*q2a)
     +2*dq*(rwa*sqb-rwb*sqa)+ds*dq2*rw3;
    _dst->sqr+=_src[i].sqr+ds*(rwa*qrb-rwb*qra)+dq*(rwa*srb-rwb*sra)
     +dr*(rwa*sqb-rwb*sqa)+ds*dq*dr*rw3;
    _dst->sqd+=_src[i].sqd+ds*(rwa*qdb-rwb*qda)+dq*(rwa*sdb-rwb*sda)
     +dd*(rwa*sqb-rwb*sqa)+ds*dq*dd*rw3;
    _dst->s2+=_src[i].s2+ds2*rw2;
    _dst->sq+=_src[i].sq+ds*dq*rw2;
    _dst->q2+=_src[i].q2+dq2*rw2;
    _dst->sr+=_src[i].sr+ds*dr*rw2;
    _dst->qr+=_src[i].qr+dq*dr*rw2;
    _dst->r2+=_src[i].r2+dr*dr*rw2;
    _dst->sd+=_src[i].sd+ds*dd*rw2;
    _dst->qd+=_src[i].qd+dq*dd*rw2;
    _dst->d2+=_src[i].d2+dd*dd*rw2;
    _dst->w+=_src[i].w;
    _dst->s+=_src[i].s;
    _dst->q+=_src[i].q;
    _dst->r+=_src[i].r;
    _dst->d+=_src[i].d;
  }
}

/*Adjust a single corner of a set of metric bins to minimize the squared
   prediction error of R and D.
  Each bin is assumed to cover a quad like so:
    (s0,q0)    (s1,q0)
       A----------B
       |          |
       |          |
       |          |
       |          |
       C----------Z
    (s0,q1)    (s1,q1)
  The values A, B, and C are fixed, and Z is the free parameter.
  Then, for example, R_i is predicted via bilinear interpolation as
    x_i=(s_i-s0)/(s1-s0)
    y_i=(q_i-q0)/(q1-q0)
    dRds1_i=A+(B-A)*x_i
    dRds2_i=C+(Z-C)*x_i
    R_i=dRds1_i+(dRds2_i-dRds1_i)*y_i
  To find the Z that minimizes the squared prediction error over i, this can
   be rewritten as
    R_i-(A+(B-A)*x_i+(C-A)*y_i+(A-B-C)*x_i*y_i)=x_i*y_i*Z
  Letting X={...,x_i*y_i,...}^T and
   Y={...,R_i-(A+(B-A)*x_i+(C-A)*y_i+(A-B-C)*x_i*y_i),...}^T,
   the optimal Z is given by Z=(X^T.Y)/(X^T.X).
  Now, we need to compute these dot products without actually storing data for
   each sample.
  Starting with X^T.X, we have
   X^T.X = sum(x_i^2*y_i^2) = sum((s_i-s0)^2*(q_i-q0)^2)/((s1-s0)^2*(q1-q0)^2).
  Expanding the interior of the sum in a monomial basis of s_i and q_i gives
    s0^2*q0^2  *(1)
     -2*s0*q0^2*(s_i)
     -2*s0^2*q0*(q_i)
     +q0^2     *(s_i^2)
     +4*s0*q0  *(s_i*q_i)
     +s0^2     *(q_i^2)
     -2*q0     *(s_i^2*q_i)
     -2*s0     *(s_i*q_i^2)
     +1        *(s_i^2*q_i^2).
  However, computing things directly in this basis leads to gross numerical
   errors, as most of the terms will have similar size and destructive
   cancellation results.
  A much better basis is the central (co-)moment basis:
    {1,s_i-sbar,q_i-qbar,(s_i-sbar)^2,(s_i-sbar)*(q_i-qbar),(q_i-qbar)^2,
     (s_i-sbar)^2*(q_i-qbar),(s_i-sbar)*(q_i-qbar)^2,(s_i-sbar)^2*(q_i-qbar)^2},
   where sbar and qbar are the average s and q values over the bin,
   respectively.
  In that basis, letting ds=sbar-s0 and dq=qbar-q0, (s_i-s0)^2*(q_i-q0)^2 is
    ds^2*dq^2*(1)
     +dq^2   *((s_i-sbar)^2)
     +4*ds*dq*((s_i-sbar)*(q_i-qbar))
     +ds^2   *((q_i-qbar)^2)
     +2*dq   *((s_i-sbar)^2*(q_i-qbar))
     +2*ds   *((s_i-sbar)*(q_i-qbar)^2)
     +1      *((s_i-sbar)^2*(q_i-qbar)^2).
  With these expressions in the central (co-)moment bases, all we need to do
   is compute sums over the (co-)moment terms, which can be done
   incrementally (see oc_mode_metrics_add() and oc_mode_metrics_merge()),
   with no need to store the individual samples.
  Now, for X^T.Y, we have
    X^T.Y = sum((R_i-A-((B-A)/(s1-s0))*(s_i-s0)-((C-A)/(q1-q0))*(q_i-q0)
     -((A-B-C)/((s1-s0)*(q1-q0)))*(s_i-s0)*(q_i-q0))*(s_i-s0)*(q_i-q0))/
     ((s1-s0)*(q1-q0)),
   or, rewriting the constants to simplify notation,
    X^T.Y = sum((C0+C1*(s_i-s0)+C2*(q_i-q0)
     +C3*(s_i-s0)*(q_i-q0)+R_i)*(s_i-s0)*(q_i-q0))/((s1-s0)*(q1-q0)).
  Again, converting to the central (co-)moment basis, the interior of the
   above sum is
    ds*dq*(rbar+C0+C1*ds+C2*dq+C3*ds*dq)  *(1)
     +(C1*dq+C3*dq^2)                     *((s_i-sbar)^2)
     +(rbar+C0+2*C1*ds+2*C2*dq+4*C3*ds*dq)*((s_i-sbar)*(q_i-qbar))
     +(C2*ds+C3*ds^2)                     *((q_i-qbar)^2)
     +dq                                  *((s_i-sbar)*(r_i-rbar))
     +ds                                  *((q_i-qbar)*(r_i-rbar))
     +(C1+2*C3*dq)                        *((s_i-sbar)^2*(q_i-qbar))
     +(C2+2*C3*ds)                        *((s_i-sbar)*(q_i-qbar)^2)
     +1                                   *((s_i-sbar)*(q_i-qbar)*(r_i-rbar))
     +C3                                  *((s_i-sbar)^2*(q_i-qbar)^2).
  You might think it would be easier (if perhaps slightly less robust) to
   accumulate terms directly around s0 and q0.
  However, we update each corner of the bins in turn, so we would have to
   change basis to move the sums from corner to corner anyway.*/
double oc_mode_metrics_solve(double *_r,double *_d,
 const oc_mode_metrics *_metrics,const int *_s0,const int *_s1,
 const int *_q0,const int *_q1,
 const double *_ra,const double *_rb,const double *_rc,
 const double *_da,const double *_db,const double *_dc,int _n){
  double xx;
  double rxy;
  double dxy;
  double wt;
  int i;
  xx=rxy=dxy=wt=0;
  for(i=0;i<_n;i++)if(_metrics[i].w>0){
    double s10;
    double q10;
    double sq10;
    double ds;
    double dq;
    double ds2;
    double dq2;
    double r;
    double d;
    double s2;
    double sq;
    double q2;
    double sr;
    double qr;
    double sd;
    double qd;
    double s2q;
    double sq2;
    double sqr;
    double sqd;
    double s2q2;
    double c0;
    double c1;
    double c2;
    double c3;
    double w;
    w=_metrics[i].w;
    wt+=w;
    s10=_s1[i]-_s0[i];
    q10=_q1[i]-_q0[i];
    sq10=s10*q10;
    ds=_metrics[i].s/w-_s0[i];
    dq=_metrics[i].q/w-_q0[i];
    ds2=ds*ds;
    dq2=dq*dq;
    s2=_metrics[i].s2;
    sq=_metrics[i].sq;
    q2=_metrics[i].q2;
    s2q=_metrics[i].s2q;
    sq2=_metrics[i].sq2;
    s2q2=_metrics[i].s2q2;
    xx+=(dq2*(ds2*w+s2)+4*ds*dq*sq+ds2*q2+2*(dq*s2q+ds*sq2)+s2q2)/(sq10*sq10);
    r=_metrics[i].r/w;
    sr=_metrics[i].sr;
    qr=_metrics[i].qr;
    sqr=_metrics[i].sqr;
    c0=-_ra[i];
    c1=-(_rb[i]-_ra[i])/s10;
    c2=-(_rc[i]-_ra[i])/q10;
    c3=-(_ra[i]-_rb[i]-_rc[i])/sq10;
    rxy+=(ds*dq*(r+c0+c1*ds+c2*dq+c3*ds*dq)*w+(c1*dq+c3*dq2)*s2
     +(r+c0+2*(c1*ds+(c2+2*c3*ds)*dq))*sq+(c2*ds+c3*ds2)*q2+dq*sr+ds*qr
     +(c1+2*c3*dq)*s2q+(c2+2*c3*ds)*sq2+sqr+c3*s2q2)/sq10;
    d=_metrics[i].d/w;
    sd=_metrics[i].sd;
    qd=_metrics[i].qd;
    sqd=_metrics[i].sqd;
    c0=-_da[i];
    c1=-(_db[i]-_da[i])/s10;
    c2=-(_dc[i]-_da[i])/q10;
    c3=-(_da[i]-_db[i]-_dc[i])/sq10;
    dxy+=(ds*dq*(d+c0+c1*ds+c2*dq+c3*ds*dq)*w+(c1*dq+c3*dq2)*s2
     +(d+c0+2*(c1*ds+(c2+2*c3*ds)*dq))*sq+(c2*ds+c3*ds2)*q2+dq*sd+ds*qd
     +(c1+2*c3*dq)*s2q+(c2+2*c3*ds)*sq2+sqd+c3*s2q2)/sq10;
  }
  if(xx>1E-3){
    *_r=rxy/xx;
    *_d=dxy/xx;
  }
  else{
    *_r=0;
    *_d=0;
  }
  return wt;
}

/*Compile collected SATD/logq/rate/RMSE metrics into a form that's immediately
   useful for mode decision.*/
void oc_mode_metrics_update(oc_mode_metrics (*_metrics)[3][2][OC_COMP_BINS],
 int _niters_min,int _reweight,oc_mode_rd (*_table)[3][2][OC_COMP_BINS],
 int _shift,double (*_weight)[3][2][OC_COMP_BINS]){
  int niters;
  int prevdr;
  int prevdd;
  int dr;
  int dd;
  int pli;
  int qti;
  int qi;
  int si;
  dd=dr=INT_MAX;
  niters=0;
  /*The encoder interpolates rate and RMSE terms bilinearly from an
     OC_LOGQ_BINS by OC_COMP_BINS grid of sample points in _table.
    To find the sample values at the grid points that minimize the total
     squared prediction error actually requires solving a relatively sparse
     linear system with a number of variables equal to the number of grid
     points.
    Instead of writing a general sparse linear system solver, we just use
     Gauss-Seidel iteration, i.e., we update one grid point at time until
     they stop changing.*/
  do{
    prevdr=dr;
    prevdd=dd;
    dd=dr=0;
    for(pli=0;pli<3;pli++){
      for(qti=0;qti<2;qti++){
        for(qi=0;qi<OC_LOGQ_BINS;qi++){
          for(si=0;si<OC_COMP_BINS;si++){
            oc_mode_metrics m[4];
            int             s0[4];
            int             s1[4];
            int             q0[4];
            int             q1[4];
            double          ra[4];
            double          rb[4];
            double          rc[4];
            double          da[4];
            double          db[4];
            double          dc[4];
            double          r;
            double          d;
            int             rate;
            int             rmse;
            int             ds;
            int             n;
            n=0;
            /*Collect the statistics for the (up to) four bins grid point
               (si,qi) touches.*/
            if(qi>0&&si>0){
              q0[n]=OC_MODE_LOGQ[qi-1][pli][qti];
              q1[n]=OC_MODE_LOGQ[qi][pli][qti];
              s0[n]=si-1<<_shift;
              s1[n]=si<<_shift;
              ra[n]=ldexp(_table[qi-1][pli][qti][si-1].rate,-OC_BIT_SCALE);
              da[n]=ldexp(_table[qi-1][pli][qti][si-1].rmse,-OC_RMSE_SCALE);
              rb[n]=ldexp(_table[qi-1][pli][qti][si].rate,-OC_BIT_SCALE);
              db[n]=ldexp(_table[qi-1][pli][qti][si].rmse,-OC_RMSE_SCALE);
              rc[n]=ldexp(_table[qi][pli][qti][si-1].rate,-OC_BIT_SCALE);
              dc[n]=ldexp(_table[qi][pli][qti][si-1].rmse,-OC_RMSE_SCALE);
              *(m+n++)=*(_metrics[qi-1][pli][qti]+si-1);
            }
            if(qi>0){
              ds=si+1<OC_COMP_BINS?1:-1;
              q0[n]=OC_MODE_LOGQ[qi-1][pli][qti];
              q1[n]=OC_MODE_LOGQ[qi][pli][qti];
              s0[n]=si+ds<<_shift;
              s1[n]=si<<_shift;
              ra[n]=ldexp(_table[qi-1][pli][qti][si+ds].rate,-OC_BIT_SCALE);
              da[n]=
               ldexp(_table[qi-1][pli][qti][si+ds].rmse,-OC_RMSE_SCALE);
              rb[n]=ldexp(_table[qi-1][pli][qti][si].rate,-OC_BIT_SCALE);
              db[n]=ldexp(_table[qi-1][pli][qti][si].rmse,-OC_RMSE_SCALE);
              rc[n]=ldexp(_table[qi][pli][qti][si+ds].rate,-OC_BIT_SCALE);
              dc[n]=ldexp(_table[qi][pli][qti][si+ds].rmse,-OC_RMSE_SCALE);
              *(m+n++)=*(_metrics[qi-1][pli][qti]+si);
            }
            if(qi+1<OC_LOGQ_BINS&&si>0){
              q0[n]=OC_MODE_LOGQ[qi+1][pli][qti];
              q1[n]=OC_MODE_LOGQ[qi][pli][qti];
              s0[n]=si-1<<_shift;
              s1[n]=si<<_shift;
              ra[n]=ldexp(_table[qi+1][pli][qti][si-1].rate,-OC_BIT_SCALE);
              da[n]=ldexp(_table[qi+1][pli][qti][si-1].rmse,-OC_RMSE_SCALE);
              rb[n]=ldexp(_table[qi+1][pli][qti][si].rate,-OC_BIT_SCALE);
              db[n]=ldexp(_table[qi+1][pli][qti][si].rmse,-OC_RMSE_SCALE);
              rc[n]=ldexp(_table[qi][pli][qti][si-1].rate,-OC_BIT_SCALE);
              dc[n]=ldexp(_table[qi][pli][qti][si-1].rmse,-OC_RMSE_SCALE);
              *(m+n++)=*(_metrics[qi][pli][qti]+si-1);
            }
            if(qi+1<OC_LOGQ_BINS){
              ds=si+1<OC_COMP_BINS?1:-1;
              q0[n]=OC_MODE_LOGQ[qi+1][pli][qti];
              q1[n]=OC_MODE_LOGQ[qi][pli][qti];
              s0[n]=si+ds<<_shift;
              s1[n]=si<<_shift;
              ra[n]=ldexp(_table[qi+1][pli][qti][si+ds].rate,-OC_BIT_SCALE);
              da[n]=
               ldexp(_table[qi+1][pli][qti][si+ds].rmse,-OC_RMSE_SCALE);
              rb[n]=ldexp(_table[qi+1][pli][qti][si].rate,-OC_BIT_SCALE);
              db[n]=ldexp(_table[qi+1][pli][qti][si].rmse,-OC_RMSE_SCALE);
              rc[n]=ldexp(_table[qi][pli][qti][si+ds].rate,-OC_BIT_SCALE);
              dc[n]=ldexp(_table[qi][pli][qti][si+ds].rmse,-OC_RMSE_SCALE);
              *(m+n++)=*(_metrics[qi][pli][qti]+si);
            }
            /*On the first pass, initialize with a simple weighted average of
               the neighboring bins.*/
            if(!OC_HAS_MODE_METRICS&&niters==0){
              double w;
              w=r=d=0;
              while(n-->0){
                w+=m[n].w;
                r+=m[n].r;
                d+=m[n].d;
              }
              r=w>1E-3?r/w:0;
              d=w>1E-3?d/w:0;
              _weight[qi][pli][qti][si]=w;
            }
            else{
              /*Update the grid point and save the weight for later.*/
              _weight[qi][pli][qti][si]=
               oc_mode_metrics_solve(&r,&d,m,s0,s1,q0,q1,ra,rb,rc,da,db,dc,n);
            }
            rate=OC_CLAMPI(-32768,(int)(ldexp(r,OC_BIT_SCALE)+0.5),32767);
            rmse=OC_CLAMPI(-32768,(int)(ldexp(d,OC_RMSE_SCALE)+0.5),32767);
            dr+=abs(rate-_table[qi][pli][qti][si].rate);
            dd+=abs(rmse-_table[qi][pli][qti][si].rmse);
            _table[qi][pli][qti][si].rate=(ogg_int16_t)rate;
            _table[qi][pli][qti][si].rmse=(ogg_int16_t)rmse;
          }
        }
      }
    }
  }
  /*After a fixed number of initial iterations, only iterate so long as the
     total change is decreasing.
    This ensures we don't oscillate forever, which is a danger, as all of our
     results are rounded fairly coarsely.*/
  while((dr>0||dd>0)&&(niters++<_niters_min||(dr<prevdr&&dd<prevdd)));
  if(_reweight){
    /*Now, reduce the values of the optimal solution until we get enough
       samples in each bin to overcome the constant OC_ZWEIGHT factor.
      This encourages sampling under-populated bins and prevents a single large
       sample early on from discouraging coding in that bin ever again.*/
    for(pli=0;pli<3;pli++){
      for(qti=0;qti<2;qti++){
        for(qi=0;qi<OC_LOGQ_BINS;qi++){
          for(si=0;si<OC_COMP_BINS;si++){
            double wt;
            wt=_weight[qi][pli][qti][si];
            wt/=OC_ZWEIGHT+wt;
            _table[qi][pli][qti][si].rate=(ogg_int16_t)
             (_table[qi][pli][qti][si].rate*wt+0.5);
            _table[qi][pli][qti][si].rmse=(ogg_int16_t)
             (_table[qi][pli][qti][si].rmse*wt+0.5);
          }
        }
      }
    }
  }
}

/*Dump the in memory mode metrics to a file.
  Note this data format isn't portable between different platforms.*/
void oc_mode_metrics_dump(void){
  FILE *fmetrics;
  fmetrics=fopen(OC_MODE_METRICS_FILENAME,"wb");
  if(fmetrics!=NULL){
    (void)fwrite(OC_MODE_LOGQ,sizeof(OC_MODE_LOGQ),1,fmetrics);
    (void)fwrite(OC_MODE_METRICS_SATD,sizeof(OC_MODE_METRICS_SATD),1,fmetrics);
    (void)fwrite(OC_MODE_METRICS_SAD,sizeof(OC_MODE_METRICS_SAD),1,fmetrics);
    fclose(fmetrics);
  }
}

void oc_mode_metrics_print_rd(FILE *_fout,const char *_table_name,
#if !defined(OC_COLLECT_METRICS)
 const oc_mode_rd (*_mode_rd_table)[3][2][OC_COMP_BINS]){
#else
 oc_mode_rd (*_mode_rd_table)[3][2][OC_COMP_BINS]){
#endif
  int qii;
  fprintf(_fout,
   "# if !defined(OC_COLLECT_METRICS)\n"
   "static const\n"
   "# endif\n"
   "oc_mode_rd %s[OC_LOGQ_BINS][3][2][OC_COMP_BINS]={\n",_table_name);
  for(qii=0;qii<OC_LOGQ_BINS;qii++){
    int pli;
    fprintf(_fout,"  {\n");
    for(pli=0;pli<3;pli++){
      int qti;
      fprintf(_fout,"    {\n");
      for(qti=0;qti<2;qti++){
        int bin;
        int qi;
        static const char *pl_names[3]={"Y'","Cb","Cr"};
        static const char *qti_names[2]={"INTRA","INTER"};
        qi=(63*qii+(OC_LOGQ_BINS-1>>1))/(OC_LOGQ_BINS-1);
        fprintf(_fout,"      /*%s  qi=%i  %s*/\n",
         pl_names[pli],qi,qti_names[qti]);
        fprintf(_fout,"      {\n");
        fprintf(_fout,"        ");
        for(bin=0;bin<OC_COMP_BINS;bin++){
          if(bin&&!(bin&0x3))fprintf(_fout,"\n        ");
          fprintf(_fout,"{%5i,%5i}",
           _mode_rd_table[qii][pli][qti][bin].rate,
           _mode_rd_table[qii][pli][qti][bin].rmse);
          if(bin+1<OC_COMP_BINS)fprintf(_fout,",");
        }
        fprintf(_fout,"\n      }");
        if(qti<1)fprintf(_fout,",");
        fprintf(_fout,"\n");
      }
      fprintf(_fout,"    }");
      if(pli<2)fprintf(_fout,",");
      fprintf(_fout,"\n");
    }
    fprintf(_fout,"  }");
    if(qii+1<OC_LOGQ_BINS)fprintf(_fout,",");
    fprintf(_fout,"\n");
  }
  fprintf(_fout,
   "};\n"
   "\n");
}

void oc_mode_metrics_print(FILE *_fout){
  int qii;
  fprintf(_fout,
   "/*File generated by libtheora with OC_COLLECT_METRICS"
   " defined at compile time.*/\n"
   "#if !defined(_modedec_H)\n"
   "# define _modedec_H (1)\n"
   "# include \"encint.h\"\n"
   "\n"
   "\n"
   "\n"
   "/*The log of the average quantizer for each of the OC_MODE_RD table rows\n"
   "   (e.g., for the represented qi's, and each pli and qti), in Q10 format.\n"
   "  The actual statistics used by the encoder will be interpolated from\n"
   "   that table based on log_plq for the actual quantization matrix used.*/\n"
   "# if !defined(OC_COLLECT_METRICS)\n"
   "static const\n"
   "# endif\n"
   "ogg_int16_t OC_MODE_LOGQ[OC_LOGQ_BINS][3][2]={\n");
  for(qii=0;qii<OC_LOGQ_BINS;qii++){
    fprintf(_fout,"  { {0x%04X,0x%04X},{0x%04X,0x%04X},{0x%04X,0x%04X} }%s\n",
     OC_MODE_LOGQ[qii][0][0],OC_MODE_LOGQ[qii][0][1],OC_MODE_LOGQ[qii][1][0],
     OC_MODE_LOGQ[qii][1][1],OC_MODE_LOGQ[qii][2][0],OC_MODE_LOGQ[qii][2][1],
     qii+1<OC_LOGQ_BINS?",":"");
  }
  fprintf(_fout,
   "};\n"
   "\n");
  oc_mode_metrics_print_rd(_fout,"OC_MODE_RD_SATD",OC_MODE_RD_SATD);
  oc_mode_metrics_print_rd(_fout,"OC_MODE_RD_SAD",OC_MODE_RD_SAD);
  fprintf(_fout,
   "#endif\n");
}


# if !defined(OC_COLLECT_NO_ENC_FUNCS)
void oc_enc_mode_metrics_load(oc_enc_ctx *_enc){
  oc_restore_fpu(&_enc->state);
  /*Load any existing mode metrics if we haven't already.*/
  if(!OC_HAS_MODE_METRICS){
    FILE *fmetrics;
    memset(OC_MODE_METRICS_SATD,0,sizeof(OC_MODE_METRICS_SATD));
    memset(OC_MODE_METRICS_SAD,0,sizeof(OC_MODE_METRICS_SAD));
    fmetrics=fopen(OC_MODE_METRICS_FILENAME,"rb");
    if(fmetrics!=NULL){
      /*Read in the binary structures as written my oc_mode_metrics_dump().
        Note this format isn't portable between different platforms.*/
      (void)fread(OC_MODE_LOGQ,sizeof(OC_MODE_LOGQ),1,fmetrics);
      (void)fread(OC_MODE_METRICS_SATD,sizeof(OC_MODE_METRICS_SATD),1,fmetrics);
      (void)fread(OC_MODE_METRICS_SAD,sizeof(OC_MODE_METRICS_SAD),1,fmetrics);
      fclose(fmetrics);
    }
    else{
      int qii;
      int qi;
      int pli;
      int qti;
      for(qii=0;qii<OC_LOGQ_BINS;qii++){
        qi=(63*qii+(OC_LOGQ_BINS-1>>1))/(OC_LOGQ_BINS-1);
        for(pli=0;pli<3;pli++)for(qti=0;qti<2;qti++){
          OC_MODE_LOGQ[qii][pli][qti]=_enc->log_plq[qi][pli][qti];
        }
      }
    }
    oc_mode_metrics_update(OC_MODE_METRICS_SATD,100,1,
     OC_MODE_RD_SATD,OC_SATD_SHIFT,OC_MODE_RD_WEIGHT_SATD);
    oc_mode_metrics_update(OC_MODE_METRICS_SAD,100,1,
     OC_MODE_RD_SAD,OC_SAD_SHIFT,OC_MODE_RD_WEIGHT_SAD);
    OC_HAS_MODE_METRICS=1;
  }
}

/*The following token skipping code used to also be used in the decoder (and
   even at one point other places in the encoder).
  However, it was obsoleted by other optimizations, and is now only used here.
  It has been moved here to avoid generating the code when it's not needed.*/

/*Determines the number of blocks or coefficients to be skipped for a given
   token value.
  _token:      The token value to skip.
  _extra_bits: The extra bits attached to this token.
  Return: A positive value indicates that number of coefficients are to be
           skipped in the current block.
          Otherwise, the negative of the return value indicates that number of
           blocks are to be ended.*/
typedef ptrdiff_t (*oc_token_skip_func)(int _token,int _extra_bits);

/*Handles the simple end of block tokens.*/
static ptrdiff_t oc_token_skip_eob(int _token,int _extra_bits){
  int nblocks_adjust;
  nblocks_adjust=OC_UNIBBLE_TABLE32(0,1,2,3,7,15,0,0,_token)+1;
  return -_extra_bits-nblocks_adjust;
}

/*The last EOB token has a special case, where an EOB run of size zero ends all
   the remaining blocks in the frame.*/
static ptrdiff_t oc_token_skip_eob6(int _token,int _extra_bits){
  /*Note: We want to return -PTRDIFF_MAX, but that requires C99, which is not
     yet available everywhere; this should be equivalent.*/
  if(!_extra_bits)return -(~(size_t)0>>1);
  return -_extra_bits;
}

/*Handles the pure zero run tokens.*/
static ptrdiff_t oc_token_skip_zrl(int _token,int _extra_bits){
  return _extra_bits+1;
}

/*Handles a normal coefficient value token.*/
static ptrdiff_t oc_token_skip_val(void){
  return 1;
}

/*Handles a category 1A zero run/coefficient value combo token.*/
static ptrdiff_t oc_token_skip_run_cat1a(int _token){
  return _token-OC_DCT_RUN_CAT1A+2;
}

/*Handles category 1b, 1c, 2a, and 2b zero run/coefficient value combo tokens.*/
static ptrdiff_t oc_token_skip_run(int _token,int _extra_bits){
  int run_cati;
  int ncoeffs_mask;
  int ncoeffs_adjust;
  run_cati=_token-OC_DCT_RUN_CAT1B;
  ncoeffs_mask=OC_BYTE_TABLE32(3,7,0,1,run_cati);
  ncoeffs_adjust=OC_BYTE_TABLE32(7,11,2,3,run_cati);
  return (_extra_bits&ncoeffs_mask)+ncoeffs_adjust;
}

/*A jump table for computing the number of coefficients or blocks to skip for
   a given token value.
  This reduces all the conditional branches, etc., needed to parse these token
   values down to one indirect jump.*/
static const oc_token_skip_func OC_TOKEN_SKIP_TABLE[TH_NDCT_TOKENS]={
  oc_token_skip_eob,
  oc_token_skip_eob,
  oc_token_skip_eob,
  oc_token_skip_eob,
  oc_token_skip_eob,
  oc_token_skip_eob,
  oc_token_skip_eob6,
  oc_token_skip_zrl,
  oc_token_skip_zrl,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_val,
  (oc_token_skip_func)oc_token_skip_run_cat1a,
  (oc_token_skip_func)oc_token_skip_run_cat1a,
  (oc_token_skip_func)oc_token_skip_run_cat1a,
  (oc_token_skip_func)oc_token_skip_run_cat1a,
  (oc_token_skip_func)oc_token_skip_run_cat1a,
  oc_token_skip_run,
  oc_token_skip_run,
  oc_token_skip_run,
  oc_token_skip_run
};

/*Determines the number of blocks or coefficients to be skipped for a given
   token value.
  _token:      The token value to skip.
  _extra_bits: The extra bits attached to this token.
  Return: A positive value indicates that number of coefficients are to be
           skipped in the current block.
          Otherwise, the negative of the return value indicates that number of
           blocks are to be ended.
          0 will never be returned, so that at least one coefficient in one
           block will always be decoded for every token.*/
static ptrdiff_t oc_dct_token_skip(int _token,int _extra_bits){
  return (*OC_TOKEN_SKIP_TABLE[_token])(_token,_extra_bits);
}


void oc_enc_mode_metrics_collect(oc_enc_ctx *_enc){
  static const unsigned char OC_ZZI_HUFF_OFFSET[64]={
     0,16,16,16,16,16,32,32,
    32,32,32,32,32,32,32,48,
    48,48,48,48,48,48,48,48,
    48,48,48,48,64,64,64,64,
    64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64
  };
  const oc_fragment *frags;
  const unsigned    *frag_sad;
  const unsigned    *frag_satd;
  const unsigned    *frag_ssd;
  const ptrdiff_t   *coded_fragis;
  ptrdiff_t          ncoded_fragis;
  ptrdiff_t          fragii;
  double             fragw;
  int                modelines[3][3][2];
  int                qti;
  int                qii;
  int                qi;
  int                pli;
  int                zzi;
  int                token;
  int                eb;
  oc_restore_fpu(&_enc->state);
  /*Figure out which metric bins to use for this frame's quantizers.*/
  for(qii=0;qii<_enc->state.nqis;qii++){
    for(pli=0;pli<3;pli++){
      for(qti=0;qti<2;qti++){
        int log_plq;
        int modeline;
        log_plq=_enc->log_plq[_enc->state.qis[qii]][pli][qti];
        for(modeline=0;modeline<OC_LOGQ_BINS-1&&
         OC_MODE_LOGQ[modeline+1][pli][qti]>log_plq;modeline++);
        modelines[qii][pli][qti]=modeline;
      }
    }
  }
  qti=_enc->state.frame_type;
  frags=_enc->state.frags;
  frag_sad=_enc->frag_sad;
  frag_satd=_enc->frag_satd;
  frag_ssd=_enc->frag_ssd;
  coded_fragis=_enc->state.coded_fragis;
  ncoded_fragis=fragii=0;
  /*Weight the fragments by the inverse frame size; this prevents HD content
     from dominating the statistics.*/
  fragw=1.0/_enc->state.nfrags;
  for(pli=0;pli<3;pli++){
    ptrdiff_t ti[64];
    int       eob_token[64];
    int       eob_run[64];
    /*Set up token indices and eob run counts.
      We don't bother trying to figure out the real cost of the runs that span
       coefficients; instead we use the costs that were available when R-D
       token optimization was done.*/
    for(zzi=0;zzi<64;zzi++){
      ti[zzi]=_enc->dct_token_offs[pli][zzi];
      if(ti[zzi]>0){
        token=_enc->dct_tokens[pli][zzi][0];
        eb=_enc->extra_bits[pli][zzi][0];
        eob_token[zzi]=token;
        eob_run[zzi]=-oc_dct_token_skip(token,eb);
      }
      else{
        eob_token[zzi]=OC_NDCT_EOB_TOKEN_MAX;
        eob_run[zzi]=0;
      }
    }
    /*Scan the list of coded fragments for this plane.*/
    ncoded_fragis+=_enc->state.ncoded_fragis[pli];
    for(;fragii<ncoded_fragis;fragii++){
      ptrdiff_t fragi;
      int       frag_bits;
      int       huffi;
      int       skip;
      int       mb_mode;
      unsigned  sad;
      unsigned  satd;
      double    sqrt_ssd;
      int       bin;
      int       qtj;
      fragi=coded_fragis[fragii];
      frag_bits=0;
      for(zzi=0;zzi<64;){
        if(eob_run[zzi]>0){
          /*We've reached the end of the block.*/
          eob_run[zzi]--;
          break;
        }
        huffi=_enc->huff_idxs[qti][zzi>0][pli+1>>1]
         +OC_ZZI_HUFF_OFFSET[zzi];
        if(eob_token[zzi]<OC_NDCT_EOB_TOKEN_MAX){
          /*This token caused an EOB run to be flushed.
            Therefore it gets the bits associated with it.*/
          frag_bits+=_enc->huff_codes[huffi][eob_token[zzi]].nbits
           +OC_DCT_TOKEN_EXTRA_BITS[eob_token[zzi]];
          eob_token[zzi]=OC_NDCT_EOB_TOKEN_MAX;
        }
        token=_enc->dct_tokens[pli][zzi][ti[zzi]];
        eb=_enc->extra_bits[pli][zzi][ti[zzi]];
        ti[zzi]++;
        skip=oc_dct_token_skip(token,eb);
        if(skip<0){
          eob_token[zzi]=token;
          eob_run[zzi]=-skip;
        }
        else{
          /*A regular DCT value token; accumulate the bits for it.*/
          frag_bits+=_enc->huff_codes[huffi][token].nbits
           +OC_DCT_TOKEN_EXTRA_BITS[token];
          zzi+=skip;
        }
      }
      mb_mode=frags[fragi].mb_mode;
      qii=frags[fragi].qii;
      qi=_enc->state.qis[qii];
      sad=frag_sad[fragi]<<(pli+1&2);
      satd=frag_satd[fragi]<<(pli+1&2);
      sqrt_ssd=sqrt(frag_ssd[fragi]);
      qtj=mb_mode!=OC_MODE_INTRA;
      /*Accumulate statistics.
        The rate (frag_bits) and RMSE (sqrt(frag_ssd)) are not scaled by
         OC_BIT_SCALE and OC_RMSE_SCALE; this lets us change the scale factor
         yet still use old data.*/
      bin=OC_MINI(satd>>OC_SATD_SHIFT,OC_COMP_BINS-1);
      oc_mode_metrics_add(
       OC_MODE_METRICS_SATD[modelines[qii][pli][qtj]][pli][qtj]+bin,
       fragw,satd,_enc->log_plq[qi][pli][qtj],frag_bits,sqrt_ssd);
      bin=OC_MINI(sad>>OC_SAD_SHIFT,OC_COMP_BINS-1);
      oc_mode_metrics_add(
       OC_MODE_METRICS_SAD[modelines[qii][pli][qtj]][pli][qtj]+bin,
       fragw,sad,_enc->log_plq[qi][pli][qtj],frag_bits,sqrt_ssd);
    }
  }
  /*Update global SA(T)D/logq/rate/RMSE estimation matrix.*/
  oc_mode_metrics_update(OC_MODE_METRICS_SATD,4,1,
   OC_MODE_RD_SATD,OC_SATD_SHIFT,OC_MODE_RD_WEIGHT_SATD);
  oc_mode_metrics_update(OC_MODE_METRICS_SAD,4,1,
   OC_MODE_RD_SAD,OC_SAD_SHIFT,OC_MODE_RD_WEIGHT_SAD);
}
# endif

#endif
