# 9. Python C Extensions

These examples demonstrate creating Python extensions in C for tasks requiring performance optimizations, such as mathematical computations, array processing, and string manipulations.

***

**1. Simple "Hello, World!" C Extension**

**C Code (`hello.c`):**

```c
#include <Python.h>

static PyObject* hello_world(PyObject* self, PyObject* args) {
    printf("Hello, World!\n");
    Py_RETURN_NONE;
}

static PyMethodDef HelloMethods[] = {
    {"hello_world", hello_world, METH_VARARGS, "Prints Hello, World!"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef hellomodule = {
    PyModuleDef_HEAD_INIT,
    "hello",
    NULL,
    -1,
    HelloMethods
};

PyMODINIT_FUNC PyInit_hello(void) {
    return PyModule_Create(&hellomodule);
}
```

**Python Setup Script (`setup.py`):**

```python
from setuptools import setup, Extension

module = Extension('hello', sources=['hello.c'])

setup(
    name='HelloWorld',
    version='1.0',
    description='A simple C extension for Python',
    ext_modules=[module],
)
```

**Build and Install:**

```bash
python setup.py build
python setup.py install
```

**Python Usage:**

```python
import hello
hello.hello_world()  # Output: Hello, World!
```

***

**2. Adding Two Numbers**

**C Code (`add.c`):**

```c
#include <Python.h>

static PyObject* add(PyObject* self, PyObject* args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;
    return PyLong_FromLong(a + b);
}

static PyMethodDef AddMethods[] = {
    {"add", add, METH_VARARGS, "Add two numbers"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef addmodule = {
    PyModuleDef_HEAD_INIT,
    "add",
    NULL,
    -1,
    AddMethods
};

PyMODINIT_FUNC PyInit_add(void) {
    return PyModule_Create(&addmodule);
}
```

**Python Usage:**

```python
import add
print(add.add(3, 5))  # Output: 8
```

***

**3. Computing Factorial**

**C Code (`factorial.c`):**

```c
#include <Python.h>

static PyObject* factorial(PyObject* self, PyObject* args) {
    int n;
    if (!PyArg_ParseTuple(args, "i", &n))
        return NULL;
    
    long result = 1;
    for (int i = 1; i <= n; i++) {
        result *= i;
    }
    return PyLong_FromLong(result);
}

static PyMethodDef FactorialMethods[] = {
    {"factorial", factorial, METH_VARARGS, "Calculate factorial"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef factorialmodule = {
    PyModuleDef_HEAD_INIT,
    "factorial",
    NULL,
    -1,
    FactorialMethods
};

PyMODINIT_FUNC PyInit_factorial(void) {
    return PyModule_Create(&factorialmodule);
}
```

***

**4. String Reversal**

```c
static PyObject* reverse_string(PyObject* self, PyObject* args) {
    const char* input;
    if (!PyArg_ParseTuple(args, "s", &input))
        return NULL;

    int len = strlen(input);
    char* reversed = malloc(len + 1);

    for (int i = 0; i < len; i++) {
        reversed[i] = input[len - i - 1];
    }
    reversed[len] = '\0';

    PyObject* result = PyUnicode_FromString(reversed);
    free(reversed);
    return result;
}
```

***

**5. Dot Product of Two Arrays**

```c
#include <Python.h>

static PyObject* dot_product(PyObject* self, PyObject* args) {
    PyObject *list1, *list2;
    if (!PyArg_ParseTuple(args, "OO", &list1, &list2))
        return NULL;

    int len1 = PyList_Size(list1);
    int len2 = PyList_Size(list2);

    if (len1 != len2)
        return NULL;

    double dot = 0.0;
    for (int i = 0; i < len1; i++) {
        double x = PyFloat_AsDouble(PyList_GetItem(list1, i));
        double y = PyFloat_AsDouble(PyList_GetItem(list2, i));
        dot += x * y;
    }
    return PyFloat_FromDouble(dot);
}
```

***

**6. Finding Maximum Value in an Array**

```c
#include <Python.h>

static PyObject* find_max(PyObject* self, PyObject* args) {
    PyObject* list;
    if (!PyArg_ParseTuple(args, "O", &list))
        return NULL;

    int len = PyList_Size(list);
    double max_val = PyFloat_AsDouble(PyList_GetItem(list, 0));
    
    for (int i = 1; i < len; i++) {
        double val = PyFloat_AsDouble(PyList_GetItem(list, i));
        if (val > max_val)
            max_val = val;
    }
    return PyFloat_FromDouble(max_val);
}
```

***

**7. Summing an Array**

```c
#include <Python.h>

static PyObject* sum_array(PyObject* self, PyObject* args) {
    PyObject* list;
    if (!PyArg_ParseTuple(args, "O", &list))
        return NULL;

    int len = PyList_Size(list);
    double sum = 0.0;

    for (int i = 0; i < len; i++) {
        sum += PyFloat_AsDouble(PyList_GetItem(list, i));
    }
    return PyFloat_FromDouble(sum);
}
```

***

**8. Converting Fahrenheit to Celsius**

```c
#include <Python.h>

static PyObject* fahrenheit_to_celsius(PyObject* self, PyObject* args) {
    double fahrenheit;
    if (!PyArg_ParseTuple(args, "d", &fahrenheit))
        return NULL;

    double celsius = (fahrenheit - 32) * 5.0 / 9.0;
    return PyFloat_FromDouble(celsius);
}
```

***

**9. Matrix Multiplication**

```c
#include <Python.h>

static PyObject* matrix_multiply(PyObject* self, PyObject* args) {
    PyObject *matrix1, *matrix2;
    if (!PyArg_ParseTuple(args, "OO", &matrix1, &matrix2))
        return NULL;

    // Implement matrix multiplication logic here...
}
```

***

**10. Simple Linear Search**

```c
#include <Python.h>

static PyObject* linear_search(PyObject* self, PyObject* args) {
    PyObject* list;
    double target;

    if (!PyArg_ParseTuple(args, "Od", &list, &target))
        return NULL;

    int len = PyList_Size(list);
    for (int i = 0; i < len; i++) {
        if (PyFloat_AsDouble(PyList_GetItem(list, i)) == target) {
            return PyLong_FromLong(i);
        }
    }
    Py_RETURN_NONE;
}
```

***
