//
// Created by joe on 4/12/15.
//

#include "rankaccu.h"

using namespace std;

const double offset = 1;

void ranksort(int l,int r,vector<int> &rank,const vector<double> &ref1,const vector<double> &ref2)
{
    int i=l,j=r,k;
    double mid1=ref1[rank[(l+r)>>1]],mid2=ref2[rank[(l+r)>>1]];
    while (i<=j)
    {
        while (ref1[rank[i]]>mid1 || (ref1[rank[i]]==mid1 && ref2[rank[i]]>mid2)) ++i;
        while (ref1[rank[j]]<mid1 || (ref1[rank[j]]==mid1 && ref2[rank[j]]<mid2)) --j;
        if (i<=j)
        {
            k=rank[i];
            rank[i]=rank[j];
            rank[j]=k;
            ++i;
            --j;
        }
    }
    if (j>l)
        ranksort(l,j,rank,ref1,ref2);
    if (i<r)
        ranksort(i,r,rank,ref1,ref2);
}

void rankmerge(int l,int r,vector<int> &C,vector<int> &rank,const vector<double> &ref1,const vector<double> &ref2)
{
    if (l==r)
        return;
    int i=l,j=((l+r)>>1)+1;
    rankmerge(i,j-1,C,rank,ref1,ref2);
    rankmerge(j,r,C,rank,ref1,ref2);
    vector<int> stage_r((unsigned long)r-l+1),stage_c((unsigned long)r-l+1);
    int cnt=0;
    int k=0;
    while (i<=((l+r)>>1) && j<=r)
    {
        if (ref1[rank[i]]>ref1[rank[j]] || (ref1[rank[i]]==ref1[rank[j]] && ref2[rank[i]]>ref2[rank[j]])) {
            stage_c[k] = C[i];
            stage_r[k++] = rank[i++];
            ++cnt;
        }
        else{
            stage_c[k] = C[j]+cnt;
            stage_r[k++] = rank[j++];
        }
    }
    while (i<=((l+r)>>1))
    {
        stage_c[k] = C[i];
        stage_r[k++] = rank[i++];
    }
    while (j<=r)
    {
        stage_c[k] = C[j]+cnt;
        stage_r[k++] = rank[j++];
    }
    for (i=l;i<=r;++i)
    {
        C[i]=stage_c[i-l];
        rank[i]=stage_r[i-l];
    }
}

void rank_accu(RidList &D,const vector<double> pred)
{
    unsigned long n = D.getSize();
    vector<int> orig_rank(n),pred_rank(n),C(n);
    vector<double> orig(n);
    int i,j;
    for (i=0;i<D.getSize();++i)
    {
        orig_rank[i]=i;
        pred_rank[i]=i;
        orig[i]=D.getL(i);
    }
    double accu_nDCG=0;
    double accu_AP=0;
    for (j=0; j<D.getSize();j+=D.getqSize())
    {
        i=j+D.getqSize()-1;
        double Y=0,Z=0;
        double AP=0;
        ranksort(j,i,orig_rank,orig,pred);
        ranksort(j,i,pred_rank,pred,orig);
        for (int k = j;k<=i;++k)
        {
            Z += (pow(2,offset+orig[orig_rank[k]]) - 1)/log2(2+k-j);
            Y += (pow(2,offset+orig[pred_rank[k]]) - 1)/log2(2+k-j);
        }
        accu_nDCG+=Y/Z;
        rankmerge(j,i,C,orig_rank,pred,orig);
        for (int k = j+1;k<=i;++k)
            AP += ((double)C[k])/(k-j);
        AP=AP*2/(i-j)-1;
        accu_AP+=AP;
        LOG(INFO)<<"qid:"<<D.getQid(j)<<"; nDCG:"<<Y/Z<<"; AP:"<< AP;
    }
    LOG(INFO)<<"over "<< D.getuSize()<< " queries. "<<"Average nDGC: "<< accu_nDCG/D.getuSize()<< " Average AP: "<<accu_AP/D.getuSize();
}

void rank_CMC(RidList &D,const std::vector<double> pred,CMC & cmc) {
    unsigned long n = D.getSize();
    vector<int> pred_rank(n);
    vector<double> orig(n);
    int i,j;
    for (i=0;i<D.getSize();++i)
    {
        pred_rank[i]=i;
        orig[i]=D.getL(i);
    }
    for (j=0; j<D.getSize();j+=D.getqSize())
    {
        i=j+D.getqSize()-1;
        ranksort(j,i,pred_rank,pred,orig);
        for (int k=j;k<=i;++k)
            if (orig[pred_rank[k]]>0)
            {
                LOG(INFO)<<"qid:"<<D.getQid(j)<<"; pred:"<<pred[k]<<"; rank:"<< k-j;
                cmc.addEntry(k-j);
                break; // account only for the first match;
            }
        LOG(INFO)<<"top: "<< D.getO(pred_rank[j]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+1]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+2]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+3]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+4]%D.getqSize())->qid
                 <<" "<< D.getO(pred_rank[j+5]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+6]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+7]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+8]%D.getqSize())->qid <<" "<<D.getO(pred_rank[j+9]%D.getqSize())->qid;
    }
}

void rank_pair(RidList &D,const vector<double> pred,vector<double> &pair)
{
    int n =D.getSize(),q=D.getqSize();
    pair.clear();
    for (int i=0;i<n;i+=q)
    {
        int corr=0;
        for (int j=0;j<q;++j)
            if (D.getL(i+j)>0) {
                corr = j;
                break;
            }

        for (int j=0;j<q;++j)
            if (j!=corr)
                pair.push_back(pred[i+corr]-pred[i+j]);
    }
}