//---------------------------------------------------------------------------
//  Represent bits states
//---------------------------------------------------------------------------
#ifdef __BORLANDC__
#include <vcl.h>
#pragma hdrstop
#endif //__BORLANDC__

#include <iostream>
#include <fstream>
#include <string>
#include <new>
#include <cstdlib>
#include <math.h>
#include "QBits.h"

using namespace std;

//---------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma package(smart_init)
#endif //__BORLANDC__
//---------------------------------------------------------------------------
/**
 *  operator<<
 */
std::ostream &
operator<<(std::ostream &os, const QBits &qb) {

    std::string s1;
    for (int i = 0; i < qb.mNumberOfStates; i++) {
        s1 = "";
        for (int j = 0; j < qb.mNumberOfQbits; j++) {
            if ((i >> j) & 1) {
                s1 = "1" + s1;
            } else {
                s1 = "0" + s1;
            }
        }
        s1 = "|" + s1 + "> = ";
        os << s1 << qb.mBitsR[i]
        << " + " << qb.mBitsI[i]
        << "i\n";
    }
    return os;
}
//---------------------------------------------------------------------------
/**
 *  operator<<
 */
std::ostream
&operator<<(std::ostream &os, const QBits *qb) {
    return (os << *qb);
}
//---------------------------------------------------------------------------
/**
 *  Constructor
 */
QBits::QBits(char * filename) {
    LoadFromFile(filename);
}
//---------------------------------------------------------------------------
/**
 *  Constructor
 */
QBits::QBits(void) {
    mBitsR = NULL;
    mBitsI = NULL;
    mBitsM = NULL;
    mMeasured =NULL;
    bValid = false;
}
//---------------------------------------------------------------------------
/**
 *  Constructor
 */
QBits::QBits(const int n) {
    mBitsR = NULL;
    mBitsI = NULL;
    mBitsM = NULL;
    mMeasured =NULL;
    bValid = false;

    Allocate(n);
}
//---------------------------------------------------------------------------
/**
 *  Copy constructor
 */
QBits::QBits(const QBits &qubits)
{
    mNumberOfQbits  = qubits.mNumberOfQbits;
    mNumberOfStates = qubits.mNumberOfStates;
    try {
        mBitsR = new double [mNumberOfStates];
        mBitsI = new double [mNumberOfStates];
        mMeasured = new bool [mNumberOfQbits];
        mBitsM = new double[GetNumberOfMeasured()];
    } catch (std::bad_alloc ex) {
#ifdef __BORLANDC__
        ShowMessage("QBits: fatal error. Out of Memory.");
#endif //__BORLANDC__
        std::exit(1);
    }

    for (int i = 0; i < mNumberOfStates; i++){
        mBitsR[i] = qubits.mBitsR[i];
        mBitsI[i] = qubits.mBitsI[i];
    }

    for (int i=0;i<mNumberOfQbits;i++){
        mMeasured[i] = qubits.mMeasured[i];
        mBitsM[i] = qubits.mBitsM[i];
    }

}
//---------------------------------------------------------------------------
/**
 *  Destructor
 */
QBits::~QBits() {
    if (mBitsR != NULL) delete [] mBitsR;
    if (mBitsI != NULL) delete [] mBitsI;
    if (mBitsM != NULL) delete [] mBitsM;
    if (mMeasured !=NULL) delete [] mMeasured;
}
//---------------------------------------------------------------------------
bool
QBits::Allocate(int n) {
    mNumberOfQbits  = n;
    mNumberOfStates = 1 << n;

    if (bValid) {
        if (mBitsR != NULL) delete [] mBitsR;
        if (mBitsI != NULL) delete [] mBitsI;
        if (mMeasured !=NULL) delete [] mMeasured;
        if (mBitsM != NULL) delete [] mBitsM;
    }

    bValid = false;
    try {
        mBitsR = new double [mNumberOfStates];
        mBitsI = new double [mNumberOfStates];
        mMeasured = new bool [mNumberOfQbits];
        mBitsM = new double[GetNumberOfMeasured()];
    } catch (std::bad_alloc ex) {
#ifdef __BORLANDC__
        ShowMessage("Error: Out of Memory.");
#endif //__BORLANDC__
        if (mBitsR != NULL) delete [] mBitsR;
        if (mBitsI != NULL) delete [] mBitsI;
        if (mMeasured !=NULL) delete [] mMeasured;
        if (mBitsM != NULL) delete [] mBitsM;
        return false;
    }
    bValid = true;

    Init();
    return true;
}
//---------------------------------------------------------------------------
/**
 * Initialize
 */
void
QBits::Init(void) {
    for (int i = 0; i < mNumberOfStates; i++){
        mBitsR[i] = 0.0;
        mBitsI[i] = 0.0;
    }
    mBitsR[0] = 1.0;
    for (int i = 0; i < mNumberOfQbits; i++){
        mMeasured[i] = false;
    }
}
//---------------------------------------------------------------------------
/**
 *  Get real part of nth state as reference
 */
double &
QBits::NthStateR(const int nth) const {
    if (nth < 0 || mNumberOfStates <= nth) {
#ifdef __BORLANDC__
        ShowMessage("QBits: fatal error");
#endif //__BORLANDC__
        std::exit(1);
    }
    return mBitsR[nth];
}
//---------------------------------------------------------------------------
/**
 *  Get imaginary part of nth state as reference
 */
double &
QBits::NthStateI(const int nth) const {
    if (nth < 0 || mNumberOfStates <= nth) {
#ifdef __BORLANDC__
        ShowMessage("QBits: fatal error");
#endif //__BORLANDC__
        std::exit(1);
    }
    return mBitsI[nth];
}
//---------------------------------------------------------------------------
/**
 * Return Absolute Value of index state
 */
double
QBits::GetAbsoluteValue(int index){
    double re = NthStateR(index);
    double im = NthStateI(index);
    return re*re+im*im;
}
//---------------------------------------------------------------------------
// Measurement
//---------------------------------------------------------------------------
int
QBits::GetNumberOfMeasured(void){
    int n = 0;
    for (int i=0;i<GetNumberOfQBits();i++){
        if (isMeasured(i)) {
            n++;
        }
    }
    return n;
}
//---------------------------------------------------------------------------
double
QBits::GetMeasuredValue(int index){
    return mBitsM[index];
}
//---------------------------------------------------------------------------
string
QBits::GetMeasuredString(int index){

    string s;
    int t = 1;
    for (int j=0;j<GetNumberOfQBits();j++){
        if (isMeasured(j)){
            if ( (index >> (t-1) )&1){
                s = "1" + s;
            }else{
                s = "0" + s;
            }
            t++;
        }else{
            s = "?" + s;
        }
    }
    s = "|" + s + ">";
    return s;
}
//---------------------------------------------------------------------------
/**
 *  Perform Measurement
 */
void
QBits::PerformMeasurement(void){
    if (mBitsM !=NULL){
        delete [] mBitsM;
    }

    int n = 1 << GetNumberOfMeasured();

    try{
        mBitsM = new double[n];
    }catch (bad_alloc ex){
        ShowMessage("QBits: fatal error. Out of Memory.");
        return;
    }

    for (int i=0;i<n;i++){
        mBitsM[i] = 0;
    }

    for (int i=0;i< GetNumberOfStates();i++){
        int index = 0;
        int t = 1;
        for (int j=0;j<GetNumberOfQBits();j++){
            if (isMeasured(j)){
                index += (((i>>j)&1)<<(t-1));
                t++;
            }
        }
        mBitsM[index] += GetAbsoluteValue(i);
    }
}
//---------------------------------------------------------------------------
#ifdef __BIG_ENDIAN__
#include "swap_endian.h"
#endif //__BIG_ENDIAN__
//---------------------------------------------------------------------------
/**
 * Load data From File
 */
void QBits::LoadFromFile(const char *filename) {
    QResultInfo ri;
    FILE *fp = fopen(filename,"rb");

#ifdef __BIG_ENDIAN__
    char    cbuf[8];
    int     dummyInt;
    fread(&ri.header, sizeof(char), 4, fp);
    fread(&ri.filename, sizeof(char), 4, fp);
    fread(&dummyInt, sizeof(int), 1, fp);
    qutil::swap_endian(&dummyInt, cbuf);
    ri.bitnumber = *(int*)cbuf;
#else
    fread(&ri,sizeof(ri),1,fp);
#endif //__BIG_ENDIAN__

    mNumberOfQbits  = ri.bitnumber;
    mNumberOfStates = 1 << ri.bitnumber;

    try {
        mBitsR = new double [mNumberOfStates];
        mBitsI = new double [mNumberOfStates];
    } catch (std::bad_alloc ex) {

#ifdef __BORLANDC__
        ShowMessage("QBits: fatal error");
#endif //__BORLANDC__

        std::exit(1);
        fclose(fp);
    }

#ifdef __BIG_ENDIAN__
    double  dummyDouble;
    for (int i = 0; i < mNumberOfStates; i++)
    {
        fread(&dummyDouble, sizeof(double), 1, fp);
        qutil::swap_endian(&dummyDouble, cbuf);
        mBitsR[i] = *(double*)cbuf;
    }
    for (int i = 0; i < mNumberOfStates; i++)
    {
        fread(&dummyDouble, sizeof(double), 1, fp);
        qutil::swap_endian(&dummyDouble, cbuf);
        mBitsI[i] = *(double*)cbuf;
    }
#else
    fread(mBitsR,sizeof(double),mNumberOfStates,fp);
    fread(mBitsI,sizeof(double),mNumberOfStates,fp);
#endif //__BIG_ENDIAN__

    fclose(fp);
}
//---------------------------------------------------------------------------
/**
 * Save data to File
 */
void
QBits::SaveToFile(const char *filename) {
    QResultInfo ri;
    ri.bitnumber = GetNumberOfQBits();
    strcpy(ri.filename,filename);

    int datasize = 1<<GetNumberOfQBits();
    double *R = GetBitsR();
    double *I = GetBitsI();

    FILE *fp = fopen(filename,"wb");

#ifdef __BIG_ENDIAN__
    char cbuf[8];
    fwrite(ri.header, sizeof(char), 4, fp);
    fwrite(ri.filename, sizeof(char), 256, fp);
    qutil::swap_endian(&ri.bitnumber, cbuf);
    fwrite(cbuf, sizeof(int), 1, fp);
    for (int i = 0; i < datasize; i++) {
        qutil::swap_endian(&R[i], cbuf);
        fwrite(cbuf, sizeof(double), 1, fp);
    }
    for (int i = 0; i < datasize; i++) {
        qutil::swap_endian(&I[i], cbuf);
        fwrite(cbuf, sizeof(double), 1, fp);
    }
#else
    fwrite(&ri,sizeof(ri),1,fp);
    fwrite(R,sizeof(double),datasize,fp);
    fwrite(I,sizeof(double),datasize,fp);
#endif //__BIG_ENDIAN__
    fclose(fp);
}
//---------------------------------------------------------------------------
/**
 * Save data as  Text File
 */
void
QBits::SaveAsText(const char *filename) {

    ofstream ofs;
    ofs.open(filename);
    ofs << "#QCAD Result file" << endl;
    ofs << "#ex) Index), (Qubits conf.), (Abs. value), (Re. part), (Im. part)" << endl;
    ofs << "#ex)      0, |00000>       , 1.000       , 0.4455    , 0.23      " << endl;
    SaveToStream(ofs);
    ofs.close();
}
//---------------------------------------------------------------------------
void
QBits::SaveMeasurementToStream(ostream &os){
    int n = 1 << GetNumberOfMeasured();
    for (int i=0;i<n;i++){
        os << i;
        os << ",  ";
        os << GetMeasuredString(i);
        os << ",  ";
        os.setf(ios::fixed | ios::floatfield);
        os.precision(8);
        os.setf(ios::showpoint);
        os << GetMeasuredValue(i) << endl;
    }
}
//---------------------------------------------------------------------------
/**
 * Save data as  Text File
 */
void
QBits::SaveToStream(ostream &ss){
    double *R = GetBitsR();
    double *I = GetBitsI();

    for (int i=0;i< GetNumberOfStates();i++){
        string s;
        for (int j=0;j<GetNumberOfQBits();j++){
            if ((i>>j)&1) {
                s = "1" + s;
            } else {
                s = "0" + s;
            }
        }
        s = "|" + s + ">";
        double a = sqrt(R[i]*R[i]+I[i]*I[i]); //abs. value
        ss << i << "," << s << ",";
        ss.width(10);
        ss.setf(ios::fixed, ios::floatfield);
        ss.setf(ios::showpoint);
        ss << a << "," << R[i] << "," << I[i] << endl;
    }
}
//---------------------------------------------------------------------------

