#include <iostream>
#include <fstream>
#include <vector>
#include <set>
#include <stdlib.h>
#include <time.h>
#include "transformationmatrix.txt"
#define cubedatatypesize 35
#define prevmoveindex (22+depth)
#define break_on_find 1

using namespace std;

class cube{
    public:
        cube(){
            char penguin[]="ppppwwwwbbbbyyyyoooorrrr";
            for (int i=0;i<24;i++)
                c[i]=penguin[i];
            c[24]=9;
        }
        cube(char n[]){
            for (int i=0;i<cubedatatypesize;i++)
                c[i]=n[i];
        }
        cube(const cube &x, char i,char depth){
            char* z=M+i*24;
            int p=0;
            for (;p<24;p++)
                c[p]=x.c[z[p]];
            for (;p<23+depth;p++)
                c[p]=x.c[p];
            c[p++]=i;
            c[p]=9;
        }
        cube(int numtwists){//random cube constructor
            char penguin[]="ppppwwwwbbbbyyyyoooorrrr";
            for (int i=0;i<24;i++)
                c[i]=penguin[i];
            c[24]=9;
            for (int i=1;i<=numtwists;i++)
                randtwist(i);
        }
        int findlmi(){
            int lmi=24;
            for (;c[lmi]!=9 and lmi<cubedatatypesize;lmi++);
            return lmi-1;
        }
        void randtwist(int depth){//Add random twist
            int m;
            if (depth==1)
                m=rand()%9;
            else{
                int lmr=c[depth+22]/3;
                m=rand()%6;
                if (lmr==0) m+=3;
                else if (lmr==1 and m>2) m+=3;
            }
            cube b(*this,m,depth);
            int i=0;
            for (;i<24+depth and i<cubedatatypesize;i++)
                c[i]=b.c[i];
            if (i!=cubedatatypesize) c[i]=9;
        }
        void twistchain(char* n){
            int i=0;
            for (;n[i]!=0;i++);
            cout<<i<<endl;
            for (int j=1;j<=i;j++){
                cube b(*this,n[j-1]-48,j);
                int k=0;
                for (;b.c[k]!=9 and k<cubedatatypesize;k++)
                    c[k]=b.c[k];
                if (k!=cubedatatypesize) c[k]=9;
            }
        }
        cube& operator=(const cube &rhs){
            for (int i=0;i<cubedatatypesize;i++)
                c[i]=rhs.c[i];
                return *this;
        }
        bool operator<(const cube &rhs) const{
            for (int i=0;i<24;i++)
                if (c[i]==rhs.c[i]) continue;
                else return(c[i]<rhs.c[i]);
            return 0;
        }
        bool operator==(cube &rhs){
            for (int i=0;i<24;i++)
                if (c[i]!=rhs.c[i])    return 0;
            return 1;
        }
        void print(){    print(findlmi()-23);    }
        
        void printmovelist(int depth){
            char m[]="R  R2 R' U  U2 U' F  F2 F' ";
            for (int i=24;i<24+depth;i++)
                cout<<m[3*c[i]]<<m[3*c[i]+1]<<m[3*c[i]+2];
            cout<<endl;
        }
        void print(int depth){
            for (int i=0;i<24;i++)
                cout<<c[i];
            cout<<":  ";
            printmovelist(depth);
        }
        void write(int depth, ofstream &out){
            for (int i=0;i<24;i++)
                out<<c[i];
            out<<"  ";
            char m[]="R  R2 R' U  U2 U' F  F2 F' ";
            for (int i=24;i<24+depth;i++)
                out<<m[3*c[i]]<<m[3*c[i]+1]<<m[3*c[i]+2];
            out<<endl;
        }
            
        char c[cubedatatypesize];
};
//int expand(cube &x, vector<cube> &bottom,int depth, cube &scrambled,vector<cube> &sol){
////    cout<<"expandc";
//    int skip=x.c[22+depth]-x.c[22+depth]%3;
//    int i=0;
//    for (;i<skip;i++){
//        bottom.push_back(cube(x,i,depth));
//        if (bottom.back()==scrambled)    {/*bottom.back().print(depth);*/sol.push_back(bottom.back());return 1;}
//    }
//    for (i+=3;i<9;i++){
//        bottom.push_back(cube(x,i,depth));
//        if (bottom.back()==scrambled)    {/*bottom.back().print(depth);*/sol.push_back(bottom.back());return 1;}
//    }
//    return 0;
//}
//int expandvector(vector<cube> &top,vector<cube> &bottom,int depth, cube &scrambled,vector<cube> &sol){
////    cout<<"expandv top size="<<top.size()<<endl;
//    int rv=0;
//    for (vector<cube>::iterator i=top.begin();i<top.end();i++){
//        rv=expand(*i,bottom,depth, scrambled,sol);
//        if (rv) {/*cout<<"rv="<<rv; */break;}
//    }
//    return rv;
//}
//int expandvectormemorymanager(vector<cube>* &vp1,int depthlevelstart,int depthlevelend,cube &scrambled,vector<cube> &sol){
//        int rv=0;
//        for (int depth=depthlevelstart;depth<=depthlevelend;depth++){
////            cout<<"depth:"<<depth<<" depthlevelend:"<<depthlevelend<<endl;
//            vector<cube>* vp2 = new vector<cube>;
//            rv=expandvector(*vp1,*vp2,depth,scrambled,sol);//dereference then pass by reference LOL
//            delete vp1;
//            vp1=vp2;
//            if (rv) { break;}
//        }
//        return rv;
//}
//vector<cube>* exploretodepth(cube &start, int depthlevelstart, int depthlevelend, cube &scrambled,vector<cube> &sol){
////    cout<<"dlstart:"<<depthlevelstart<<" dlend"<<depthlevelend<<endl;
//    vector<cube>* vp1;
//    int rv=0;
//    if (depthlevelstart==1){//Can go 9 levels with 2 gigs of ram    takes 14.79s
//        vp1 = new vector<cube>;
//        for (int i=0;i<9;i++)    {vp1->push_back(cube(start,i,1));/*cout<<"pushback";*/}
//        rv=expandvectormemorymanager(vp1,2,depthlevelend,scrambled,sol);
////        cout<<"rv="<<rv;
//    }
//    else{
//        vp1 = new vector<cube>;
//        vp1->push_back(start);
//        rv=expandvectormemorymanager(vp1,depthlevelstart,depthlevelend,scrambled,sol);
////        cout<<"rv="<<rv;
//    }
//    return vp1;
//}
//void breadththendepthsearch(cube &start, cube &scrambled,vector<cube> &sol){
//    //This function is used to cut the tree to a managable size.
//    // It does of course create redundant cubes. (Maybe not anymore, since I changed leveljump to 9.)
//    // Also this function assumes that no solutions were found leveljump deep.
////    cout<<"btdsearch"<<endl;
//    const int leveljump=9;
//    vector<cube>* d3 = exploretodepth(start,1,leveljump,scrambled,sol);
//    if (!sol.empty()) {delete d3;return;}
//    vector<cube>* tempcubevec;
////    cout<<"d3->size()"<<d3->size()<<endl;
//    for (vector<cube>::iterator i=d3->begin();i<d3->end();i++){
//        tempcubevec=exploretodepth(*i,leveljump+1,maxdepth,scrambled,sol);
//        delete tempcubevec;
//        if (!sol.empty()) break;
//    }
//    delete d3;
//}

//int solve(){
//    cube eleven2("bypprpwbwrobbwyowoopyrry");
//    vector<cube> sol;
//    cube start;
//    breadththendepthsearch(start,eleven2,sol);
//    cout<<sol.size();
//    if (!sol.empty()) sol[0].print();
//}



bool isuniqueset(cube &c, vector < set<cube> > &pyr, int highestindex){
    for (int i=0;i<=highestindex;i++){
        if (pyr[i].count(c)!=0) return 0;
    }
    return 1;
}

vector< set<cube> > buildpyramidset(int maxdepth){
//    ofstream fileout;
//    fileout.open("pyramidset6.txt");
    cube start;
    vector < set<cube> > pyr;
    for (int i=0;i<=maxdepth;i++)
        pyr.push_back(set<cube>());
    int depth=0;
    pyr[depth].insert(cube());
//    start.write(0,fileout);
    
    depth=1;
    for (int i=0;i<9;i++){
        pyr[depth].insert(cube(start,i,depth));
//        cube(start,i,depth).write(1,fileout);
    }
    
    depth = 2;
    for (;depth<=maxdepth;depth++){
//        cout<<"starting level "<<depth<<" .";
//        int counter=0;
        for (set<cube>::iterator i=pyr[depth-1].begin(); i!=pyr[depth-1].end(); i++){
            int skip = (*i).c[22+depth]-(*i).c[22+depth]%3;
            int j=0;
            for (;j<skip;j++){
                cube b(*i,j,depth);
                if (isuniqueset(b,pyr,depth))    {pyr[depth].insert(b); 
        //                b.write(depth,fileout);
                }
            }
            for (j+=3;j<9;j++){
                cube b(*i,j,depth);
                if (isuniqueset(b,pyr,depth))    {pyr[depth].insert(b); 
        //                b.write(depth,fileout);
                }
            }
        cout<<"\ndepth "<<depth<<" has "<<pyr[depth].size()<<" elements\n";
        }
    }
    return pyr;
}

int main(){
    vector<set <cube> > pyr = buildpyramidset(11);
    int total=0;
    for (int i=0;i<12;i++)
        total+= pyr[i].size();
    cout<<"Total number of cubes found = "<<total;
}