//#####################################################################
// Copyright 2020.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################

#include <Core/Basic_Geometry/SPHERE.h>
#include <Core/Data_Structures/HASHTABLE.h>
#include <Core/Data_Structures/TUPLE.h>
#include <Core/Log/LOG.h>
#include <Core/Matrices/MATRIX.h>
#include <Core/Random_Numbers/RANDOM_NUMBERS.h>
#include <Core/Utilities/VIEWER_DIR.h>
#include <Core/Vectors/VECTOR.h>

using namespace PhysBAM;

// TASK 1: We will introduce RANDOM_NUMBERS and logging/printing facilities in
// this part.  Read through RANDOM_NUMBERS to get a general idea for what is
// available.

int TASK=-1;

template<class A,class B>
void Test(const A& a,const B& b)
{
    if(TASK>=0) std::cout<<"TASK: "<<TASK<<std::endl;
    auto aa=LOG::sprintf("%P",a);
    auto bb=LOG::sprintf("%P",b);
    std::cout<<"should be: "<<bb<<std::endl;
    std::cout<<"computed : "<<aa<<std::endl;
    if(aa==bb) std::cout<<"SAME\n"<<std::endl;
    else std::cout<<"DIFFERENT\n"<<std::endl;
    TASK=-1;
}

int main(int argc, char* argv[])
{
    // These three typedefs are idiomatic for PhysBAM.
    typedef double T;
    typedef VECTOR<T,2> TV;
    // typedef VECTOR<int,2> IV;

    // PhysBAM supports two main output paradigms for printing data.  The first
    // is based on the standard C++ streams.  The only real difference is that
    // we use LOG::cout instead of std::cout.  Everything you can print with one
    // works with the other.  The difference is that LOG::cout can be asked to
    // redirect a copy of the stdout to a log file.  This is very useful for
    // simulations since you can look up after the fact useful pieces of
    // information - timing, time step sizes, maximum velocity, etc.  You should
    // generally use LOG::cout instead of std::cout.  Usage-wise, there is
    // nothing surprising here.  A wide variety of primitive PhysBAM types
    // support printing in this way.  A few types also support input through
    // cin.

    TV v(2,3);
    RANGE<TV> range=RANGE<TV>::Centered_Box();
    ARRAY<pair<int,std::string> > ar={{3,"food"},{8,"cup"}};
    LOG::cout<<"range: "<<range<<"  vector: "<<v<<"  array: "<<ar<<std::endl;

    HASHTABLE<pair<int,std::string> > h0;
    h0.Set_All(ar);
    HASHTABLE<int,std::string> h1;
    h1.Set(3,"fun");
    h1.Set(7,"day");
    LOG::cout<<"h0: "<<h0<<"  h1: "<<h1<<std::endl;

    MATRIX<T,3> R;
    R+=3;
    LOG::cout<<"R: "<<R<<"  vec: "<<R.Column(0)<<std::endl;

    // The second (and much newer) idiom is LOG::printf.  This is essentially
    // just the same as the regular printf function, except that it supports the
    // special %P.  This accepts any type of argument that LOG::cout does.
    // Under the hood, it uses LOG::cout to do this.  Unlike std::printf,
    // LOG::printf is typesafe.  This is because std::printf uses the
    // non-type-safe C-style var-args facility.  LOG::printf uses variadic
    // templates.
    LOG::printf("range: %P  vector: %P  array: %P\n",range,v,ar);
    LOG::printf("h0: %P  h1: %P\n",h0,h1);
    LOG::printf("R: %P  vec: %P\n",R,R.Column(0));

    // Of course, you can use the regular ones, too.  They can be intermixed.
    LOG::printf("%04d %s %c %g %P %.3f\n",13,"cat",'z',3.23,h1,3.1415926);

    ARRAY<float> a0={3,1,3.01,-1.4/3};
    
    // There is also limited support for formatting specifiers on the %P.
    LOG::printf("%%P     '%P'\n",a0);
    LOG::printf("%%.3P   '%.3P'\n",a0);
    LOG::printf("%%.16P   '%.16P'\n",a0);
    LOG::printf("%%10.5P   '%8.5P'\n",1.4/3);

    ARRAY<pair<VECTOR<float,3>,std::string> > a1(2);
    for(auto&i:a1) i.second="dog";
    int depth=4;
    LOG::printf("%*s%P\n",depth,"",a1);

    // Create the random number generator.  This is based on the Mersenne
    // Twister pseudorandom number generator in the STL.  This is a very good
    // generator for pretty much everything but crypto.  By default, the seed is
    // set to time, which will give you different results every time you run the
    // program, unless you run it twice within a second.
    RANDOM_NUMBERS<T> rand;

    // Fix the seed.  This is useful when determinism is helpful, such as
    // reproducing results, testing, or debugging.
    rand.Set_Seed(1212);

    // Random numbers can be requested individually.
    T t0=rand.Get_Number(); // in [0,1)
    T t1=rand.Get_Uniform_Number(-10,10); // in [-10,10)
    int i0=rand.Get_Uniform_Integer(5,10); // in [5,10]
    LOG::printf("%P %P %P\n",t0,t1,i0);

    // Normally, however, random numbers are obtained through Fill_Uniform.  In
    // this case, the number is chosen in [-1,1].
    rand.Fill_Uniform(t0,-1,1);
    LOG::printf("t0 %P\n",t0);

    // Each scalar is filled independently.
    ARRAY<T> a2(10);
    rand.Fill_Uniform(a2,-1,1);
    LOG::printf("a0 %P\n",a2);

    // Nesting is fine.  Every component of every vector is filled independently.
    ARRAY<TV> a3(3);
    rand.Fill_Uniform(a3,-1,1);
    LOG::printf("a1 %P\n",a3);

    // Works on matrices.
    MATRIX<T,3> m0;
    rand.Fill_Uniform(m0,-1,1);
    LOG::printf("m0 %P\n",m0);

    // A few types have special versions.  The x components are in the range
    // [0,1) and the y components are in the range [2,3).
    RANGE<TV> r0(TV(0,2),TV(1,3));
    rand.Fill_Uniform(a3,r0);
    LOG::printf("a3 %P\n",a3);


    
    return 0;
}
