Wednesday, July 7, 2021

Solution via C++ code for #25 from 16052021 Eugene Djobs's stream

Пусть M – сумма делителей, максимально приближенных к квадратному корню числа, не считая единицы и самого числа. Квадратный корень в качестве делителя не рассматривать. Если нет делителей, отличных oт квадратного корня, единицы и самого числа, значение M считается равным 0. Напишите программу, которая перебирает целые числа, большие 710017, в порядке возрастания и ищет среди них такие, для которых значение M кратно 10 и больше M для предыдущего найденного числа.. Вывести первые 5 найденных чисел и соответствующие им значения M.

Замена 710017 на 713000218 и 5 на 100

Python code for original task

[boris@fedora33server FORWARD]$ cat dba25160521.py

numbers = []

M=0

for i in range(713000218, 10000000000):

    minDiv = 0

    for div in range(int(i**0.5),1,-1):

        if i % div == 0 and div**2 != i:

            minDiv = div

            break

    if minDiv != 0:

        currMV = (i // minDiv) + minDiv

    if currMV % 10 == 0 and currMV > M:

        numbers.append(i)

        print(i,currMV)

        M = currMV

    if len(numbers) == 100:

        break

Проверяем что для первых 5 найденных чисел программы дают те же результаты

[boris@fedora33server FORWARD]$ python dba25160521.py

713000221 67330

713000261 437190

713000269 11688590

713000279 23000040

713000571 237666860

[boris@fedora33server FORWARD]$ ./mainVector1

 Input start : 713000218

 Input depth : 5

713000221 67330 

713000261 437190 

713000269 11688590 

713000279 23000040 

713000571 237666860 


***************************
Теперь заменим 5 на 100
***************************

[boris@fedora33server FORWARD]$ cat procVector1.cpp

#include <iostream>

#include <vector>

#include <cstddef>

#include <cmath>

std::vector<std::vector<int> >  resultList(int m, int depth)

{

   std::vector<std::vector<int> > vec;

   std::vector<int> v1;

    int i,j,M,minDiv,currMV,div;

    M = 0;

    for (int i = m; i < 10000000000 ; i++) {

        minDiv = 0;    

        for (div = (int)sqrt(i); div > 1; div--)

        {

          if (i%div == 0 && div*div != i)

             {

              minDiv = div ;

              break ;

             }

         }

        if (minDiv != 0)

           currMV = (i/minDiv) + minDiv;

        if (currMV % 10 == 0 && currMV > M)

         {

           v1.push_back(i);

           v1.push_back(currMV);

           vec.push_back(v1);

           M = currMV;

           while (!v1.empty())

           {    

             v1.pop_back();

           }  

          }

        if (vec.size() == depth)

              break;

     }

 return vec;

}

[boris@fedora33server FORWARD]$ cat mainVector1.cpp

#include <iostream>

#include <vector>

#include <cstddef>

std::vector<std::vector<int> > resultList(int m,int n);

int main()

{

   int m,n;

   std::cout << " Input start : "; 

   std::cin >> m ; 

   std::cout << " Input depth : ";

   std::cin >> n ;

   std::vector<std::vector<int> > rez = resultList(m,n);

   // Displaying the 2D vector

    for (int i = 0; i < rez.size(); i++) {

       for (int j = 0; j < rez[i].size(); j++)

           std::cout << rez[i][j] << " ";

     std::cout << std::endl;

  }

}

Compilation via g++ on Fedora Linux 34

$ g++ procVector1.cpp  mainVector1.cpp -o mainVector1

[boris@fedora33server FORWARD]$ ./mainVector1
 Input start : 713000218
 Input depth : 100
713000221 67330 
713000261 437190 
713000269 11688590 
713000279 23000040 
713000571 237666860 
713001381 237667130 
713001441 237667150 
713001621 237667210 
713001651 237667220 
713001711 237667240 
713001831 237667280 
713001921 237667310 
713002011 237667340 
713002071 237667360 
713002161 237667390 
713002431 237667480 
713002611 237667540 
713002701 237667570 
713002881 237667630 
713003091 237667700 
713003241 237667750 
713003271 237667760 
713003451 237667820 
713003721 237667910 
713003901 237667970 
713004141 237668050 
713004231 237668080 
713004261 237668090 
713004351 237668120 
713004441 237668150 
713004771 237668260 
713005071 237668360 
713005311 237668440 
713005401 237668470 
713005701 237668570 
713005791 237668600 
713005941 237668650 
713006421 237668810 
713006661 237668890 
713006751 237668920 
713006961 237668990 
713007111 237669040 
713007231 237669080 
713007411 237669140 
713007651 237669220 
713007951 237669320 
713008221 237669410 
713008371 237669460 
713008641 237669550 
713008851 237669620 
713008911 237669640 
713009211 237669740 
713009481 237669830 
713009991 237670000 
713010021 237670010 
713010171 237670060 
713010531 237670180 
713010561 237670190 
713011281 237670430 
713011641 237670550 
713011731 237670580 
713011911 237670640 
713012151 237670720 
713012361 237670790 
713012451 237670820 
713012511 237670840 
713012721 237670910 
713012991 237671000 
713013141 237671050 
713013321 237671110 
713013771 237671260 
713013801 237671270 
713013891 237671300 
713014041 237671350 
713014131 237671380 
713014431 237671480 
713014611 237671540 
713015421 237671810 
713015481 237671830 
713015691 237671900 
713015781 237671930 
713016201 237672070 
713016321 237672110 
713016651 237672220 
713016831 237672280 
713016921 237672310 
713017131 237672380 
713017671 237672560 
713017761 237672590 
713017911 237672640 
713018091 237672700 
713018181 237672730 
713018211 237672740 
713018391 237672800 
713018571 237672860 
713019171 237673060 
713019261 237673090 
713019381 237673130 
713019621 237673210 
713019891 237673300 
 

Sunday, July 4, 2021

Creating Python Wrapper for one C++ procedure been inspired by Yandex Q news wire in Mathematics Version 2

 (.env) [boris@fedora33server YANDEXDBA]$ cat test.h

#pragma once

#include <cstddef>

#include <vector>

namespace abc {

std::vector<int>  catchList(int q,int m,int pw1,int pw2) ;

}  

(.env) [boris@fedora33server YANDEXDBA]$ cat test.cpp

#include <Python.h>

#include <iostream>

#include <vector>

#include <cmath>

#include "test.h"

#pragma GCC diagnostic ignored "-Wsign-compare"

namespace abc {

int digitSum(int n)  

{  

  int sum=0,m;    

  while(n>0)    

  {    

    m=n%10;    

    sum=sum+m;    

    n=n/10;    

  }    

return sum;  

}  

std::vector<int>  catchList(int k,int m,int pw1,int pw2) {

 std::vector<int> result;

 int j,z;

 for( j=k; j < m; ++j)

  {

    z = digitSum(j);

    if (pow(double(z),pw1) == pow(double(j),pw2))

              result.push_back(j);

  }

 return result;

 }

extern "C"{}

namespace {

static PyObject *caughtList(PyObject* self, PyObject* args)

{

   int p,n;

   int pwr1,pwr2;

  // Now four arguments pass through PyArg_ParseTuple

   if(!PyArg_ParseTuple(args,"iiii" ,&p,&n,&pwr1,&pwr2))

       return NULL;

   std::vector<int>numbers = abc::catchList(p,n,pwr1,pwr2);

   PyObject* result = PyList_New(numbers.size());

   for(int i = 0; i < numbers.size(); i++) {

     PyList_SetItem(result, i, PyLong_FromLong(numbers[i]));

   }

   return result;  

};

// Our Module's Function Definition struct

// We require this `NULL` to signal the end of our method

// definition

static PyMethodDef myMethods[] = {

   { "caughtList", caughtList, METH_VARARGS, "Calculate prime numbers" },

   { NULL, NULL, 0, NULL }

};

// Our Module Definition struct

static struct PyModuleDef myModule = {

   PyModuleDef_HEAD_INIT,

   "myModule",

   "Test Module",

   -1,

   myMethods

};

// Initializes our module using our above struct

PyMODINIT_FUNC PyInit_myModule(void)

{

   return PyModule_Create(&myModule);

};

} // namespace(.env) 

[boris@fedora33server YANDEXDBA]$ cat setup.py

from distutils.core import setup, Extension

import sysconfig

language = 'c++'

std = 'c++20'

default_compile_args = sysconfig.get_config_var('CFLAGS').split()

extra_compile_args = [f"-std={std}", "-Wall", "-Wextra", "-Werror", "-DNDEBUG", "-O3"]

setup(name = 'myModule', version = '1.0',  \

   ext_modules = [Extension('myModule', ['test.cpp'])])

(.env) [boris@fedora33server YANDEXDBA]$ python setup.py install

running install

running build

running build_ext

building 'myModule' extension

creating build

creating build/temp.linux-x86_64-3.9

gcc -pthread -Wno-unused-result -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/home/boris/YANDEXDBA/.env/include -I/usr/include/python3.9 -c test.cpp -o build/temp.linux-x86_64-3.9/test.o

creating build/lib.linux-x86_64-3.9

g++ -pthread -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g build/temp.linux-x86_64-3.9/test.o -L/usr/lib64 -o build/lib.linux-x86_64-3.9/myModule.cpython-39-x86_64-linux-gnu.so

running install_lib

copying build/lib.linux-x86_64-3.9/myModule.cpython-39-x86_64-linux-gnu.so -> /home/boris/YANDEXDBA/.env/lib64/python3.9/site-packages

running install_egg_info

Removing /home/boris/YANDEXDBA/.env/lib64/python3.9/site-packages/myModule-1.0-py3.9.egg-info

Writing /home/boris/YANDEXDBA/.env/lib64/python3.9/site-packages/myModule-1.0-py3.9.egg-info

(.env) [boris@fedora33server YANDEXDBA]$ cat MyProg1.py
import myModule
minV =  int(input("Input scaning start value :" ))
maxV =  int(input("Input scaning finish value :" ))
p1 = int(input("Input power for digitSum :" ))
p2 = int(input("Input power for Number to detect: " ))
print(myModule.caughtList(minV,maxV,p1,p2))






























Friday, July 2, 2021

Creating Python Wrapper for one C++ procedure been inspired by Yandex Q news wire in Mathematics

UPDATE as of 03/07/2021 15:36 MSK

To obtain the answer for particular question consider version of procedure catchList( ) with loop from 10 to 100 instead of 100 to 1000.

std::vector<int>  catchList(int m,int pw1,int pw2) {

 std::vector<int> result;

 int j,z;

 for( j=10; j < m; ++j)

  {

    z = digitSum(j);

    if (pow(double(z),pw1) == pow(double(j),pw2))

              result.push_back(j);

  }

 return result;

 }

(.env) [boris@fedora33server YANDEXQ]$ python setup.py install

Next step is addressing exactly question has been asked

(.env) [boris@fedora33server YANDEXQ]$ cat MyProg1.py

import myModule

p1 = int(input("input power for digitSum :" ))

p2 = int(input("input power for Number to detect: " ))

print(myModule.caughtList(100,p1,p2))

(.env) [boris@fedora33server YANDEXQ]$ python MyProg1.py

input power for digitSum :3

input power for Number to detect: 2

[27]














END UPDATE

(.env) [boris@fedora33server YANDEXQ]$ cat test.h

#pragma once

#include <cstddef>

#include <vector>

namespace abc {

std::vector<int>  catchList(int m,int pw1,int pw2) ;

}  

(.env) [boris@fedora33server YANDEXQ]$ cat test.cpp

#include <Python.h>

#include <iostream>

#include <vector>

#include <cmath>

#include "test.h"

#pragma GCC diagnostic ignored "-Wsign-compare"

namespace abc {

int digitSum(int n)  

{  

  int sum=0,m;    

  while(n>0)    

  {    

    m=n%10;    

    sum=sum+m;    

    n=n/10;    

  }    

return sum;  

}  

std::vector<int>  catchList(int m,int pw1,int pw2) {

 std::vector<int> result;

 int j,z;

 for( j=100; j < m; ++j)

  {

    z = digitSum(j);

    if (pow(double(z),pw1) == pow(double(j),pw2))

              result.push_back(j);

  }

 return result;

 }

extern "C"{}

namespace {

// myModule.caughtList(1000,power1,power2)

// is supposed to be invoked from Python script

static PyObject *caughtList(PyObject* self, PyObject* args)

{

   int n;

   int pwr1,pwr2;

   if(!PyArg_ParseTuple(args,"iii",&n,&pwr1,&pwr2))

       return NULL;

   std::vector<int>numbers = abc::catchList(n,pwr1,pwr2);

   PyObject* result = PyList_New(numbers.size());

   for(int i = 0; i < numbers.size(); i++) {

     PyList_SetItem(result, i, PyLong_FromLong(numbers[i]));

   }

   return result;  

};

// Our Module's Function Definition struct

// We require this `NULL` to signal the end of our method

// definition

static PyMethodDef myMethods[] = {

   { "caughtList", caughtList, METH_VARARGS, "Calculate prime numbers" },

   { NULL, NULL, 0, NULL }

};

// Our Module Definition struct

static struct PyModuleDef myModule = {

   PyModuleDef_HEAD_INIT,

   "myModule",

   "Test Module",

   -1,

   myMethods

};

// Initializes our module using our above struct

PyMODINIT_FUNC PyInit_myModule(void)

{

   return PyModule_Create(&myModule);

};

} // namespace

(.env) [boris@fedora33server YANDEXQ]$ cat setup.py
from distutils.core import setup, Extension
import sysconfig

language = 'c++'
std = 'c++20'
# GCC flags to compile C++ Code
default_compile_args = sysconfig.get_config_var('CFLAGS').split()
extra_compile_args = [f"-std={std}", "-Wall", "-Wextra", "-Werror", "-DNDEBUG", "-O3"]
setup(name = 'myModule', version = '1.0',  \
   ext_modules = [Extension('myModule', ['test.cpp'])])

Now we are going to build shared library which would contain required procedure. Notice we are in Python Virtual Environment  to avoid problems with "install".

(.env) [boris@fedora33server YANDEXQ]$ python setup.py install
running install
running build
running build_ext
building 'myModule' extension
creating build
creating build/temp.linux-x86_64-3.9
gcc -pthread -Wno-unused-result -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/home/boris/YANDEXQ/.env/include -I/usr/include/python3.9 -c test.cpp -o build/temp.linux-x86_64-3.9/test.o
creating build/lib.linux-x86_64-3.9
g++ -pthread -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g build/temp.linux-x86_64-3.9/test.o -L/usr/lib64 -o build/lib.linux-x86_64-3.9/myModule.cpython-39-x86_64-linux-gnu.so
running install_lib
copying build/lib.linux-x86_64-3.9/myModule.cpython-39-x86_64-linux-gnu.so -> /home/boris/YANDEXQ/.env/lib64/python3.9/site-packages
running install_egg_info
Removing /home/boris/YANDEXQ/.env/lib64/python3.9/site-packages/myModule-1.0-py3.9.egg-info
Writing /home/boris/YANDEXQ/.env/lib64/python3.9/site-packages/myModule-1.0-py3.9.egg-info

******************
Make sure we did it 
******************

(.env) [boris@fedora33server YANDEXQ]$ cd build
(.env) [boris@fedora33server build]$ pwd
/home/boris/YANDEXQ/build
(.env) [boris@fedora33server build]$ ls -CRl
.:
total 0
drwxrwxr-x. 2 boris boris 53 Jul  2 20:42 lib.linux-x86_64-3.9
drwxrwxr-x. 2 boris boris 20 Jul  2 20:42 temp.linux-x86_64-3.9

./lib.linux-x86_64-3.9:
total 84
-rwxrwxr-x. 1 boris boris 82696 Jul  2 20:42 myModule.cpython-39-x86_64-linux-gnu.so

./temp.linux-x86_64-3.9:
total 128
-rw-rw-r--. 1 boris boris 127024 Jul  2 20:42 test.o

(.env) [boris@fedora33server YANDEXQ]$ cat MyProg1.py
import myModule
p1 = int(input("input power for digitSum :" ))
p2 = int(input("input power for Number to detect: " ))
print(myModule.caughtList(1000,p1,p2))






























Saturday, June 19, 2021

Creating Basic Python C Extensions

According https://realpython.com/build-python-c-extension-module/

To write Python modules in C, you’ll need to use the Python API, which defines the various functions, macros, and variables that allow the Python interpreter to call your C code. All of these tools and more are collectively bundled in the <Python.h> header file.

Testing has been done on Fedora Linux 34 . Python version is 3.9.5 

 (.env) [boris@fedora33server ]$ cat fputsmodule.c

#include <Python.h>

static PyObject *method_fputs(PyObject *self, PyObject *args) {

    char *str, *filename = NULL;

    int bytes_copied = -1;

    /* Parse arguments */

    if(!PyArg_ParseTuple(args, "ss", &str, &filename)) {

        return NULL;

    }

    FILE *fp = fopen(filename, "w");

    bytes_copied = fputs(str, fp);

    fclose(fp);

    return PyLong_FromLong(bytes_copied);

}

static PyMethodDef FputsMethods[] = {

    {"fputs", method_fputs, METH_VARARGS, "Python interface for fputs C library function"},

    {NULL, NULL, 0, NULL}

};

static struct PyModuleDef fputsmodule = {

    PyModuleDef_HEAD_INIT,

    "fputs",

    "Python interface for the fputs C library function",

    -1,

    FputsMethods

};

PyMODINIT_FUNC PyInit_fputs(void) {

    return PyModule_Create(&fputsmodule);

}

(.env) [boris@fedora33server ]$ cat setup.py

from distutils.core import setup, Extension

def main():

    setup(name="fputs",

          version="1.0.0",

          description="Python interface for the fputs C library function",

          author="Boris D",

          author_email="BorisD@gmail.com",

          ext_modules=[Extension("fputs", ["fputsmodule.c"])])

if __name__ == "__main__":

    main()

(.env) [boris@fedora33server ]$ python3 setup.py install

(.env) [boris@fedora33server ]$ ll
total 12
drwxrwxr-x. 4 boris boris  63 Jun 19 19:30 build
-rw-rw-r--. 1 boris boris 796 Jun 19 19:08 fputsmodule.c
-rw-rw-r--. 1 boris boris 153 Jun 19 19:24 MyFputs.py
-rw-rw-r--. 1 boris boris 347 Jun 19 19:09 setup.py

(.env) [boris@fedora33server ]$ cat MyFputs.py
import fputs

# Write to an empty file named `output.txt`
fputs.fputs("Python is writing to file!", "./output.txt")
with open("./output.txt", "r") as f:
     print(f.read())

(.env) [boris@fedora33server ]$ python MyFputs.py
Python is writing to file!

Runtime snapshots











































(.env) [boris@fedora33server build]$ ls -CRl
.:
total 0
drwxrwxr-x. 2 boris boris 50 Jun 19 20:01 lib.linux-x86_64-3.9
drwxrwxr-x. 2 boris boris 27 Jun 19 20:01 temp.linux-x86_64-3.9

./lib.linux-x86_64-3.9:
total 28
-rwxrwxr-x. 1 boris boris 26720 Jun 19 20:01 fputs.cpython-39-x86_64-linux-gnu.so

./temp.linux-x86_64-3.9:
total 24
-rw-rw-r--. 1 boris boris 20832 Jun 19 20:01 fputsmodule.o

However, in the link above author doesn't focus your attention on
update required in static PyMethodDef myMethods[]  still having
"METH_NOARGS" instead of "METH_VARARGS", what actually confuses inexperienced learners. Following below is working code follows up link above, but  making  code sample easy to reproduce. Notice also that outside VENV you would have problems to run `python setup install`

(.env) [boris@fedora33server FIBONACHI]$ cat test.c

#include <Python.h>

int Cfib(int n)
{
    if (n < 2)
        return n;
    else
        return Cfib(n-1)+Cfib(n-2);
}

// Our Python binding to our C function
// This will take one and only one non-keyword argument
static PyObject *fib(PyObject* self, PyObject* args)
{
    // instantiate our `n` value
    int n;
    // if our `n` value
    if(!PyArg_ParseTuple(args, "i", &n))
        return NULL;
    // return our computed fib number
    

    int result = Cfib(n);
    return Py_BuildValue("i",result);
}

// Our Module's Function Definition struct
// We require this `NULL` to signal the end of our method
// definition
static PyMethodDef myMethods[] = {
    { "fib", fib, METH_VARARGS, "Calculate fibonacii value" },
    { NULL, NULL, 0, NULL }
};

// Our Module Definition struct
static struct PyModuleDef myModule = {
    PyModuleDef_HEAD_INIT,
    "myModule",
    "Test Module",
    -1,
    myMethods
};

// Initializes our module using our above struct
PyMODINIT_FUNC PyInit_myModule(void)
{
    return PyModule_Create(&myModule);
}

(.env) [boris@fedora33server FIBONACHI]$ cat setup.py
from distutils.core import setup, Extension
setup(name = 'myModule', version = '1.0',  \
   ext_modules = [Extension('myModule', ['test.c'])])

(.env) [boris@fedora33server FIBONACHI]python3 setup.py install

(.env) [boris@fedora33server FIBONACHI]$ cat MyProg.py
import myModule
a = int(input("Input number:"))
print(myModule.fib(a))

(.env) [boris@fedora33server FIBONACHI]$ python MyProg.py
Input number:11
89


Relatively complicated sample based on
and minor refactoring just to make things clear


(.env) [boris@sever33fedora enhancePython]$ cat demo.h
unsigned long cfactorial_sum(char num_chars[]);
unsigned long ifactorial_sum(long nums[], int size);
unsigned long factorial(long n);

(.env) [boris@sever33fedora enhancePython]$ cat demolib.c
#include <stdio.h>
#include "demo.h"


unsigned long cfactorial_sum(char num_chars[]) {
    unsigned long fact_num;
    unsigned long sum = 0;

    for (int i = 0; num_chars[i]; i++) {
        int ith_num = num_chars[i] - '0';
        fact_num = factorial(ith_num);
        sum = sum + fact_num;
    }
    return sum;
}

unsigned long ifactorial_sum(long nums[], int size) {
    unsigned long fact_num;
    unsigned long sum = 0;
    for (int i = 0; i < size; i++) {
        fact_num = factorial(nums[i]);
        sum += fact_num;
    }
    return sum;
}

unsigned long factorial(long n) {
    if (n == 0)
        return 1;
    return (unsigned)n * factorial(n-1);
}

(.env) [boris@sever33fedora enhancePython]$ cat demo.c   
#include <Python.h>
#include <stdio.h>
#include "demo.h"

// wrapper function for cfactorial_sum
static PyObject *DemoLib_cFactorialSum(PyObject *self, PyObject *args) {
    char *char_nums;
    if (!PyArg_ParseTuple(args, "s", &char_nums)) {
        return NULL;
    }

    unsigned long fact_sum;
    fact_sum = cfactorial_sum(char_nums);

    return Py_BuildValue("i", fact_sum);
}

// wrapper function for ifactorial_sum
static PyObject *DemoLib_iFactorialSum(PyObject *self, PyObject *args) {

    PyObject *lst; 

    if (!PyArg_ParseTuple(args, "O", &lst)) {
        return NULL;
    }

    int n = PyObject_Length(lst);
    if (n < 0) {
        return NULL;
    }

    long nums[n];
    for (int i = 0; i < n; i++) {
        PyLongObject *item = PyList_GetItem(lst, i);
        /* long num = PyLong_AsLong(item); */

        nums[i] = PyLong_AsLong(item);
    }

    unsigned long fact_sum;
    fact_sum = ifactorial_sum(nums, n);

    return Py_BuildValue("i", fact_sum);
}

// module's function table
static PyMethodDef DemoLib_FunctionsTable[] = {
    {
        "sfactorial_sum", // name exposed to Python
        DemoLib_cFactorialSum, // C wrapper function
        METH_VARARGS, // received variable args (but really just 1)
        "Calculates factorial sum from digits in string of numbers" // documentation
    }, {
        "ifactorial_sum", // name exposed to Python
        DemoLib_iFactorialSum, // C wrapper function
        METH_VARARGS, // received variable args (but really just 1)
        "Calculates factorial sum from list of ints" // documentation
    }, {
        NULL, NULL, 0, NULL
    }
};

// modules definition
static struct PyModuleDef DemoLib_Module = {
    PyModuleDef_HEAD_INIT,
    "demo",     // name of module exposed to Python
    "Demo Python wrapper for custom C extension library.", // module documentation
    -1,
    DemoLib_FunctionsTable
};

PyMODINIT_FUNC PyInit_demo(void) {
    return PyModule_Create(&DemoLib_Module);
}

(.env) [boris@sever33fedora enhancePython]$ cat setup.py
from setuptools import Extension, setup

module = Extension("demo",
                  sources=[
                    'demo.c',
                    'demolib.c'
                  ])
setup(name='demo',
     version='1.0',
     description='Python wrapper for custom C extension',
     ext_modules=[module])
     
(.env) [boris@sever33fedora enhancePython]$ python setup.py install

(.env) [boris@sever33fedora enhancePython]$ python MyProg.py
Callng sfactorial_sum("1234567") gives 
 5913
Calling ifactorial_sum([1,2,3,4,5,6,7]) gives 
 5913

Friday, June 18, 2021

GCC build of shared library to verify calling C-function from Python 3.9.5 module on Fedora 34 Linux (Server Edition)

 1) We are using "ctype" native Python module to load the shared library.

2) The ctype Python module is available starting with Python 2.5. Make sure you have the most recent Python installed on your system


 (.env) [boris@fedora33server CPYTHON]$ cat arithmatic.h
void connect();
int randNum();
int multNum(int a, int b);

(.env) [boris@fedora33server CPYTHON]$ cat arithmatic.c
#include <stdio.h>
#include <stdlib.h>
#include "arithmatic.h"
void connect()
{
   printf("Connected to C extension\n");
}
//return random value in range of 0-100
int randNum()
{
    int nRand = rand() % 100; 
    return nRand;
}
//Multiply two integer numbers and return value
int multNum(int a, int b)
{
    int nMult = a*b;
    return nMult;
}

That is supposed to run in one line, otherwise you have to place
"\" to let gcc know that the rest of line would go after cartridge return

(.env) [boris@fedora33server CPYTHON]$ gcc -shared -o libcalci.so -fPIC arithmatic.c

(.env) [boris@fedora33server CPYTHON]$ ll
total 28
-rw-rw-r--. 1 boris boris   291 Jun 18 19:23 arithmatic.c
-rw-rw-r--. 1 boris boris    57 Jun 18 18:34 arithmatic.h
-rwxrwxr-x. 1 boris boris 16280 Jun 18 19:31 libcalci.so
-rw-rw-r--. 1 boris boris   374 Jun 18 19:21 MyTest.py

(.env) [boris@fedora33server CPYTHON]$ cat  MyTest.py

from ctypes import *
libCalc = CDLL("./libcalci.so")
 
#call C function to check connection
libCalc.connect() 
 
#calling randNum() C function
#it returns random number
varRand = libCalc.randNum()
print ("Random Number:", varRand, type(varRand))
 
#calling multNum() C function
#it returns multiplication of two numbers
varMult = libCalc.multNum(38,27)
print ("Multiplication : ", varMult)


(.env) [boris@fedora33server CPYTHON]$ python  MyTest.py
Connected to C extension
Random Number: 83 <class 'int'>
Multiplication :  1026











A bit more complicated sample

(.env) [boris@fedora33server CDEVPY]$ cat sum.c
int our_function(int num_numbers, int *numbers) {
    int i;
    int sum = 0;
    for (i = 0; i < num_numbers; i++) {
        sum += numbers[i];
    }
    return sum;
}

(.env) [boris@fedora33server CDEVPY]$ gcc -fPIC -shared -o libsum.so sum.c

(.env) [boris@fedora33server CDEVPY]$ ll
total 28
-rwxrwxr-x. 1 boris boris 16160 Jun 18 21:35 libsum.so
-rw-rw-r--. 1 boris boris   107 Jun 18 21:33 MyRun.py
-rw-rw-r--. 1 boris boris   169 Jun 18 21:29 sum.c
-rw-rw-r--. 1 boris boris   345 Jun 18 21:29 sum.py

(.env) [boris@fedora33server CDEVPY]$ cat sum.py
import ctypes

_sum = ctypes.CDLL('./libsum.so')
_sum.our_function.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_int))

def our_function(numbers):
    global _sum
    num_numbers = len(numbers)
    array_type = ctypes.c_int * num_numbers
    result = _sum.our_function(ctypes.c_int(num_numbers), array_type(*numbers))
    return int(result)

(.env) [boris@fedora33server CDEVPY]$ cat  MyRun.py
import sum
def main():
    print (sum.our_function([11,2,-3,4,-3,6]))

if __name__=="__main__":
    main()
(.env) [boris@fedora33server CDEVPY]$ python  MyRun.py
17

(.env) [boris@fedora33server CDEVPY]$ ll
total 28
-rwxrwxr-x. 1 boris boris 16160 Jun 18 21:35 libsum.so
-rw-rw-r--. 1 boris boris   107 Jun 18 21:33 MyRun.py
drwxrwxr-x. 2 boris boris    32 Jun 18 21:36 __pycache__
-rw-rw-r--. 1 boris boris   169 Jun 18 21:29 sum.c
-rw-rw-r--. 1 boris boris   345 Jun 18 21:29 sum.py

PyCharm project based ctypes wrapper

Pretty short sample in C++

[boris@fedora33server ]$ ll
total 8
-rw-rw-r--. 1 boris boris 126 Jun 19 08:46 dullmath.cpp
-rw-rw-r--. 1 boris boris  79 Jun 19 08:52 MyProg.py

[boris@fedora33server ]$ cat dullmath.cpp
extern "C" int fibonacci(int n) {
  if (n <= 0) return 0;
  if (n == 1) return 1;
  return fibonacci(n-1) + fibonacci(n-2);
}

[boris@fedora33server]$ g++ -shared  dullmath.cpp -o   libdullmath.so

[boris@fedora33server ]$ ll
total 24
-rw-rw-r--. 1 boris boris   126 Jun 19 08:46 dullmath.cpp
-rwxrwxr-x. 1 boris boris 16176 Jun 19 08:52 libdullmath.so
-rw-rw-r--. 1 boris boris    79 Jun 19 08:52 MyProg.py

[boris@fedora33server]$ cat MyProg.py
import ctypes

lib = ctypes.CDLL(f'./libdullmath.so')
print(lib.fibonacci(11))

[boris@fedora33server ]$ python MyProg.py
89

One more sample

[boris@fedora33server ]$ cat function.c
int myFunction(int num)
{
    if (num == 0)
 
        // if number is 0, do not perform any operation.
        return 0;
    else
        // if number divides by 11, return 1 else return 0
          return (num % 11 == 0 ? 1 : 0) ;
 
}
[boris@fedora33server ]$ gcc -fPIC -shared -o libfun.so function.c
[boris@fedora33server ]$ ll libfun.so
-rwxrwxr-x. 1 boris boris 16168 Jun 19 10:31 libfun.so

[boris@fedora33server ]$ cat MyProg.py

import ctypes
  
test_text = input ("Введите число: ")
test_number = int(test_text)

# libfun loaded to the python file
# using fun.myFunction(),
# C function can be accessed
# but type of argument is the problem.
                        
fun = ctypes.CDLL("./libfun.so")  

# Now whenever argument
# will be passed to the function                                                       
# ctypes will check it.
           
fun.myFunction.argtypes = [ctypes.c_int]
 
returnVale = fun.myFunction(test_number)  
print(returnVale)

[boris@fedora33server ]$ python MyProg.py
Введите число: 121
1
[boris@fedora33server ]$ python MyProg.py
Введите число: 555
0
[boris@fedora33server ]$ python MyProg.py
Введите число: 550
1


REFERENCES