/*
test6.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -o test6.so -fPIC test6.c

Depuis Python, exécuter le script test6.py qui est donné dans un autre fichier.

Pour les types que Python reconnait et peut convertir en C :
https://www.tutorialspoint.com/python/python_further_extensions.htm
***********************************************************************/

#include "Python.h"

static PyObject *test6_sommeInv(PyObject *self, PyObject *args) {
//===============================================================
// Définit la fonction en C qui sera appelée depuis Python
// PyObject *x_obj;  // autre possibilité.
// Somme des inverses des nombres entiers de lNbMin à lNbMax-1
// lNbMin et lNbMax sont les deux arguments.
long lNbMin;
long lNbMax;
long lNbr;
double vRep;

if (!PyArg_ParseTuple(args, "ll", &lNbMin, &lNbMax)) return NULL; 

vRep = 0.0;
for (lNbr=lNbMax-1; lNbr>=lNbMin; lNbr--) vRep = vRep + 1.0 / lNbr;

return PyFloat_FromDouble(vRep);
// https://docs.python.org/3.3/c-api/float.html
} // test6_sommeInv

static PyObject *test6_sommeInv9(PyObject *self, PyObject *args) {
//================================================================
// Définit la fonction en C qui sera appelée depuis Python
// PyObject *x_obj;  // autre possibilité.
// Somme des inverses des nombres entiers de lNbMin à lNbMax-1
// Tous les nombres ayant le chiffre 9 dans leur écriture décimale
// sont enlevés de la somme.
// Il y a deux paramètrs en argument : lNbMin, lNbMax
// lNbMin et lNbMax sont les deux arguments.
long lNbMin;
long lNbMax;
long lNbr;
long lDixPow;
double vRep;

if (!PyArg_ParseTuple(args, "ll", &lNbMin, &lNbMax)) return NULL; 

vRep = 0.0;
lNbr=lNbMin;
while (lNbr<lNbMax) {
  vRep = vRep + 1.0 / lNbr;
  lNbr++;
  
  if (lNbr % 10 == 9) { lNbr ++;
    if (lNbr/10 % 10 == 9) { lNbr += 10;
      if (lNbr/100 % 10 == 9) { lNbr += 100;
        // J'ai laissé quelques "if" pour comprendre la structures des tests.
        lDixPow = 1000;
        while (lNbr/lDixPow % 10 == 9) {
          lNbr += lDixPow;
          lDixPow *=10;
          }
        }      
      }
    }
  } // while

return PyFloat_FromDouble(vRep);
// https://docs.python.org/3.3/c-api/float.html
} // test6_sommeInv9

static PyObject *test6_sommeInvx(PyObject *self, PyObject *args) {
//===============================================================
// Définit la fonction en C qui sera appelée depuis Python
// PyObject *x_obj;  // autre possibilité.
// Somme des inverses des nombres entiers de lNbMin à lNbMax-1
// Tous les nombres ayant le chiffre lc dans leur écriture décimale
// sont enlevés de la somme.
// Il y a trois paramètrs en argument : lNbMin, lNbMax et lc

long lNbMin;
long lNbMax;
long lc;
long lNbr;
long lDixPow;
double vRep;

if (!PyArg_ParseTuple(args, "lll", &lNbMin, &lNbMax, &lc)) return NULL; 

if (lc == 9) return test6_sommeInv9(self, args);

vRep = 0.0;
lNbr=lNbMin;
while (lNbr<lNbMax) {
  vRep = vRep + 1.0 / lNbr;
  lNbr++;
  
  lDixPow = 1;
  while (lNbr/lDixPow >= 1) {
    if (lNbr/lDixPow % 10 == lc) {
      lNbr += lDixPow;
      break; // sort de la boucle while
      }
    lDixPow *=10;
    } // while
  } // while

return PyFloat_FromDouble(vRep);
// https://docs.python.org/3.3/c-api/float.html
} // test6_sommeInvx

// Définit l'aide associée à la fonction.
#define AIDE "sommeInv prend deux nombres entiers Nmin et Nmax en entrée\n\
et retourne la somme des inverses des entiers de Nmin à (Nmax-1)."

// Définit l'aide associée à la fonction.
#define AIDE9 "sommeInv9 prend deux nombres entiers Nmin et Nmax en entrée\n\
et retourne la somme des inverses des entiers de Nmin à (Nmax-1)\n\
dans laquelle tous les nombres contenant le chiffre 9 ont été éliminés.\n\
Voici des nombres éliminés :\n\
9 ; 19 ; 29 ; 9xxx ; 19xxx ; 29xxx ; 999999.\n\
Nmin ne doit pas contenir de chiffre 9."

// Définit l'aide associée à la fonction.
#define AIDEx "sommeInvx prend trois nombres entiers Nmin, Nmax et Nc en entrée\n\
et retourne la somme des inverses des entiers de Nmin à (Nmax-1)\n\
dans laquelle tous les nombres contenant le chiffre Nc ont été enliminés\n\
c.f. simmeInv9 pour l'analogie.\n\
Nmin ne doit pas contenir de chiffre Nc."

static PyMethodDef test6Methods[] = {
//===================================
{"sommeInv",  test6_sommeInv, METH_VARARGS, AIDE}, // Est un texte d'aide. Obtenu avec : print(test6.sommeInv.__doc__)
{"sommeInv9",  test6_sommeInv9, METH_VARARGS, AIDE9}, // Est un texte d'aide. Obtenu avec : print(test6.sommeInv9.__doc__)
{"sommeInvx",  test6_sommeInvx, METH_VARARGS, AIDEx}, // Est un texte d'aide. Obtenu avec : print(test6.sommeInvx.__doc__)
{NULL, NULL, 0, NULL}  // Sentinel 
};

static struct PyModuleDef test6module = {
//=======================================
// Est une structure nécessaire pour Python ???
  PyModuleDef_HEAD_INIT,
  "test6",   /* name of module */
  NULL,  //test6_doc, /* module documentation, may be NULL */
  -1,       /* size of per-interpreter state of the module,
             or -1 if the module keeps state in global variables. */
  test6Methods
};

PyMODINIT_FUNC PyInit_test6(void) {
//=================================
// Fonction de création du module
return PyModule_Create(&test6module);
} // PyInit_test6

int main(int argc, char *argv[]) {
//================================
// Fonction principale, qui sera exécutée au chargement du module
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
  fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  exit(1);
  }

/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("test6", PyInit_test6);

/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);

/* Initialize the Python interpreter.  Required. */
Py_Initialize();

/* Optionally import the module; alternatively,
   import can be deferred until the embedded script
   imports it. */
PyImport_ImportModule("test6");

PyMem_RawFree(program);
return 0;
} // main
