#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "gmp.h"


void m_rate_of_change
(
   mpz_t mRateOfChange,
   long  lColumn,
   long  lDepth
);

void m_times_cell_has_changed
(
   mpz_t mTimesChanged,
   mpz_t mRateOfChange,
   mpz_t mIteration
);


mpz_t *pmROC  = NULL;
long  lPixels = 0L;


int main
(
   int   iArgs    ,
   char *apsArgs[]
)
{
    int   i = 0;
    char *pcArgN = NULL;
    char *pcArgR = NULL;
    char *pcArgW = NULL;
    char *pcArgH = NULL;
    long  lN     = 0L;
    long  lR     = 0L;
    long  lW     = 0L;
    long  lH     = 0L;
    long  lPx    = 0L;


    mpz_t mTC;
    mpz_t mValue;
    mpz_t mPerm;
    mpz_t mRateOfChange;
    mpz_t mTimesChanged;
    mpz_t mN;
    mpz_t mR;
    mpz_t mP;
    mpz_t mTCD;

    // a -n=16 -r=8 -w=4 -h=4
    printf ("-n=number in the set. ie colours\n");
    printf ("-r=range of sequence. ie width * height\n");

    for (i = 0; i < iArgs; i++)
    {
       if (0 == strncmp ("-n=", apsArgs[i], 3))         // -n
       {
          pcArgN = apsArgs[i] + 3;
          lN     = atol (pcArgN);
       }
       else if (0 == strncmp ("-r=", apsArgs[i], 3))        // -r
       {
          pcArgR = apsArgs[i] + 3;
          lR     = atol (pcArgR);
       }
       else if (0 == strncmp ("-w=", apsArgs[i], 3))        // -w
       {
          pcArgW = apsArgs[i] + 3;
          lW     = atol (pcArgW);
       }
       else if (0 == strncmp ("-h=", apsArgs[i], 3))        // -h
       {
          pcArgH = apsArgs[i] + 3;
          lH     = atol (pcArgH);
       }

    }

   //

   lPixels = lW * lH;

   printf ("pcArgN=[%s] lN=%ld\n", pcArgN, lN);
   printf ("pcArgR=[%s] lR=%ld\n", pcArgR, lR);
   printf ("lW=%ld lH=%ld lPixels=%ld\n", lW, lH, lPixels);

   // permutation  (order important, repetition allowed)          = n^r
   // permutation  (order NOT important, repetition allowed)      = (n+r-1)!/r!(n-1)!
   // permutation  (order NOT important, repetition NOT allowed)  = n!/(n-r)!(r!)      (NOTE: n > r)
   // permutation  (order important, repetition NOT allowed)      = n!/(n-r)!          (NOTE: n > r)

   // n = the number in the set. ie colors
   // r = the number to choose. ie width*height

   mpz_init (mPerm);
   mpz_init (mN);
   mpz_init (mR);

   mpz_set_ui (mPerm, 0);
   mpz_set_ui (mN   , lN);
   mpz_set_ui (mR   , lR);
  // gmp_printf ("lNa=%Zd\n", lNa);
  // gmp_printf ("lNb=%Zd\n", lNb);
  // gmp_printf ("lRa=%Zd\n", lRa);


   mpz_pow_ui (mPerm, mN, lR);
   gmp_printf ("mN (Colours) = %Zd\n", mN);
   gmp_printf ("lR (Pixels)  = %ld\n", lR);
   gmp_printf ("Permutations = %Zd\n", mPerm);

   // Precompute the rate of change for each column

#ifdef NOMEM
   pmROC = (mpz_t *)malloc (sizeof (mpz_t) * lPixels);
   if (NULL == pmROC)
      exit (printf ("malloc() failed\n"));

   long lC = 0L;
   for (lC = 0L; lC < lPixels; lC++)
   {
      printf ("%ld pixel\n", lC);

      mpz_init (pmROC[lC]);
      mpz_init (mRateOfChange);

      m_rate_of_change (mRateOfChange, lC, lN);
      mpz_set (pmROC[lC], mRateOfChange);
   }
   printf ("Pre-computed rateofchange for %ld pixels\n", lPixels);

   mpz_clear (mRateOfChange);
#endif
 // For each permuation, do each pixel

   printf ("Permutation 0\n");
   mpz_init   (mP);
   mpz_set_ui (mP, 0);


 //  while (mpz_cmp (mP, mPerm) < 0)
   {
      // for each pixel.

      gmp_printf ("Perm=%Zd - ", mP);

      for (lPx = 0L; lPx < lPixels; lPx++)
      {
         mpz_init   (mTimesChanged);
         mpz_set_ui (mTimesChanged, 0);

         m_rate_of_change (mRateOfChange, lPx, lN);
         m_times_cell_has_changed (mTimesChanged, mRateOfChange, mP);
         //m_times_cell_has_changed (mTimesChanged, pmROC[lPx], mP);

         //printf ("lPx=%ld\n", lPx);

         mpz_init   (mTC);
         mpz_init   (mTCD);
         mpz_init   (mValue);

         mpz_set_ui (mTC   , 0);
         mpz_set_ui (mTCD  , 0);
         mpz_set_ui (mValue, 0);


         mpz_set    (mTC, mTimesChanged);
         mpz_fdiv_q (mTCD, mTC, mN);   // timeschanged / depth

         //gmp_printf ("\nmTCD=%Zd mTimesChanged=%Zd mTC=%Zd mN=%Zd pmROC[lPx]=%Zd\n", mTCD, mTimesChanged, mTC, mN, pmROC[lPx]);

         mpz_mul    (mTCD, mTCD, mN);   // * depth
         mpz_sub    (mValue, mTimesChanged, mTCD);  // timeschanged - mTC

         gmp_printf ("%Z02d,", mValue);
         if (0 == fmod ((double)lPx+1, (double)lW))
            printf ("\n");

         mpz_clear (mTC);
         mpz_clear (mTCD);
         mpz_clear (mValue);

         mpz_clear (mTimesChanged);
         mpz_clear (mRateOfChange);
      }

      printf ("=done\n");
   //   mpz_add_ui (mP, mP, 1);

   }


//   for (lC = 0L; lC < lPixels; lC++)
//      mpz_clear (pmROC[lC]);
//   free (pmROC);

   return (0);
}

/*
// Given a permutation index, n=colours, r=range,
int pixel_from_perm
(
   *plColour,
    mPermIndex,
    lDepth,
    lWidth,
    lHeight,
    lColumn  // between 0 and width*height
)
{
   rate_of_change          = (depth ^ column)+1
   times_cell_has_changed  = itteration / (rate_of_change - 1);
   value                   = times_cell_has_changed - ((times_cell_has_changed / depth) * depth);
}
*/

void m_times_cell_has_changed
(
   mpz_t mTimesChanged,
   mpz_t mRateOfChange,
   mpz_t mIteration
)
{
   mpz_t mROC;

   mpz_init   (mROC);
   mpz_set    (mROC, mRateOfChange);
   mpz_sub_ui (mROC, mROC, 1);

   //gmp_printf ("mROC=%Zd mRateOfChange=%Zd mIteration=%Zd\n", mROC, mRateOfChange, mIteration);
   mpz_fdiv_q (mTimesChanged, mIteration, mROC);
   mpz_clear (mROC);
}

void m_rate_of_change
(
   mpz_t mRateOfChange,
   long  lColumn,
   long  lDepth
)
{
   mpz_t mDepth;

   mpz_init (mRateOfChange);
   mpz_init (mDepth);

   mpz_set_ui (mDepth, lDepth);
   mpz_pow_ui (mRateOfChange, mDepth, lColumn);
   mpz_add_ui (mRateOfChange, mRateOfChange, 1L);
   //gmp_printf ("column=%ld, Rate of change=%Zd\n", lColumn, mRateOfChange);

   mpz_clear (mDepth);
}
