//#####################################################################
// 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/Arrays/ARRAY.h>
#include <Core/Log/LOG.h>

using namespace PhysBAM;

// TASK 1: Make sure the Automatic_Program line in the SConstript in this
// directory corresponding to this file is uncommented.

// TASK 2: Compile the project (and the rest of the library if needed).  On
// linux/mac:
//
// release: scons --warn=no-duplicate-environment --warn=no-deprecated -Q --implicit-cache -u -j 4
//
// debug: scons --warn=no-duplicate-environment --warn=no-deprecated -Q --implicit-cache -u -j 4 TYPE=debug
//
// Note that the "-j 4" part tells scons how many CPU cores to use.  I recommend
// using min(C, (M-2)/1.5) as the number of cores, where your machine has C
// cores (including hyperthreaded, if you have that) and M gigabytes of RAM.
// This step may take a long time to complete the first time you do it.

// TASK 3: read through ARRAY.h and its base class ARRAY_BASE.h.  What you
// should be looking for are (1) what data the classes store, (2) what functions
// they provide for you to call.  As you are given a task, look through the
// files for functions that you think might help you complete that task, and
// read through the definitions of those functions to see how they work and what
// they do.  You can also try using the function and running the program to see
// if it had the effect you expected.

int TASK=-1;

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

int main(int argc, char* argv[])
{
    ARRAY<int> a0;
    Test(a0,"()");

    TASK=4;
    // Append the number 7 to a0.
    // TODO
    Test(a0,"(7)");

    // This constructor also resizes the array to 5 elements.  The elements are
    // automatically initialized to zero.
    ARRAY<int> a1(5);
    Test(a1,"(0 0 0 0 0)");
    
    TASK=5;
    // Set the first entry of a1 to 8.  Note that arrays in this version
    // of PhysBAM are zero-indexed.
    // TODO
    Test(a1,"(8 0 0 0 0)");

    TASK=6;
    // There are two ways to access the size of an array: a1.m and
    // a1.Size().  Use a for loop to set each element i to the value i*i.
    // TODO
    Test(a1,"(0 1 4 9 16)");

    TASK=7;
    // Resize a0 so that it has 6 elements.  Note that, as with the
    // constructor, the new elements are set to zero by default.
    // TODO
    Test(a0,"(7 0 0 0 0 0)");

    TASK=8;
    // Append a1 to the end of a0.  There is a routine that does this
    // for you.
    // TODO
    Test(a0,"(7 0 0 0 0 0 0 1 4 9 16)");

    TASK=9;
    // Set all elements of a0 to be 2.  There is a routine that does
    // this for you.
    // TODO
    Test(a0,"(2 2 2 2 2 2 2 2 2 2 2)");

    // Next, lets look at some more ways to create an ARRAY.
    
    ARRAY<int> a2(a1);
    Test(a2,"(0 1 4 9 16)");

    ARRAY<int> a3(a1.m,init_new,1);
    Test(a3,"(1 1 1 1 1)");

    ARRAY<int> a4(a1.m,no_init); // Contains 5 values, uninitialized.
    // Set all of them to 7.
    // TODO
    Test(a4,"(7 7 7 7 7)");

    ARRAY<int> a5={4,5,6};
    Test(a5,"(4 5 6)");

    TASK=10;
    // Resize a5 to 5 elements, setting new values to 1.  There is a
    // function that does this for you.
    // TODO
    Test(a5,"(4 5 6 1 1)");

    TASK=11;
    // Resize a5 to 8 elements, setting all values to 2.  There is a
    // function that does this for you.
    // TODO
    Test(a5,"(2 2 2 2 2 2 2 2)");

    TASK=12;
    // Set a5 so that it holds (4 5 6 8 3 2).  You can do this using
    // one statement.
    // TODO
    Test(a5,"(4 5 6 8 3 2)");

    TASK=13;
    // We can visit the elements in a5 by using a range-based for loop.  Use such a loop to replace each entry in a5 with its square.
    // TODO
    Test(a5,"(16 25 36 64 9 4)");

    ARRAY<int> a6={4,1,0};
    // Note that this looks like an array A(i) whose value is equal to
    // a5(a6(i)).  You will see this construct occasionally.
    Test(a5.Subset(a6),"(9 25 16)");

    ARRAY<int> a7(8);
    TASK=14;
    // Set entries 4, 1, and 0 (the entries of a6) of a7 to the number
    // 1.  Note that this can be done in one line.
    // TODO
    Test(a7,"(1 1 0 0 1 0 0 0)");

    ARRAY<int> a8 = {3,5,8,1};
    ARRAY<int> a9 = {0,7,1,2};

    TASK=15;
    // For each of the output lines below, calculate the desired a0
    // from a8 and a9 using the functions available to you.  Each solution
    // should be a one-liner.  You should not write any loops here.  You might
    // not need a8 or a9 for every part.
    // TODO: calculate a0.
    // TODO
    Test(a0,"(3 12 9 3)");

    // TODO: calculate a0.
    // TODO
    Test(a0,"(3 -2 7 -1)");

    // TODO: calculate a0.
    // TODO
    Test(a0,"(6 10 16 2)");

    // TODO: calculate a0.
    // TODO
    Test(a0,"(9 8 23 1)");

    // TODO: calculate a0.
    // TODO
    Test(a0,"(7 7 7 7)");

    // TODO: calculate a0.
    // TODO
    Test(a0,"(10 17 11 12)");

    // TODO: calculate a0.
    // TODO
    Test(a0,"(0 -7 -1 -2)");

    // TODO: calculate a0.  (Hint: this is computed from a8.)
    // TODO
    Test(a0,"(1 2 4 0)");

    TASK=16;
    // Use the Last function to update a0.
    // TODO
    Test(a0,"(1 2 4 7)");

    TASK=17;
    // Remove the second entry while keeping the order unchanged.
    // TODO
    Test(a0,"(1 4 7)");

    TASK=18;
    // Insert a new entry into the array at index 2, keeping the order
    // unchanged.
    // TODO
    Test(a0,"(1 4 8 7)");

    TASK=19;
    // Clear out a0.
    // TODO
    Test(a0,"()");

    TASK=20;
    // Update a0 so it has twenty 3's.  (Don't write a loop.
    // You can do this in one statement.)
    // TODO
    Test(a0,"(3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3)");

    TASK=21;
    // Update a0 so it has the following value.
    // TODO
    Test(a0,"(3 3 3 3 3 3)");

    TASK=22;
    // Update a0 so it has the following value.  You can use loops and
    // more than one statement.
    // TODO
    for(int i=0;i<a0.m;i++) a0(i)=i%3;
    Test(a0,"(0 1 2 0 1 2 0 1 2 0 1 2)");

    TASK=23;
    // Update a0 so it has the following value.
    // TODO
    Test(a0,"(0 1 2 0 1 7 0 1 2 0 1 2)");

    TASK=24;
    // Update a0 so it has the following value.  No loops, one
    // statement only.
    // TODO
    Test(a0,"(2 1 0 2 1 0 7 1 0 2 1 0)");

    TASK=25;
    // Update a0 so it has the following value.  No loops, one
    // statement only.
    // TODO
    Test(a0,"(0 0 0 0 1 1 1 1 2 2 2 7)");

    ARRAY<ARRAY<int> > b0(3);
    Test(b0,"(() () ())");

    TASK=26;
    // Update b0 so that each of the print statements below prints out
    // the correct values.  Each can be done with one statement or one loop.
    // TODO
    Test(b0,"(() (3 3 3 3) ())");

    // TODO
    Test(b0,"(() (3 3 3 3) (0 0 0))");

    // TODO
    Test(b0,"(() (3 3 3 3) (3 4 5))");

    // TODO
    Test(b0,"(() (3 4 5) (3 3 3 3))");

    // TODO
    Test(b0,"((1) (3 4 5) (3 3 3 3))");

    // TODO
    Test(b0,"((1) (4 5) (3 3 3 3))");

    // TODO
    Test(b0,"((1) (4 5) (3 3 3 3) () ())");

    //  (Use a8.)
    // TODO
    Test(b0,"((1) (4 5) (3 3 3 3) () () (3 5 8 1))");

    TASK=27;
    // Create and initialize an ARRAY with std::string objects called
    // c0.
    int c0=0; // Replace this with the real thing.
    // TODO
    Test(c0,"(cat dog lizard)");

    return 0;
}
