From 2971ad04834d2d787c3433b7d9073be691c2b1a5 Mon Sep 17 00:00:00 2001 From: Madhu18S Date: Wed, 8 Jul 2026 08:11:35 +0530 Subject: [PATCH 1/4] backend: integrate initial calcium qqbar infrastructure and arithmetic hooks --- .gitignore | 1 + src/flint/types/qqbar.pxd | 21 +++++++++++++++++++++ src/flint/types/qqbar.pyx | 27 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/flint/types/qqbar.pxd create mode 100644 src/flint/types/qqbar.pyx diff --git a/.gitignore b/.gitignore index 075ec3ce..392b7644 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ MANIFEST *.DS_Store .venv .hypothesis +venv/ diff --git a/src/flint/types/qqbar.pxd b/src/flint/types/qqbar.pxd new file mode 100644 index 00000000..36f919e0 --- /dev/null +++ b/src/flint/types/qqbar.pxd @@ -0,0 +1,21 @@ +# src/flint/types/qqbar.pxd +# src/flint/types/qqbar.pxd +from flint.flint_base.flint_base cimport flint_ctx, flint_scalar + +# Note: In python-flint's repo layout, flint_base is flat in the flint/ folder, +# so we cimport directly from 'flint.flint_base' rather than subdirectories. + +cdef extern from "flint/qqbar.h": + ctypedef struct qqbar_struct: + # Leaving this block completely blank or using a mock field + int _dummy + ctypedef qqbar_struct qqbar_t[1] + + # Core lifetime and initialization C signatures + void qqbar_init(qqbar_t res) + void qqbar_clear(qqbar_t res) + void qqbar_set(qqbar_t res, const qqbar_t x) + +cdef class qqbar(flint_scalar): + cdef qqbar_t val + cpdef int is_rational(self) diff --git a/src/flint/types/qqbar.pyx b/src/flint/types/qqbar.pyx new file mode 100644 index 00000000..a42cf6c2 --- /dev/null +++ b/src/flint/types/qqbar.pyx @@ -0,0 +1,27 @@ +# src/flint/types/qqbar.pyx +from flint.flint_base.flint_base cimport flint_scalar +cdef class qqbar(flint_scalar): + """ + Python wrapper class for FLINT's qqbar_t (Algebraic Numbers Fields) + """ + def __cinit__(self): + # Always safe-allocate raw C pointers inside __cinit__ + qqbar_init(self.val) + + def __dealloc__(self): + # Prevent memory leaks when Python cleans up the object instance + qqbar_clear(self.val) + + def __init__(self, val=None): + if val is not None: + if isinstance(val, qqbar): + qqbar_set(self.val, (val).val) + else: + raise TypeError("Cannot initialize qqbar type from input") + + cpdef int is_rational(self): + # Example method shell pointing to underlying properties + return 0 + + def __repr__(self): + return "Algebraic Number (qqbar)" \ No newline at end of file From 0fe76b427acb645f5e5e096c23998d69f490786e Mon Sep 17 00:00:00 2001 From: Madhu18S Date: Wed, 8 Jul 2026 22:44:31 +0530 Subject: [PATCH 2/4] feat: implement qqbar string formatting via POSIX stdout capture and add multi-type interoperability for fmpz/fmpq --- src/flint/types/qqbar.pxd | 31 ++++++++++---- src/flint/types/qqbar.pyx | 87 +++++++++++++++++++++++++++++++++++---- 2 files changed, 102 insertions(+), 16 deletions(-) diff --git a/src/flint/types/qqbar.pxd b/src/flint/types/qqbar.pxd index 36f919e0..caa21f81 100644 --- a/src/flint/types/qqbar.pxd +++ b/src/flint/types/qqbar.pxd @@ -1,21 +1,34 @@ # src/flint/types/qqbar.pxd -# src/flint/types/qqbar.pxd from flint.flint_base.flint_base cimport flint_ctx, flint_scalar +from flint.flintlib.types.flint cimport slong +from flint.flintlib.functions.fmpz cimport fmpz_t +from flint.flintlib.functions.fmpq cimport fmpq_t -# Note: In python-flint's repo layout, flint_base is flat in the flint/ folder, -# so we cimport directly from 'flint.flint_base' rather than subdirectories. -cdef extern from "flint/qqbar.h": +cdef extern from "flint/qqbar.h" nogil: ctypedef struct qqbar_struct: - # Leaving this block completely blank or using a mock field - int _dummy + pass ctypedef qqbar_struct qqbar_t[1] - # Core lifetime and initialization C signatures + # Lifecycle methods void qqbar_init(qqbar_t res) void qqbar_clear(qqbar_t res) void qqbar_set(qqbar_t res, const qqbar_t x) + void qqbar_set_si(qqbar_t res, slong x) + + # Printing methods + void qqbar_print(const qqbar_t x) + void qqbar_printn(const qqbar_t x, slong n) + + # Interoperability setters + void qqbar_set_fmpz(qqbar_t res, const fmpz_t x) + void qqbar_set_fmpq(qqbar_t res, const fmpq_t x) + + # Arithmetic operations + void qqbar_add(qqbar_t res, const qqbar_t x, const qqbar_t y) + void qqbar_sub(qqbar_t res, const qqbar_t x, const qqbar_t y) + void qqbar_mul(qqbar_t res, const qqbar_t x, const qqbar_t y) + void qqbar_div(qqbar_t res, const qqbar_t x, const qqbar_t y) cdef class qqbar(flint_scalar): - cdef qqbar_t val - cpdef int is_rational(self) + cdef qqbar_t val \ No newline at end of file diff --git a/src/flint/types/qqbar.pyx b/src/flint/types/qqbar.pyx index a42cf6c2..5ee5dce5 100644 --- a/src/flint/types/qqbar.pyx +++ b/src/flint/types/qqbar.pyx @@ -1,27 +1,100 @@ # src/flint/types/qqbar.pyx from flint.flint_base.flint_base cimport flint_scalar +from flint.types.fmpz cimport fmpz +from flint.types.fmpq cimport fmpq +from flint.types.qqbar cimport ( + qqbar_init, qqbar_clear, qqbar_set, qqbar_set_si, + qqbar_add, qqbar_sub, qqbar_mul, qqbar_div, qqbar +) + cdef class qqbar(flint_scalar): """ - Python wrapper class for FLINT's qqbar_t (Algebraic Numbers Fields) + The qqbar class represents algebraic numbers inside python-flint. """ + def __cinit__(self): - # Always safe-allocate raw C pointers inside __cinit__ qqbar_init(self.val) def __dealloc__(self): - # Prevent memory leaks when Python cleans up the object instance qqbar_clear(self.val) def __init__(self, val=None): if val is not None: if isinstance(val, qqbar): qqbar_set(self.val, (val).val) + elif isinstance(val, int): + qqbar_set_si(self.val, val) + elif isinstance(val, fmpz): + qqbar_set_fmpz(self.val, (val).val) + elif isinstance(val, fmpq): + qqbar_set_fmpq(self.val, (val).val) else: raise TypeError("Cannot initialize qqbar type from input") - cpdef int is_rational(self): - # Example method shell pointing to underlying properties - return 0 + # Explicit Python Operator Overloads + def __add__(x, y): + cdef qqbar res = qqbar() + cdef qqbar c_x = x if isinstance(x, qqbar) else qqbar(x) + cdef qqbar c_y = y if isinstance(y, qqbar) else qqbar(y) + qqbar_add(res.val, c_x.val, c_y.val) + return res + + def __sub__(x, y): + cdef qqbar res = qqbar() + cdef qqbar c_x = x if isinstance(x, qqbar) else qqbar(x) + cdef qqbar c_y = y if isinstance(y, qqbar) else qqbar(y) + qqbar_sub(res.val, c_x.val, c_y.val) + return res + + def __mul__(x, y): + cdef qqbar res = qqbar() + cdef qqbar c_x = x if isinstance(x, qqbar) else qqbar(x) + cdef qqbar c_y = y if isinstance(y, qqbar) else qqbar(y) + qqbar_mul(res.val, c_x.val, c_y.val) + return res + + def __truediv__(x, y): + cdef qqbar res = qqbar() + cdef qqbar c_x = x if isinstance(x, qqbar) else qqbar(x) + cdef qqbar c_y = y if isinstance(y, qqbar) else qqbar(y) + qqbar_div(res.val, c_x.val, c_y.val) + return res + def str(self, Py_ssize_t digits=15): + """ + Converts the algebraic number to a decimal string representation by capturing C-level stdout. + """ + import os + import sys + + # Save current stdout file descriptor + cdef int stdout_fd = 1 + cdef int saved_stdout = os.dup(stdout_fd) + + # Create a pipe to catch stream output + pipe_read, pipe_write = os.pipe() + + # Redirect stdout to our pipe + os.dup2(pipe_write, stdout_fd) + + try: + # Execute raw C-level printing function + qqbar_printn(self.val, digits) + sys.stdout.flush() + finally: + # Revert stdout back to original descriptor + os.close(pipe_write) + os.dup2(saved_stdout, stdout_fd) + os.close(saved_stdout) + + # Read the captured string from the pipe + cdef bytes captured = os.read(pipe_read, 4096) + os.close(pipe_read) + + return captured.decode('utf-8').strip() + + def __str__(self): + return self.str() def __repr__(self): - return "Algebraic Number (qqbar)" \ No newline at end of file + # Displays a clean numerical representation + return f"qqbar({self.str()})" \ No newline at end of file From edb2242ebff90aa823ef62dfa12c83753d35c31e Mon Sep 17 00:00:00 2001 From: Madhu18S Date: Wed, 8 Jul 2026 23:14:51 +0530 Subject: [PATCH 3/4] style: fix trailing whitespaces and newlines for cython-lint compliance --- src/flint/types/qqbar.pxd | 9 ++-- src/flint/types/qqbar.pyx | 93 +++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/flint/types/qqbar.pxd b/src/flint/types/qqbar.pxd index caa21f81..1c3ec34b 100644 --- a/src/flint/types/qqbar.pxd +++ b/src/flint/types/qqbar.pxd @@ -1,10 +1,7 @@ -# src/flint/types/qqbar.pxd -from flint.flint_base.flint_base cimport flint_ctx, flint_scalar from flint.flintlib.types.flint cimport slong from flint.flintlib.functions.fmpz cimport fmpz_t from flint.flintlib.functions.fmpq cimport fmpq_t - cdef extern from "flint/qqbar.h" nogil: ctypedef struct qqbar_struct: pass @@ -15,7 +12,7 @@ cdef extern from "flint/qqbar.h" nogil: void qqbar_clear(qqbar_t res) void qqbar_set(qqbar_t res, const qqbar_t x) void qqbar_set_si(qqbar_t res, slong x) - + # Printing methods void qqbar_print(const qqbar_t x) void qqbar_printn(const qqbar_t x, slong n) @@ -30,5 +27,7 @@ cdef extern from "flint/qqbar.h" nogil: void qqbar_mul(qqbar_t res, const qqbar_t x, const qqbar_t y) void qqbar_div(qqbar_t res, const qqbar_t x, const qqbar_t y) +from flint.flint_base.flint_base cimport flint_scalar + cdef class qqbar(flint_scalar): - cdef qqbar_t val \ No newline at end of file + cdef qqbar_t val diff --git a/src/flint/types/qqbar.pyx b/src/flint/types/qqbar.pyx index 5ee5dce5..725bf768 100644 --- a/src/flint/types/qqbar.pyx +++ b/src/flint/types/qqbar.pyx @@ -1,15 +1,22 @@ -# src/flint/types/qqbar.pyx -from flint.flint_base.flint_base cimport flint_scalar +from flint.flintlib.functions.qqbar cimport ( + qqbar_init, + qqbar_clear, + qqbar_set, + qqbar_set_si, + qqbar_printn, + qqbar_set_fmpz, + qqbar_set_fmpq, + qqbar_add, + qqbar_sub, + qqbar_mul, + qqbar_div +) from flint.types.fmpz cimport fmpz from flint.types.fmpq cimport fmpq -from flint.types.qqbar cimport ( - qqbar_init, qqbar_clear, qqbar_set, qqbar_set_si, - qqbar_add, qqbar_sub, qqbar_mul, qqbar_div, qqbar -) cdef class qqbar(flint_scalar): """ - The qqbar class represents algebraic numbers inside python-flint. + The qqbar class represents algebraic numbers. """ def __cinit__(self): @@ -31,7 +38,38 @@ cdef class qqbar(flint_scalar): else: raise TypeError("Cannot initialize qqbar type from input") - # Explicit Python Operator Overloads + def str(self, Py_ssize_t digits=15): + """ + Converts the algebraic number to a decimal string representation by capturing C-level stdout. + """ + import os + import sys + + cdef int stdout_fd = 1 + cdef int saved_stdout = os.dup(stdout_fd) + + pipe_read, pipe_write = os.pipe() + os.dup2(pipe_write, stdout_fd) + + try: + qqbar_printn(self.val, digits) + sys.stdout.flush() + finally: + os.close(pipe_write) + os.dup2(saved_stdout, stdout_fd) + os.close(saved_stdout) + + cdef bytes captured = os.read(pipe_read, 4096) + os.close(pipe_read) + + return captured.decode('utf-8').strip() + + def __str__(self): + return self.str() + + def __repr__(self): + return f"qqbar({self.str()})" + def __add__(x, y): cdef qqbar res = qqbar() cdef qqbar c_x = x if isinstance(x, qqbar) else qqbar(x) @@ -59,42 +97,3 @@ cdef class qqbar(flint_scalar): cdef qqbar c_y = y if isinstance(y, qqbar) else qqbar(y) qqbar_div(res.val, c_x.val, c_y.val) return res - def str(self, Py_ssize_t digits=15): - """ - Converts the algebraic number to a decimal string representation by capturing C-level stdout. - """ - import os - import sys - - # Save current stdout file descriptor - cdef int stdout_fd = 1 - cdef int saved_stdout = os.dup(stdout_fd) - - # Create a pipe to catch stream output - pipe_read, pipe_write = os.pipe() - - # Redirect stdout to our pipe - os.dup2(pipe_write, stdout_fd) - - try: - # Execute raw C-level printing function - qqbar_printn(self.val, digits) - sys.stdout.flush() - finally: - # Revert stdout back to original descriptor - os.close(pipe_write) - os.dup2(saved_stdout, stdout_fd) - os.close(saved_stdout) - - # Read the captured string from the pipe - cdef bytes captured = os.read(pipe_read, 4096) - os.close(pipe_read) - - return captured.decode('utf-8').strip() - - def __str__(self): - return self.str() - - def __repr__(self): - # Displays a clean numerical representation - return f"qqbar({self.str()})" \ No newline at end of file From 4d3b2af3d699b49eaaaeb83d3af3d1cdd19bdbbb Mon Sep 17 00:00:00 2001 From: Madhu18S Date: Thu, 9 Jul 2026 08:26:09 +0530 Subject: [PATCH 4/4] refactor: replace low-level os pipe capture with cross-platform io.StringIO --- src/flint/types/qqbar.pyx | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/src/flint/types/qqbar.pyx b/src/flint/types/qqbar.pyx index 725bf768..f331415d 100644 --- a/src/flint/types/qqbar.pyx +++ b/src/flint/types/qqbar.pyx @@ -1,16 +1,3 @@ -from flint.flintlib.functions.qqbar cimport ( - qqbar_init, - qqbar_clear, - qqbar_set, - qqbar_set_si, - qqbar_printn, - qqbar_set_fmpz, - qqbar_set_fmpq, - qqbar_add, - qqbar_sub, - qqbar_mul, - qqbar_div -) from flint.types.fmpz cimport fmpz from flint.types.fmpq cimport fmpq @@ -40,29 +27,23 @@ cdef class qqbar(flint_scalar): def str(self, Py_ssize_t digits=15): """ - Converts the algebraic number to a decimal string representation by capturing C-level stdout. + Converts the algebraic number to a decimal string representation. """ - import os import sys + import io - cdef int stdout_fd = 1 - cdef int saved_stdout = os.dup(stdout_fd) - - pipe_read, pipe_write = os.pipe() - os.dup2(pipe_write, stdout_fd) + # Temporarily intercept stdout using a clean, cross-platform string buffer + old_stdout = sys.stdout + sys.stdout = io.StringIO() try: qqbar_printn(self.val, digits) sys.stdout.flush() + output = sys.stdout.getvalue() finally: - os.close(pipe_write) - os.dup2(saved_stdout, stdout_fd) - os.close(saved_stdout) - - cdef bytes captured = os.read(pipe_read, 4096) - os.close(pipe_read) + sys.stdout = old_stdout - return captured.decode('utf-8').strip() + return output.strip() def __str__(self): return self.str()