diff --git a/README.md b/README.md
index c99db2d..d4f2bc2 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,20 @@
## Work In Progress... 🚧
Enigma is a C/C++ based tensor framework designed for building dynamic neural networks with optimized tensor computations and GPU acceleration, featuring seamless **Python** bindings for easy integration and usage through a simple `enigma` import.
+
### Language Support:
-![C++](https://img.shields.io/badge/C%2B%2B-orange.svg) ![Python](https://img.shields.io/badge/Python-blue.svg)
+![C++](https://img.shields.io/badge/C%2B%2B-orange.svg) ![Python](https://img.shields.io/badge/Python-blue.svg)
+
+## Installation
+
+```bash
+pip install enigma
+```
Project Roadmap(Click Me)
@@ -150,4 +157,163 @@ Enigma is a C/C++ based tensor framework designed for building dynamic neural ne
- [ ] Include guidelines for contributing to the project.
---
+
+
+## Quick Start
+
+```python
+import enigma
+
+# Create scalars
+x = enigma.Scalar(42) # Integer
+y = enigma.Scalar(3.14) # Float
+z = enigma.Scalar(1 + 2j) # Complex
+b = enigma.Scalar(True) # Boolean
+
+# Basic arithmetic
+result = x + y # Automatic type promotion
+print(result) # 45.14
+```
+
+## Basic Usage
+
+### Creating Scalars
+
+```python
+import enigma
+
+# Different ways to create scalars
+i = enigma.Scalar(42) # Integer type
+f = enigma.Scalar(3.14) # Float type
+c = enigma.Scalar(1 + 2j) # Complex type
+b = enigma.Scalar(True) # Boolean type
+
+# Check types
+print(i.dtype) # ScalarType.Int64
+print(f.is_floating_point()) # True
+print(c.is_complex()) # True
+print(b.is_bool()) # True
+```
+
+### Arithmetic Operations
+
+```python
+# Basic arithmetic with automatic type promotion
+x = enigma.Scalar(10)
+y = enigma.Scalar(3)
+
+addition = x + y # 13
+subtraction = x - y # 7
+multiplication = x * y # 30
+division = x / y # 3.333... (promotes to float)
+
+# Mixed-type operations
+f = enigma.Scalar(3.14)
+result = x * f # 31.4 (float result)
+```
+
+### Type Conversion
+
+```python
+# Safe type conversions
+x = enigma.Scalar(42)
+as_float = x.to_float() # 42.0
+as_int = x.to_int() # 42
+as_bool = x.to_bool() # True
+
+# Error handling for invalid conversions
+try:
+ enigma.Scalar(3.14).to_int() # Will raise ScalarTypeError
+except enigma.ScalarTypeError as e:
+ print(f"Cannot convert: {e}")
+```
+
+### Type Promotion Rules
+
+```python
+# Check type promotion
+int_type = enigma.int64
+float_type = enigma.float64
+result_type = enigma.promote_types(int_type, float_type)
+print(result_type) # ScalarType.Float64
+
+# Automatic promotion in operations
+i = enigma.Scalar(5) # Int64
+f = enigma.Scalar(2.5) # Float64
+result = i + f # Result is Float64
+print(result.dtype) # ScalarType.Float64
+```
+
+### Error Handling
+
+```python
+try:
+ # Division by zero
+ result = enigma.Scalar(1) / enigma.Scalar(0)
+except enigma.ScalarError as e:
+ print(f"Error: {e}")
+
+try:
+ # Invalid type conversion
+ float_val = enigma.Scalar(3.14)
+ int_val = float_val.to_int() # Will raise ScalarTypeError
+except enigma.ScalarTypeError as e:
+ print(f"Conversion error: {e}")
+```
+
+### Complex Numbers
+
+```python
+# Working with complex numbers
+c1 = enigma.Scalar(1 + 2j)
+c2 = enigma.Scalar(2 - 1j)
+
+# Complex arithmetic
+sum_c = c1 + c2 # 3 + 1j
+prod_c = c1 * c2 # 4 + 3j
+
+# Converting to Python complex
+py_complex = c1.to_complex() # Get Python complex number
+print(py_complex.real) # 1.0
+print(py_complex.imag) # 2.0
+```
+
+### Type Safety
+
+```python
+# Strict type checking
+bool_val = enigma.Scalar(True)
+int_val = enigma.Scalar(1)
+
+# No implicit conversion between bool and int
+print(bool_val == int_val) # False
+
+# Check if casting is possible
+can_cast = enigma.can_cast(enigma.float64, enigma.int64)
+print(can_cast) # False (can't safely cast float to int)
+```
+
+### Comparisons
+
+```python
+# Value comparisons
+a = enigma.Scalar(42)
+b = enigma.Scalar(42.0)
+c = enigma.Scalar(43)
+
+print(a == b) # True (same value, different types)
+print(a != c) # True
+```
+
+## Advanced Features
+
+### Epsilon Comparisons for Floating Point
+
+```python
+x = enigma.Scalar(0.1 + 0.2)
+y = enigma.Scalar(0.3)
+
+# Automatically handles floating point precision
+print(x == y) # True
+```
diff --git a/examples/scalar_examples.py b/examples/scalar_examples.py
new file mode 100644
index 0000000..5729611
--- /dev/null
+++ b/examples/scalar_examples.py
@@ -0,0 +1,164 @@
+import enigma
+from typing import Any
+
+
+def print_section(title: str) -> None:
+ """Helper to print formatted section titles."""
+ print(f"\n{'='*50}")
+ print(f" {title}")
+ print(f"{'='*50}\n")
+
+
+def demonstrate_basic_creation():
+ """Demonstrate basic scalar creation and type inference."""
+ print_section("Basic Scalar Creation")
+
+ # Different ways to create scalars
+ examples = [
+ (42, "Integer"),
+ (3.14, "Float"),
+ (True, "Boolean"),
+ (1 + 2j, "Complex"),
+ ]
+
+ for value, name in examples:
+ scalar = enigma.Scalar(value)
+ print(f"{name:8} | Value: {scalar} | Type: {scalar.dtype}")
+
+ # Show default construction
+ default_scalar = enigma.Scalar()
+ print(f"\nDefault | Value: {default_scalar} | Type: {
+ default_scalar.dtype}")
+
+
+def demonstrate_type_checking():
+ """Demonstrate type checking methods."""
+ print_section("Type Checking")
+
+ scalars = {
+ "Integer": enigma.Scalar(42),
+ "Float": enigma.Scalar(3.14),
+ "Complex": enigma.Scalar(1 + 2j),
+ "Boolean": enigma.Scalar(True)
+ }
+
+ for name, scalar in scalars.items():
+ print(f"{name:8} is:")
+ print(f" Integral? {scalar.is_integral()}")
+ print(f" Floating? {scalar.is_floating_point()}")
+ print(f" Complex? {scalar.is_complex()}")
+ print(f" Boolean? {scalar.is_bool()}\n")
+
+
+def demonstrate_arithmetic():
+ """Demonstrate arithmetic operations."""
+ print_section("Arithmetic Operations")
+
+ # Basic arithmetic
+ a = enigma.Scalar(10)
+ b = enigma.Scalar(3)
+
+ print("Integer operations:")
+ print(f"10 + 3 = {a + b}")
+ print(f"10 - 3 = {a - b}")
+ print(f"10 * 3 = {a * b}")
+ print(f"10 / 3 = {a / b}") # Note: Division promotes to float
+
+ # Mixed-type arithmetic
+ c = enigma.Scalar(3.14)
+ print("\nMixed-type operations:")
+ print(f"10 + 3.14 = {a + c}")
+ print(f"10 * 3.14 = {a * c}")
+
+ # Complex arithmetic
+ d = enigma.Scalar(1 + 1j)
+ print("\nComplex operations:")
+ print(f"(1 + 1j) * 3.14 = {d * c}")
+
+
+def demonstrate_type_conversion():
+ """Demonstrate type conversion capabilities."""
+ print_section("Type Conversion")
+
+ # Safe conversions
+ scalar = enigma.Scalar(42)
+ print(f"Original: {scalar} (type: {scalar.dtype})")
+ print(f"To float: {scalar.to_float()} (explicit conversion)")
+ print(f"To int : {scalar.to_int()}")
+ print(f"To bool : {scalar.to_bool()}")
+
+ # Conversion errors
+ print("\nDemonstrating conversion errors:")
+ try:
+ enigma.Scalar(3.14).to_int()
+ except enigma.ScalarTypeError as e:
+ print(f"Expected error: {e}")
+
+
+def demonstrate_type_promotion():
+ """Demonstrate type promotion rules."""
+ print_section("Type Promotion")
+
+ cases = [
+ (enigma.int64, enigma.float64),
+ (enigma.float32, enigma.float64),
+ (enigma.int32, enigma.complex64),
+ ]
+
+ for type1, type2 in cases:
+ promoted = enigma.promote_types(type1, type2)
+ print(f"{type1} + {type2} → {promoted}")
+
+
+def demonstrate_error_handling():
+ """Demonstrate error handling."""
+ print_section("Error Handling")
+ examples = [
+ # Division by zero
+ lambda: enigma.Scalar(1) / enigma.Scalar(0),
+ # Invalid conversion
+ lambda: enigma.Scalar(3.14).to_int(),
+ # Complex to real conversion with imaginary part
+ lambda: enigma.Scalar(1 + 1j).to_float(),
+ ]
+
+ for i, example in enumerate(examples, 1):
+ try:
+ example()
+ except enigma.ScalarTypeError as e:
+ print(f"Example {i}: {type(e).__name__}: {e}")
+
+
+def demonstrate_comparisons():
+ """Demonstrate comparison operations."""
+ print_section("Comparisons")
+
+ a = enigma.Scalar(42)
+ b = enigma.Scalar(42.0)
+ c = enigma.Scalar(43)
+
+ print(f"42 == 42.0: {a == b}")
+ print(f"42 != 43 : {a != c}")
+
+ # Show type-aware comparisons
+ print(f"\nType-aware comparisons:")
+ print(f"Scalar(1) == Scalar(True): {
+ enigma.Scalar(1) == enigma.Scalar(True)}")
+
+
+def main():
+ """Run all demonstrations."""
+ print("\nEnigma Scalar Library Demonstration")
+ print("Version:", enigma.__version__, "\n")
+
+ demonstrate_basic_creation()
+ demonstrate_type_checking()
+ demonstrate_arithmetic()
+ demonstrate_type_conversion()
+ demonstrate_type_promotion()
+ demonstrate_error_handling()
+ demonstrate_comparisons()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/meson.build b/meson.build
index cf1d6f5..48e0ad6 100644
--- a/meson.build
+++ b/meson.build
@@ -28,6 +28,34 @@ enigma_lib = static_library('enigma',
cpp_args: cpp_args
)
+# Python extension module
+py_mod = import('python')
+py3 = py_mod.find_installation('python3', pure: false) # Set pure to false
+py3_dep = py3.dependency()
+
+pybind11_dep = dependency('pybind11', required: true)
+
+py3.extension_module('_enigma',
+ 'python/src/bindings.cpp',
+ include_directories: inc_dir,
+ link_with: enigma_lib,
+ dependencies: [pybind11_dep, py3_dep],
+ cpp_args: cpp_args,
+ install: true,
+ install_dir: py3.get_install_dir() / 'enigma' # Specify install directory
+)
+
+# Install Python package files
+python_files = [
+ 'python/enigma/__init__.py',
+]
+
+py3.install_sources(
+ python_files,
+ pure: false, # Set pure to false
+ subdir: 'enigma'
+)
+
# Test Dependencies
gtest_proj = subproject('gtest')
gtest_dep = gtest_proj.get_variable('gtest_dep')
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..09fe0a0
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,14 @@
+[build-system]
+requires = [
+ "meson>=1.3.2",
+ "meson-python",
+ "setuptools>=42",
+ "wheel",
+ "pybind11>=2.6.0",
+]
+build-backend = "mesonpy"
+
+[tool.pytest.ini_options]
+testpaths = ["python/tests"]
+python_files = ["test_*.py"]
+addopts = "-v --tb=short"
\ No newline at end of file
diff --git a/python/enigma/__init__.py b/python/enigma/__init__.py
new file mode 100644
index 0000000..4e1beec
--- /dev/null
+++ b/python/enigma/__init__.py
@@ -0,0 +1,27 @@
+from ._enigma import (
+ Scalar,
+ ScalarType,
+ ScalarError,
+ ScalarTypeError,
+ get_dtype,
+ promote_types,
+ can_cast,
+)
+
+
+int8 = ScalarType.int8
+int16 = ScalarType.int16
+int32 = ScalarType.int32
+int64 = ScalarType.int64
+uint8 = ScalarType.uint8
+uint16 = ScalarType.uint16
+uint32 = ScalarType.uint32
+uint64 = ScalarType.uint64
+float32 = ScalarType.float32
+float64 = ScalarType.float64
+complex64 = ScalarType.complex64
+complex128 = ScalarType.complex128
+bool_ = ScalarType.bool
+
+# Version info
+__version__ = "0.0.1"
diff --git a/python/src/bindings.cpp b/python/src/bindings.cpp
new file mode 100644
index 0000000..f57fe15
--- /dev/null
+++ b/python/src/bindings.cpp
@@ -0,0 +1,188 @@
+// python/src/bindings.cpp
+#include
+#include
+#include
+#include "Scalar.h"
+#include "DEBUG.h"
+
+namespace py = pybind11;
+using namespace enigma;
+
+// Helper function to convert Python numeric types to Scalar
+Scalar py_to_scalar(const py::object &obj)
+{
+ if (py::isinstance(obj))
+ {
+ return Scalar(obj.cast());
+ }
+ if (py::isinstance(obj))
+ {
+ return Scalar(obj.cast());
+ }
+ if (py::isinstance(obj))
+ {
+ return Scalar(obj.cast());
+ }
+ if (PyComplex_Check(obj.ptr()))
+ {
+ std::complex c(
+ PyComplex_RealAsDouble(obj.ptr()),
+ PyComplex_ImagAsDouble(obj.ptr()));
+ return Scalar(c);
+ }
+
+ // Try to extract Scalar from Python object
+ try
+ {
+ return obj.cast();
+ }
+ catch (const py::cast_error &)
+ {
+ throw py::type_error("Cannot convert Python object to Scalar");
+ }
+}
+
+// Convert Scalar to appropriate Python type
+py::object scalar_to_py(const Scalar &scalar)
+{
+ if (scalar.isBoolean())
+ {
+ return py::bool_(scalar.to());
+ }
+ if (scalar.isIntegral())
+ {
+ return py::int_(scalar.to());
+ }
+ if (scalar.isFloatingPoint())
+ {
+ return py::float_(scalar.to());
+ }
+ if (scalar.isComplex())
+ {
+ auto c = scalar.to>();
+ return py::reinterpret_steal(
+ PyComplex_FromDoubles(c.real(), c.imag()));
+ }
+ throw py::type_error("Unknown Scalar type");
+}
+
+PYBIND11_MODULE(_enigma, m)
+{
+ // Create the module
+ m.doc() = "Python bindings for the Enigma tensor framework";
+
+ // Register exception translations
+ py::register_exception(m, "ScalarError");
+ py::register_exception(m, "ScalarTypeError");
+
+ // Register ScalarType enum
+ py::enum_(m, "ScalarType")
+ .value("int8", ScalarType::Int8)
+ .value("int16", ScalarType::Int16)
+ .value("int32", ScalarType::Int32)
+ .value("int64", ScalarType::Int64)
+ .value("uint8", ScalarType::UInt8)
+ .value("uint16", ScalarType::UInt16)
+ .value("uint32", ScalarType::UInt32)
+ .value("uint64", ScalarType::UInt64)
+ .value("float32", ScalarType::Float32)
+ .value("float64", ScalarType::Float64)
+ .value("complex64", ScalarType::Complex64)
+ .value("complex128", ScalarType::Complex128)
+ .value("bool", ScalarType::Bool)
+ .export_values();
+
+ // Register Scalar class
+ py::class_(m, "Scalar")
+ // Constructors
+ .def(py::init<>())
+ .def(py::init([](const py::object &value, py::object dtype)
+ {
+ if (!dtype.is_none()) {
+ auto scalar_type = dtype.cast();
+ // TODO: Implement explicit dtype conversion
+ throw py::type_error("Explicit dtype not yet implemented");
+ }
+ return py_to_scalar(value); }),
+ py::arg("value"), py::arg("dtype") = py::none())
+
+ // Type checking methods
+ .def_property_readonly("dtype", &Scalar::type)
+ .def("is_floating_point", &Scalar::isFloatingPoint)
+ .def("is_integral", &Scalar::isIntegral)
+ .def("is_complex", &Scalar::isComplex)
+ .def("is_bool", &Scalar::isBoolean)
+
+ // Conversion methods
+ .def("to_float", [](const Scalar &self)
+ { return self.to(); })
+ .def("to_int", [](const Scalar &self)
+ { return self.to(); })
+ .def("to_bool", [](const Scalar &self)
+ { return self.to(); })
+ .def("to_complex", [](const Scalar &self)
+ { return self.to>(); })
+
+ // String representation
+ .def("__str__", &Scalar::toString)
+ .def("__repr__", [](const Scalar &self)
+ { return "enigma.Scalar(" + self.toString() + ")"; })
+
+ // Arithmetic operators
+ .def("__add__", [](const Scalar &self, const py::object &other)
+ {
+ if (py::isinstance(other)) {
+ return self + other.cast();
+ }
+ return self + py_to_scalar(other); })
+ .def("__sub__", [](const Scalar &self, const py::object &other)
+ {
+ if (py::isinstance(other)) {
+ return self - other.cast();
+ }
+ return self - py_to_scalar(other); })
+ .def("__mul__", [](const Scalar &self, const py::object &other)
+ {
+ if (py::isinstance(other)) {
+ return self * other.cast();
+ }
+ return self * py_to_scalar(other); })
+ .def("__truediv__", [](const Scalar &self, const py::object &other)
+ {
+ if (py::isinstance(other)) {
+ return self / other.cast();
+ }
+ return self / py_to_scalar(other); })
+ .def("__neg__", [](const Scalar &self)
+ { return -self; })
+
+ // Reverse operators
+ .def("__radd__", [](const Scalar &self, const py::object &other)
+ { return py_to_scalar(other) + self; })
+ .def("__rsub__", [](const Scalar &self, const py::object &other)
+ { return py_to_scalar(other) - self; })
+ .def("__rmul__", [](const Scalar &self, const py::object &other)
+ { return py_to_scalar(other) * self; })
+ .def("__rtruediv__", [](const Scalar &self, const py::object &other)
+ { return py_to_scalar(other) / self; })
+
+ // Comparison operators
+ .def("__eq__", [](const Scalar &self, const py::object &other)
+ {
+ if (py::isinstance(other)) {
+ return self == other.cast();
+ }
+ return self == py_to_scalar(other); })
+ .def("__ne__", [](const Scalar &self, const py::object &other)
+ {
+ if (py::isinstance(other)) {
+ return self != other.cast();
+ }
+ return self != py_to_scalar(other); });
+
+ // Module-level functions
+ m.def("get_dtype", [](const Scalar &scalar)
+ { return scalar.type(); });
+ m.def("promote_types", &Scalar::promoteTypes);
+ m.def("can_cast", &Scalar::canCast);
+}
\ No newline at end of file
diff --git a/python/tests/test_scalar.py b/python/tests/test_scalar.py
new file mode 100644
index 0000000..7d0f010
--- /dev/null
+++ b/python/tests/test_scalar.py
@@ -0,0 +1,209 @@
+# python/tests/test_scalar.py
+import pytest
+import enigma
+import math
+import time
+from typing import Callable
+
+
+class TestScalar:
+ # Helper constant
+ EPSILON = 1e-7
+
+ def approx_equal(self, a: float, b: float) -> bool:
+ return abs(a - b) < self.EPSILON
+
+ def test_default_construction(self):
+ """Test default construction of Scalar"""
+ s = enigma.Scalar()
+ assert s.dtype == enigma.float64
+ assert self.approx_equal(s.to_float(), 0.0)
+
+ def test_type_construction(self):
+ """Test construction with different types"""
+ # Integer construction
+ s1 = enigma.Scalar(42)
+ assert s1.dtype == enigma.int64
+ assert s1.to_int() == 42
+
+ # Float construction
+ s2 = enigma.Scalar(3.14)
+ assert s2.dtype == enigma.float64
+ assert self.approx_equal(s2.to_float(), 3.14)
+
+ # Boolean construction
+ s3 = enigma.Scalar(True)
+ assert s3.dtype == enigma.bool_
+ assert s3.to_bool()
+
+ # Complex construction
+ s4 = enigma.Scalar(1.0 + 2.0j)
+ assert s4.dtype == enigma.complex128
+
+ def test_type_checking(self):
+ """Test type checking methods"""
+ i = enigma.Scalar(42)
+ assert i.is_integral()
+ assert not i.is_floating_point()
+ assert not i.is_complex()
+ assert not i.is_bool()
+
+ f = enigma.Scalar(3.14)
+ assert not f.is_integral()
+ assert f.is_floating_point()
+ assert not f.is_complex()
+ assert not f.is_bool()
+
+ c = enigma.Scalar(1.0 + 2.0j)
+ assert not c.is_integral()
+ assert not c.is_floating_point()
+ assert c.is_complex()
+ assert not c.is_bool()
+
+ b = enigma.Scalar(True)
+ assert not b.is_integral()
+ assert not b.is_floating_point()
+ assert not b.is_complex()
+ assert b.is_bool()
+
+ def test_numeric_conversions(self):
+ """Test numeric type conversions"""
+ # Int to Float
+ i = enigma.Scalar(42)
+ assert self.approx_equal(i.to_float(), 42.0)
+
+ # Float to Int (when possible)
+ f = enigma.Scalar(42.0)
+ assert f.to_int() == 42
+
+ # Bool to numeric
+ b = enigma.Scalar(True)
+ assert b.to_int() == 1
+ assert self.approx_equal(b.to_float(), 1.0)
+
+ # Complex to real (when possible)
+ c = enigma.Scalar(1.0 + 0.0j)
+ assert self.approx_equal(c.to_float(), 1.0)
+
+ def test_conversion_errors(self):
+ """Test conversion error cases"""
+ # Float with fractional part to int
+ f = enigma.Scalar(3.14)
+ with pytest.raises(enigma.ScalarTypeError):
+ f.to_int()
+
+ # Complex with imaginary part to real
+ c = enigma.Scalar(1.0 + 2.0j)
+ with pytest.raises(enigma.ScalarTypeError):
+ c.to_float()
+
+ def test_basic_arithmetic(self):
+ """Test basic arithmetic operations"""
+ # Integer arithmetic
+ i1, i2 = enigma.Scalar(42), enigma.Scalar(8)
+ assert (i1 + i2).to_int() == 50
+ assert (i1 - i2).to_int() == 34
+ assert (i1 * i2).to_int() == 336
+ assert self.approx_equal((i1 / i2).to_float(), 5.25)
+
+ # Floating point arithmetic
+ f1, f2 = enigma.Scalar(3.14), enigma.Scalar(2.0)
+ assert self.approx_equal((f1 + f2).to_float(), 5.14)
+ assert self.approx_equal((f1 - f2).to_float(), 1.14)
+ assert self.approx_equal((f1 * f2).to_float(), 6.28)
+ assert self.approx_equal((f1 / f2).to_float(), 1.57)
+
+ # Complex arithmetic
+ c1 = enigma.Scalar(1.0 + 2.0j)
+ c2 = enigma.Scalar(2.0 - 1.0j)
+ result = (c1 + c2).to_complex()
+ assert self.approx_equal(result.real, 3.0)
+ assert self.approx_equal(result.imag, 1.0)
+
+ def test_mixed_type_operations(self):
+ """Test operations between different types"""
+ i = enigma.Scalar(42)
+ f = enigma.Scalar(3.14)
+ c = enigma.Scalar(1.0 + 2.0j)
+
+ # Int + Float
+ result = (i + f).to_float()
+ assert self.approx_equal(result, 45.14)
+
+ # Float + Complex
+ result = (f + c).to_complex()
+ assert self.approx_equal(result.real, 4.14)
+ assert self.approx_equal(result.imag, 2.0)
+
+ # Int * Float
+ result = (i * f).to_float()
+ assert self.approx_equal(result, 131.88)
+
+ def test_division_operations(self):
+ """Test division operations"""
+ # Basic division
+ assert self.approx_equal(
+ (enigma.Scalar(6) / enigma.Scalar(2)).to_float(), 3.0)
+ assert self.approx_equal(
+ (enigma.Scalar(3.14) / enigma.Scalar(2.0)).to_float(), 1.57)
+
+ # Division by zero
+ with pytest.raises(enigma.ScalarTypeError):
+ enigma.Scalar(1) / enigma.Scalar(0)
+ with pytest.raises(enigma.ScalarTypeError):
+ enigma.Scalar(1.0) / enigma.Scalar(0.0)
+
+ def test_comparison_operations(self):
+ """Test comparison operations"""
+ # Same type comparisons
+ assert enigma.Scalar(42) == enigma.Scalar(42)
+ assert enigma.Scalar(3.14159) == enigma.Scalar(3.14159)
+ assert enigma.Scalar(True) == enigma.Scalar(True)
+
+ # Mixed type comparisons
+ assert enigma.Scalar(42) == enigma.Scalar(42.0)
+
+ # Boolean comparisons (strict)
+ assert not (enigma.Scalar(1) == enigma.Scalar(True))
+ assert not (enigma.Scalar(0) == enigma.Scalar(False))
+
+ def test_type_promotion(self):
+ """Test type promotion rules"""
+ # Same type promotion
+ assert enigma.promote_types(enigma.int64, enigma.int64) == enigma.int64
+
+ # Complex promotion
+ assert enigma.promote_types(
+ enigma.float64, enigma.complex64) == enigma.complex128
+ assert enigma.promote_types(
+ enigma.int64, enigma.complex64) == enigma.complex128
+
+ # Float promotion
+ assert enigma.promote_types(
+ enigma.int64, enigma.float64) == enigma.float64
+
+ def test_type_casting(self):
+ """Test type casting capabilities"""
+ # Same type casting
+ assert enigma.can_cast(enigma.int64, enigma.int64)
+
+ # Complex casting
+ assert not enigma.can_cast(enigma.complex64, enigma.float64)
+ assert enigma.can_cast(enigma.float64, enigma.complex64)
+
+ # Float casting
+ assert not enigma.can_cast(enigma.float64, enigma.int64)
+ assert enigma.can_cast(enigma.int64, enigma.float64)
+
+ def test_string_representation(self):
+ """Test string conversion"""
+ assert str(enigma.Scalar(42)) == "42"
+ assert str(enigma.Scalar(True)) == "true"
+
+ # Floating point precision
+ f = enigma.Scalar(3.14159265359)
+ assert "3.14159" in str(f)
+
+
+if __name__ == "__main__":
+ pytest.main([__file__])
diff --git a/reinstall.sh b/reinstall.sh
new file mode 100755
index 0000000..b502ee5
--- /dev/null
+++ b/reinstall.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -xeuo pipefail
+IFS=$'\n\t'
+
+if [ -d "build/" ]
+then
+ rm -r build
+fi
+
+#meson setup build -Db_sanitize=address,undefined
+python -m pip uninstall -y enigma
+# python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug"
+python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v'
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..44737cf
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,21 @@
+from setuptools import setup, find_packages
+
+setup(
+ name="enigma",
+ version="0.0.1",
+ packages=find_packages(),
+ install_requires=[],
+ author="Swayam Singh",
+ author_email="singhswayam008@gmail.com",
+ description="Python bindings for the Enigma tensor framework",
+ long_description=open("README.md").read(),
+ long_description_content_type="text/markdown",
+ url="https://github.com/swayaminsync/enigma",
+ classifiers=[
+ "Programming Language :: Python :: 3",
+ "Programming Language :: C++",
+ "License :: OSI Approved :: Apache Software License",
+ "Operating System :: OS Independent",
+ ],
+ python_requires=">=3.8",
+)
diff --git a/test.sh b/test.sh
deleted file mode 100644
index b66233b..0000000
--- a/test.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-meson compile -C build
-meson test -C build -v
-
-: '
-gdb build/storage_cow_test
-(gdb) run
-(gdb) bt
-'
\ No newline at end of file