Saturday, March 9, 2013

n-math.py: Man vs. Machine - Updated 2025! - Auto-Sharpness Fix! Server & Risc-V/Asm Clients :o


I was posed the question
With the preposition of y > z < x > 1
 Solve for x where zx + x = y

Naturally, as maths would in simple terms dictate the answer:
x = y/z+1

Computers on the other hand would go through the convoluted length of controlling hyperbolic paraboloids as:
   [memory] int / ( [+increment] [(pointer to mem)] )   
x = [1 / z] * y / (1 + [(1/z)])

I only question it?


I welcome feedback, conjecture and teachings; Please see the client's below the server program in both assembly and risc-v.

Here's a possible solution space which allows for nearest-neighbour deviation handling of data corroboration.

##n-math.py Server#

import socket
import struct
import numpy as np
import threading

def hyperbolic_parabolic_interpolation_nd_revised(all_fy_data, all_fx_data, x_interp):
    """
    Performs hyperbolic-parabolic interpolation on n-dimensional data, with
    sharpness dynamically adjusted by the standard deviation of nearest neighbors.

    Args:
        all_fy_data (list of numpy.ndarray): A list of y-data arrays.
        all_fx_data (list of numpy.ndarray): A list of corresponding x-data arrays.
        x_interp (numpy.ndarray): Array of x-values for interpolation.

    Returns:
        numpy.ndarray: Concatenated array of interpolated y-values.
    """
    all_interp_y = []
    num_dimensions = len(all_fy_data)

    if len(all_fx_data) != num_dimensions:
        raise ValueError("The number of x-data arrays must match the number of y-data arrays.")

    for fx, fy in zip(all_fx_data, all_fy_data):
        try:
            if len(fx) != len(fy) or len(fx) < 3:
                raise ValueError("X and Y data must have equal length and at least three points for this interpolation.")

            interp_y = []
            for x in x_interp:
                # Find the three closest data points
                distances = np.abs(fx - x)
                closest_indices = np.argsort(distances)[:3]
                x_closest = fx[closest_indices]
                y_closest = fy[closest_indices]
                sorted_indices = np.argsort(x_closest)
                x1, x2, x3 = x_closest[sorted_indices]
                y1, y2, y3 = y_closest[sorted_indices]

                # Dynamically adjust sharpness based on the standard deviation of nearest neighbors
                # We normalize the std dev to be a small positive number to avoid extreme sharpness values
                if len(y_closest) < 2:
                    std_dev = 0
                else:
                    std_dev = np.std(y_closest)
                
                # A higher standard deviation suggests more noise/scatter, so we want to smooth the interpolation
                # by reducing the sharpness. We use a reciprocal or inverse relationship.
                # Adding a small epsilon to the denominator prevents division by zero.
                dynamic_sharpness = 1.0 / (1.0 + std_dev + 1e-9)

                # Parabolic interpolation
                if np.isclose(x1, x2):
                    a_p = 0
                    b_p = (y3 - y1) / (x3 - x1) if not np.isclose(x3, x1) else 0
                    c_p = y1 - b_p * x1
                else:
                    a_p = ((y3 - y1) / (x3 - x1) - (y2 - y1) / (x2 - x1)) / (x3 - x2) if not np.isclose(x3, x2) else 0
                    b_p = (y2 - y1) / (x2 - x1) - a_p * (x1 + x2) if not np.isclose(x2, x1) else 0
                    c_p = y1 - b_p * x1 - a_p * x1**2
                y_parabolic = a_p * x**2 + b_p * x + c_p

                # Hyperbolic interpolation (using a simple form)
                if np.isclose(x2 - x1, 0) or np.isclose(x3 - x2, 0):
                    y_hyperbolic = y2
                else:
                    k1 = (y2 - y1) / (x2 - x1)
                    k2 = (y3 - y2) / (x3 - x2)
                    if np.isclose(k1, k2):
                        y_hyperbolic = y1 + k1 * (x - x1)
                    else:
                        A = (k2 - k1) / (x3 - x1)
                        y_hyperbolic = y1 + k1 * (x - x1) + A * (x - x1) * (x - x2)

                # Blend the two interpolations using the dynamic sharpness
                # The blend factor logic remains, but now `sharpness` is `dynamic_sharpness`
                blend_factor = 1 / (1 + np.exp(-dynamic_sharpness * (np.abs(x - x2) - 0.5 * (x3 - x1))))
                interp_y_val = (1 - blend_factor) * y_parabolic + blend_factor * y_hyperbolic
                interp_y.append(interp_y_val)

            all_interp_y.extend(interp_y)

        except ValueError as e:
            raise ValueError(str(e))
        except Exception as e:
            raise Exception(f"An unexpected error occurred during interpolation for one dimension: {e}")

    return np.array(all_interp_y)

def handle_client(client_socket, addr):
    """
    Handles communication with a single client, receiving n-dimensional float data
    and sending back hyperbolic-parabolic interpolated results.
    Expects a leading byte (always 2 for this refactored version),
    followed by the number of data dimensions, then for each dimension:
    the number of floats in the x array, the x array, the number of floats in the y array, the y array.
    Finally, it expects the number of interpolation x values and the interpolation x values.
    """
    print(f"Handling client: {addr}")
    try:
        # Receive operation code (expecting 2 for hyperbolic-parabolic)
        operation_code_bytes = client_socket.recv(1)
        if not operation_code_bytes:
            print(f"Client {addr} disconnected unexpectedly (no operation code).")
            return
        operation_code = struct.unpack('!B', operation_code_bytes)[0]
        if operation_code != 2:
            error_message = f"Invalid operation code for hyperbolic-parabolic interpolation: {operation_code}".encode('utf-8')
            client_socket.sendall(struct.pack('!I', len(error_message)))
            client_socket.sendall(error_message)
            print(f"Client {addr} sent an invalid operation code: {operation_code}")
            return

        # Receive the number of data dimensions
        num_dimensions_bytes = client_socket.recv(4)
        if not num_dimensions_bytes:
            print(f"Client {addr} disconnected unexpectedly (no number of dimensions).")
            return
        num_dimensions = struct.unpack('!I', num_dimensions_bytes)[0]

        all_fx_data = []
        all_fy_data = []

        for dim in range(num_dimensions):
            # Receive the number of floats in the x array for this dimension
            num_fx_bytes = client_socket.recv(4)
            if not num_fx_bytes:
                print(f"Client {addr} disconnected unexpectedly (no length for x data in dimension {dim+1}).")
                return
            num_fx = struct.unpack('!I', num_fx_bytes)[0]

            # Receive the x float data for this dimension
            fx_bytes = b''
            expected_fx_bytes = num_fx * 4
            while len(fx_bytes) < expected_fx_bytes:
                chunk = client_socket.recv(4096)
                if not chunk:
                    print(f"Client {addr} disconnected unexpectedly (incomplete x data in dimension {dim+1}).")
                    return
                fx_bytes += chunk
            fx_data = np.array(struct.unpack(f'!{num_fx}f', fx_bytes))
            all_fx_data.append(fx_data)

            # Receive the number of floats in the y array for this dimension
            num_fy_bytes = client_socket.recv(4)
            if not num_fy_bytes:
                print(f"Client {addr} disconnected unexpectedly (no length for y data in dimension {dim+1}).")
                return
            num_fy = struct.unpack('!I', num_fy_bytes)[0]

            # Receive the y float data for this dimension
            fy_bytes = b''
            expected_fy_bytes = num_fy * 4
            while len(fy_bytes) < expected_fy_bytes:
                chunk = client_socket.recv(4096)
                if not chunk:
                    print(f"Client {addr} disconnected unexpectedly (incomplete y data in dimension {dim+1}).")
                    return
                fy_bytes += chunk
            fy_data = np.array(struct.unpack(f'!{num_fy}f', fy_bytes))
            all_fy_data.append(fy_data)

        # Receive the number of interpolation x values
        interp_x_count_bytes = client_socket.recv(4)
        if not interp_x_count_bytes:
            print(f"Client {addr} disconnected unexpectedly (no length for interpolation x).")
            return
        interp_x_count = struct.unpack('!I', interp_x_count_bytes)[0]

        # Receive the interpolation x values
        interp_x_bytes = b''
        expected_interp_x_bytes = interp_x_count * 4
        while len(interp_x_bytes) < expected_interp_x_bytes:
            chunk = client_socket.recv(4096)
            if not chunk:
                print(f"Client {addr} disconnected unexpectedly (incomplete interpolation x).")
                return
            interp_x_bytes += chunk
        interp_x_data = np.array(struct.unpack(f'!{interp_x_count}f', interp_x_bytes))

        # Perform hyperbolic-parabolic interpolation with the revised function
        # Note: The sharpness parameter is no longer needed here.
        result = hyperbolic_parabolic_interpolation_nd_revised(all_fy_data, all_fx_data, interp_x_data)

        # Send the float result back to the client
        result_bytes = struct.pack(f'!{len(result)}f', *result)
        result_length = len(result_bytes)
        client_socket.sendall(struct.pack('!I', result_length))
        client_socket.sendall(result_bytes)

    except ValueError as e:
        print(f"ValueError on server from {addr}: {e}")
        error_message = str(e).encode('utf-8')
        client_socket.sendall(struct.pack('!I', len(error_message)))
        client_socket.sendall(error_message)
    except ConnectionResetError:
        print(f"Client {addr} forcibly closed the connection.")
    except Exception as e:
        print(f"An unexpected error occurred in handle_client for {addr}: {e}")
    finally:
        client_socket.close()
        print(f"Connection with client {addr} closed.")

def start_server(host, port):
    """
    Starts a server to listen for incoming n-dimensional float data streams for
    hyperbolic-parabolic interpolation.
    Handles each client connection in a separate thread.
    """
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((host, port))
    server_socket.listen(5)
    print(f"Server listening on {host}:{port}")

    try:
        while True:
            client_socket, addr = server_socket.accept()
            print(f"Accepted connection from {addr}")
            client_thread = threading.Thread(target=handle_client, args=(client_socket, addr))
            client_thread.start()
    except KeyboardInterrupt:
        print("\nServer shutting down...")
    finally:
        server_socket.close()
        print("Server socket closed.")

if __name__ == "__main__":
    SERVER_HOST = '127.0.0.1'
    SERVER_PORT = 12345
    start_server(SERVER_HOST, SERVER_PORT)




##Assembly client#

section .data
    msg_handling db "Handling client: ", 0
    msg_disconnected_op_code db "Client %d.%d.%d.%d disconnected unexpectedly (no operation code).", 0
    msg_invalid_op_code db "Invalid operation code for hyperbolic-parabolic interpolation: %u", 0
    msg_disconnected_num_dim db "Client %d.%d.%d.%d disconnected unexpectedly (no number of dimensions).", 0
    msg_disconnected_x_len db "Client %d.%d.%d.%d disconnected unexpectedly (no length for x data in dimension %u).", 0
    msg_disconnected_incomplete_x db "Client %d.%d.%d.%d disconnected unexpectedly (incomplete x data in dimension %u).", 0
    msg_disconnected_y_len db "Client %d.%d.%d.%d disconnected unexpectedly (no length for y data in dimension %u).", 0
    msg_disconnected_incomplete_y db "Client %d.%d.%d.%d disconnected unexpectedly (incomplete y data in dimension %u).", 0
    msg_disconnected_interp_x_len db "Client %d.%d.%d.%d disconnected unexpectedly (no length for interpolation x).", 0
    msg_disconnected_incomplete_interp_x db "Client %d.%d.%d.%d disconnected unexpectedly (incomplete interpolation x).", 0
    msg_value_error db "ValueError on server from %d.%d.%d.%d: %s", 0
    msg_connection_reset db "Client %d.%d.%d.%d forcibly closed the connection.", 0
    msg_unexpected_error db "An unexpected error occurred in handle_client for %d.%d.%d.%d: %s", 0
    msg_connection_closed db "Connection with client %d.%d.%d.%d closed.", 0
    op_code_expected dw 2 ; Expecting operation code 2
    float_size dw 4
    chunk_size dw 4096
    struct_unpack_fmt_b db "!B", 0
    struct_unpack_fmt_i db "!I", 0
    struct_unpack_fmt_f db "!%uf", 0 ; Placeholder for number of floats
    struct_pack_fmt_i db "!I", 0
    struct_pack_fmt_f db "!%uf", 0 ; Placeholder for number of floats

section .bss
    client_address resd 4 ; To store client IP address
    num_dimensions_received resd 1
    num_fx_received resd 1
    num_fy_received resd 1
    num_interp_x_received resd 1
    fx_data_buffer resb 16384 ; Adjust size as needed
    fy_data_buffer resb 16384 ; Adjust size as needed
    interp_x_data_buffer resb 16384 ; Adjust size as needed
    error_message_buffer resb 256 ; For storing error messages
    result_length_send resd 1
    result_buffer resb 16384 ; Adjust size as needed

extern printf
extern recv
extern send
extern close
extern struct_unpack
extern struct_pack
extern strlen
extern strcpy
extern hyperbolic_parabolic_interpolation_nd

section .text
    global handle_client

handle_client:
    push ebp
    mov ebp, esp
    push ebx
    push esi
    push edi
    push ebp ; For backtrace

    mov esi, [ebp+8] ; client_socket
    mov edi, [ebp+12] ; addr (pointer to sockaddr_in)

    ; Extract client IP address for logging
    mov eax, [edi+4] ; sin_addr
    mov [client_address], eax

    ; Print "Handling client: "
    push msg_handling
    call printf
    add esp, 4

    ; Print client address
    push dword [client_address+0]
    push dword [client_address+1]
    push dword [client_address+2]
    push dword [client_address+3]
    push format_ip
    call printf
    add esp, 16

    ; Receive operation code (1 byte)
    push 1
    push esi
    push operation_code_buffer
    call recv
    add esp, 12
    cmp eax, 1
    jl .client_disconnected_op_code

    push operation_code_buffer
    push struct_unpack_fmt_b
    push operation_code_received
    call struct_unpack
    add esp, 12

    cmp byte [operation_code_received], word [op_code_expected]
    jne .invalid_op_code

    ; Receive the number of data dimensions (4 bytes)
    push 4
    push esi
    push num_dimensions_buffer
    call recv
    add esp, 12
    cmp eax, 4
    jl .client_disconnected_num_dim

    push num_dimensions_buffer
    push struct_unpack_fmt_i
    push dword [num_dimensions_received]
    call struct_unpack
    add esp, 12

    mov ecx, [num_dimensions_received] ; Loop through dimensions
    mov ebx, 0 ; Dimension counter

.dimension_loop:
    cmp ebx, ecx
    jge .receive_interp_x_count

    inc ebx

    ; Receive the number of floats in the x array for this dimension (4 bytes)
    push 4
    push esi
    push num_fx_buffer
    call recv
    add esp, 12
    cmp eax, 4
    jl .client_disconnected_x_len

    push num_fx_buffer
    push struct_unpack_fmt_i
    push dword [num_fx_received]
    call struct_unpack
    add esp, 12

    mov esi, [ebp+8] ; Reset socket for recv
    mov eax, [num_fx_received]
    mov edx, word [float_size]
    mul edx ; Expected number of bytes for x data
    mov [expected_bytes], eax
    mov edi, fx_data_buffer
    call receive_all

    cmp eax, [expected_bytes]
    jnz .client_disconnected_incomplete_x

    ; Receive the number of floats in the y array for this dimension (4 bytes)
    push 4
    push esi
    push num_fy_buffer
    call recv
    add esp, 12
    cmp eax, 4
    jl .client_disconnected_y_len

    push num_fy_buffer
    push struct_unpack_fmt_i
    push dword [num_fy_received]
    call struct_unpack
    add esp, 12

    mov esi, [ebp+8] ; Reset socket for recv
    mov eax, [num_fy_received]
    mov edx, word [float_size]
    mul edx ; Expected number of bytes for y data
    mov [expected_bytes], eax
    mov edi, fy_data_buffer
    call receive_all

    cmp eax, [expected_bytes]
    jnz .client_disconnected_incomplete_y

    ; TODO: Store fx_data_buffer and fy_data_buffer pointers for interpolation

    jmp .dimension_loop

.receive_interp_x_count:
    ; Receive the number of interpolation x values (4 bytes)
    push 4
    push esi
    push num_interp_x_buffer
    call recv
    add esp, 12
    cmp eax, 4
    jl .client_disconnected_interp_x_len

    push num_interp_x_buffer
    push struct_unpack_fmt_i
    push dword [num_interp_x_received]
    call struct_unpack
    add esp, 12

    mov esi, [ebp+8] ; Reset socket for recv
    mov eax, [num_interp_x_received]
    mov edx, word [float_size]
    mul edx ; Expected number of bytes for interpolation x data
    mov [expected_bytes], eax
    mov edi, interp_x_data_buffer
    call receive_all

    cmp eax, [expected_bytes]
    jnz .client_disconnected_incomplete_interp_x

    ; TODO: Call the hyperbolic_parabolic_interpolation_nd function
    ; Need to set up arguments according to the C calling convention
    ; This will involve pushing pointers to the received data, the number of dimensions, etc.

    push dword [num_interp_x_received] ; Length of x_interp
    push interp_x_data_buffer ; Pointer to x_interp

    ; Need to reconstruct the all_fx_data and all_fy_data lists
    ; This will require knowing the lengths of each array within these lists
    ; The Python code iterates through dimensions, so we need to do something similar

    ; This part is complex and requires careful memory management and passing of data structures
    ; A direct assembly implementation of the Python list of numpy arrays is non-trivial

    ; Placeholder for calling the interpolation function
    ; push dword [num_dimensions_received]
    ; push pointer to all_fy_data
    ; push pointer to all_fx_data
    ; call hyperbolic_parabolic_interpolation_nd
    ; add esp, ... ; Clean up stack

    ; Assume the result is now in result_buffer and its length is in result_length_received

    ; Send the float result back to the client
    mov eax, [result_length_received]
    push eax ; Length of result
    push struct_pack_fmt_i
    push result_length_send
    call struct_pack
    add esp, 12

    push dword [result_length_send]
    push esi
    push dword [result_length_send]
    call send
    add esp, 12
    cmp eax, dword [result_length_send]
    jnz .connection_error_send_result_len

    push dword [result_length_received]
    mov ecx, eax
    push esi
    push result_buffer
    push ecx
    call send
    add esp, 12
    cmp eax, ecx
    jnz .connection_error_send_result

    jmp .cleanup

.invalid_op_code:
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push dword [operation_code_received]
    push msg_invalid_op_code
    call printf
    add esp, 20
    jmp .cleanup_socket

.client_disconnected_op_code:
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_op_code
    call printf
    add esp, 16
    jmp .cleanup_socket

.client_disconnected_num_dim:
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_num_dim
    call printf
    add esp, 16
    jmp .cleanup_socket

.client_disconnected_x_len:
    push ebx ; Dimension number
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_x_len
    call printf
    add esp, 20
    jmp .cleanup_socket

.client_disconnected_incomplete_x:
    push ebx ; Dimension number
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_incomplete_x
    call printf
    add esp, 20
    jmp .cleanup_socket

.client_disconnected_y_len:
    push ebx ; Dimension number
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_y_len
    call printf
    add esp, 20
    jmp .cleanup_socket

.client_disconnected_incomplete_y:
    push ebx ; Dimension number
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_incomplete_y
    call printf
    add esp, 20
    jmp .cleanup_socket

.client_disconnected_interp_x_len:
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_interp_x_len
    call printf
    add esp, 16
    jmp .cleanup_socket

.client_disconnected_incomplete_interp_x:
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_disconnected_incomplete_interp_x
    call printf
    add esp, 16
    jmp .cleanup_socket

.value_error:
    ; Assume error message is in error_message_buffer
    push dword [error_message_length]
    push error_message_buffer
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_value_error
    call printf
    add esp, 20
    jmp .cleanup_socket

.connection_reset_error:
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_connection_reset
    call printf
    add esp, 16
    jmp .cleanup_socket

.unexpected_error:
    ; Assume error message is in error_message_buffer
    push dword [error_message_length]
    push error_message_buffer
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_unexpected_error
    call printf
    add esp, 20
    jmp .cleanup_socket

.connection_error_send_result_len:
    ; Handle error sending result length
    jmp .cleanup_socket

.connection_error_send_result:
    ; Handle error sending result data
    jmp .cleanup_socket

.cleanup_socket:
    push dword [esi]
    call close
    add esp, 4

.cleanup:
    push dword [client_address+3]
    push dword [client_address+2]
    push dword [client_address+1]
    push dword [client_address+0]
    push msg_connection_closed
    call printf
    add esp, 16

    pop ebp
    pop edi
    pop esi
    pop ebx
    pop ebp
    ret

; Helper function to receive a specific number of bytes
receive_all:
    push ebp
    mov ebp, esp
    mov edi, [ebp+8] ; Buffer
    mov esi, [ebp+12] ; Socket
    mov ecx, [expected_bytes] ; Number of bytes to receive
    xor eax, eax ; Total bytes received

.receive_loop:
    cmp eax, ecx
    jge .receive_done
    push ecx
    sub ecx, eax ; Remaining bytes
    cmp ecx, word [chunk_size]
    jle .receive_chunk_size
    mov ecx, word [chunk_size]
.receive_chunk_size:
    push ecx
    push esi
    push edi
    call recv
    add esp, 12
    cmp eax, 0
    jle .receive_error ; Connection closed
    add edi, eax
    add eax, [ebp-4] ; Add to total received
    mov [ebp-4], eax
    pop ecx
    jmp .receive_loop

.receive_done:
    mov eax, [ebp-4] ; Return total bytes received
    jmp .receive_exit

.receive_error:
    mov eax, -1
    jmp .receive_exit

.receive_exit:
    mov esp, ebp
    pop ebp
    ret

section .bss
    operation_code_buffer resb 1
    num_dimensions_buffer resb 4
    num_fx_buffer resb 4
    num_fy

## Risc-V Client#
 
# RiscV assembly translation of the provided x86 code.
# This code assumes the standard RISC-V calling convention (registers a0-a7 for arguments).
# It uses the s-registers (s0-s11) as callee-saved registers.

.data
    # String literals for logging messages.
    msg_handling: .asciz "Handling client: %s\n"
    msg_disconnected_op_code: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (no operation code).\n"
    msg_invalid_op_code: .asciz "Invalid operation code for hyperbolic-parabolic interpolation: %u\n"
    msg_disconnected_num_dim: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (no number of dimensions).\n"
    msg_disconnected_x_len: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (no length for x data in dimension %u).\n"
    msg_disconnected_incomplete_x: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (incomplete x data in dimension %u).\n"
    msg_disconnected_y_len: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (no length for y data in dimension %u).\n"
    msg_disconnected_incomplete_y: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (incomplete y data in dimension %u).\n"
    msg_disconnected_interp_x_len: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (no length for interpolation x).\n"
    msg_disconnected_incomplete_interp_x: .asciz "Client %u.%u.%u.%u disconnected unexpectedly (incomplete interpolation x).\n"
    msg_value_error: .asciz "ValueError on server from %u.%u.%u.%u: %s\n"
    msg_connection_reset: .asciz "Client %u.%u.%u.%u forcibly closed the connection.\n"
    msg_unexpected_error: .asciz "An unexpected error occurred in handle_client for %u.%u.%u.%u: %s\n"
    msg_connection_closed: .asciz "Connection with client %u.%u.%u.%u closed.\n"
    
    # Constants and format strings.
    op_code_expected: .word 2
    float_size: .word 4
    chunk_size: .word 4096
    
    # The format specifiers below need to be managed by a higher-level library
    # like Python's struct. They are represented as static strings here but
    # would be passed as arguments to the equivalent function calls.
    struct_unpack_fmt_b: .asciz "!B"
    struct_unpack_fmt_i: .asciz "!I"
    struct_unpack_fmt_f: .asciz "!%uf"
    struct_pack_fmt_i: .asciz "!I"
    struct_pack_fmt_f: .asciz "!%uf"
    format_ip: .asciz "%u.%u.%u.%u" # A format string for printing the IP address

.bss
    # Variables and buffers. The x86 'resd' and 'resb' are translated to '.space'.
    client_address: .space 16 # To store client IP address (4 words)
    num_dimensions_received: .space 4
    num_fx_received: .space 4
    num_fy_received: .space 4
    num_interp_x_received: .space 4
    fx_data_buffer: .space 16384 # Adjust size as needed
    fy_data_buffer: .space 16384 # Adjust size as needed
    interp_x_data_buffer: .space 16384 # Adjust size as needed
    error_message_buffer: .space 256 # For storing error messages
    result_length_send: .space 4
    result_buffer: .space 16384 # Adjust size as needed
    
    # Buffers for single value receives
    operation_code_buffer: .space 1
    num_dimensions_buffer: .space 4
    num_fx_buffer: .space 4
    num_fy_buffer: .space 4
    num_interp_x_buffer: .space 4
    
    # Variables used in the receive_all helper function
    expected_bytes: .space 4
    
.text
    .global handle_client

handle_client:
    # RISC-V function prologue.
    # Save the frame pointer (s0) and return address (ra).
    addi sp, sp, -32       # Adjust stack pointer to make space for saved registers
    sd ra, 24(sp)          # Store return address
    sd s0, 16(sp)          # Store frame pointer
    sd s1, 8(sp)           # Store callee-saved register s1 (used for socket)
    sd s2, 0(sp)           # Store callee-saved register s2 (used for address)
    addi s0, sp, 32        # Set up frame pointer

    # The original x86 code used stack arguments.
    # We assume the RISC-V equivalent passes arguments in registers.
    # a0 = client_socket (was [ebp+8])
    # a1 = addr (pointer to sockaddr_in) (was [ebp+12])
    
    # We will use s1 for the client socket and s2 for the address pointer.
    mv s1, a0              # s1 = client_socket
    mv s2, a1              # s2 = addr

    # Extract client IP address for logging.
    # The x86 code assumes a specific structure for sockaddr_in.
    lw t0, 4(s2)           # t0 = sin_addr
    sw t0, client_address  # Store sin_addr in client_address
    
    # Print "Handling client: "
    la a0, msg_handling
    jal ra, printf
    
    # Print client address
    la a0, format_ip
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    jal ra, printf
    
    # Receive operation code (1 byte)
    li a2, 1               # Number of bytes to receive
    mv a1, s1              # Client socket
    la a0, operation_code_buffer # Buffer
    jal ra, recv
    li t0, 1               # Expected bytes
    blt a0, t0, .client_disconnected_op_code
    
    # Unpack the received byte
    la a0, struct_unpack_fmt_b
    li a1, 1               # Number of bytes
    la a2, operation_code_buffer
    la a3, operation_code_received
    jal ra, struct_unpack

    # Compare received op code with expected op code
    lb t0, operation_code_received # Load byte
    lw t1, op_code_expected      # Load expected word
    bne t0, t1, .invalid_op_code

    # Receive the number of data dimensions (4 bytes)
    li a2, 4
    mv a1, s1
    la a0, num_dimensions_buffer
    jal ra, recv
    li t0, 4
    blt a0, t0, .client_disconnected_num_dim

    # Unpack number of dimensions
    la a0, struct_unpack_fmt_i
    li a1, 4
    la a2, num_dimensions_buffer
    la a3, num_dimensions_received
    jal ra, struct_unpack

    # Loop through dimensions
    lw s3, num_dimensions_received # s3 = loop counter, was ecx
    li s4, 0                      # s4 = dimension counter, was ebx

.dimension_loop:
    bge s4, s3, .receive_interp_x_count
    
    addi s4, s4, 1 # Increment dimension counter

    # Receive the number of floats in the x array for this dimension (4 bytes)
    li a2, 4
    mv a1, s1
    la a0, num_fx_buffer
    jal ra, recv
    li t0, 4
    blt a0, t0, .client_disconnected_x_len

    # Unpack number of fx floats
    la a0, struct_unpack_fmt_i
    li a1, 4
    la a2, num_fx_buffer
    la a3, num_fx_received
    jal ra, struct_unpack
    
    # Calculate expected bytes for x data
    lw t0, num_fx_received
    lw t1, float_size
    mul t2, t0, t1
    sw t2, expected_bytes
    
    # Receive all x data
    la a0, fx_data_buffer
    mv a1, s1
    jal ra, receive_all
    
    lw t0, expected_bytes
    bne a0, t0, .client_disconnected_incomplete_x
    
    # Receive the number of floats in the y array for this dimension (4 bytes)
    li a2, 4
    mv a1, s1
    la a0, num_fy_buffer
    jal ra, recv
    li t0, 4
    blt a0, t0, .client_disconnected_y_len
    
    # Unpack number of fy floats
    la a0, struct_unpack_fmt_i
    li a1, 4
    la a2, num_fy_buffer
    la a3, num_fy_received
    jal ra, struct_unpack
    
    # Calculate expected bytes for y data
    lw t0, num_fy_received
    lw t1, float_size
    mul t2, t0, t1
    sw t2, expected_bytes
    
    # Receive all y data
    la a0, fy_data_buffer
    mv a1, s1
    jal ra, receive_all
    
    lw t0, expected_bytes
    bne a0, t0, .client_disconnected_incomplete_y

    # TODO: In a real implementation, you would store the pointers to fx_data_buffer
    # and fy_data_buffer in an array to pass to the interpolation function.
    
    j .dimension_loop

.receive_interp_x_count:
    # Receive the number of interpolation x values (4 bytes)
    li a2, 4
    mv a1, s1
    la a0, num_interp_x_buffer
    jal ra, recv
    li t0, 4
    blt a0, t0, .client_disconnected_interp_x_len
    
    # Unpack number of interpolation x values
    la a0, struct_unpack_fmt_i
    li a1, 4
    la a2, num_interp_x_buffer
    la a3, num_interp_x_received
    jal ra, struct_unpack

    # Calculate expected bytes for interpolation x data
    lw t0, num_interp_x_received
    lw t1, float_size
    mul t2, t0, t1
    sw t2, expected_bytes
    
    # Receive all interpolation x data
    la a0, interp_x_data_buffer
    mv a1, s1
    jal ra, receive_all
    
    lw t0, expected_bytes
    bne a0, t0, .client_disconnected_incomplete_interp_x

    # TODO: This is where the call to hyperbolic_parabolic_interpolation_nd would go.
    # The C calling convention would be used. This is a placeholder.
    # la a0, all_fx_data_list_pointer
    # la a1, all_fy_data_list_pointer
    # lw a2, num_dimensions_received
    # la a3, interp_x_data_buffer
    # lw a4, num_interp_x_received
    # la a5, result_buffer
    # jal ra, hyperbolic_parabolic_interpolation_nd

    # Assume the function returns the length of the result in a0.
    # The result is in result_buffer.
    mv t0, a0              # t0 = result_length_received

    # Send the length of the float result back to the client
    la a0, struct_pack_fmt_i
    mv a1, t0
    la a2, result_length_send
    jal ra, struct_pack
    
    lw t1, result_length_send
    mv a2, t1
    mv a1, s1
    la a0, result_length_send
    jal ra, send
    bne a0, t1, .connection_error_send_result_len

    # Send the result data
    mv a2, t0
    mv a1, s1
    la a0, result_buffer
    jal ra, send
    bne a0, t0, .connection_error_send_result

    j .cleanup

.invalid_op_code:
    # Print error message for invalid op code.
    la a0, msg_invalid_op_code
    lw a1, operation_code_received
    lw a2, client_address+12
    lw a3, client_address+8
    lw a4, client_address+4
    lw a5, client_address+0
    jal ra, printf
    j .cleanup_socket

.client_disconnected_op_code:
    # Print error message for unexpected disconnection (no op code).
    la a0, msg_disconnected_op_code
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    jal ra, printf
    j .cleanup_socket

.client_disconnected_num_dim:
    # Print error message for unexpected disconnection (no number of dimensions).
    la a0, msg_disconnected_num_dim
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    jal ra, printf
    j .cleanup_socket

.client_disconnected_x_len:
    # Print error message.
    la a0, msg_disconnected_x_len
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    mv a5, s4
    jal ra, printf
    j .cleanup_socket

.client_disconnected_incomplete_x:
    # Print error message.
    la a0, msg_disconnected_incomplete_x
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    mv a5, s4
    jal ra, printf
    j .cleanup_socket

.client_disconnected_y_len:
    # Print error message.
    la a0, msg_disconnected_y_len
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    mv a5, s4
    jal ra, printf
    j .cleanup_socket

.client_disconnected_incomplete_y:
    # Print error message.
    la a0, msg_disconnected_incomplete_y
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    mv a5, s4
    jal ra, printf
    j .cleanup_socket

.client_disconnected_interp_x_len:
    # Print error message.
    la a0, msg_disconnected_interp_x_len
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    jal ra, printf
    j .cleanup_socket

.client_disconnected_incomplete_interp_x:
    # Print error message.
    la a0, msg_disconnected_incomplete_interp_x
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    jal ra, printf
    j .cleanup_socket

.value_error:
    # Print error message. Assumes error message is in a buffer.
    la a0, msg_value_error
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    la a5, error_message_buffer
    jal ra, printf
    j .cleanup_socket

.connection_reset_error:
    # Print error message.
    la a0, msg_connection_reset
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    jal ra, printf
    j .cleanup_socket

.unexpected_error:
    # Print error message.
    la a0, msg_unexpected_error
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    la a5, error_message_buffer
    jal ra, printf
    j .cleanup_socket

.connection_error_send_result_len:
    # Handle error sending result length
    j .cleanup_socket

.connection_error_send_result:
    # Handle error sending result data
    j .cleanup_socket

.cleanup_socket:
    # Close the socket
    mv a0, s1
    jal ra, close

.cleanup:
    # Print "Connection with client..."
    la a0, msg_connection_closed
    lw a1, client_address+12
    lw a2, client_address+8
    lw a3, client_address+4
    lw a4, client_address+0
    jal ra, printf
    
    # RISC-V function epilogue.
    # Restore the stack and saved registers, then return.
    ld ra, 24(sp)
    ld s0, 16(sp)
    ld s1, 8(sp)
    ld s2, 0(sp)
    addi sp, sp, 32
    ret

# Helper function to receive a specific number of bytes
receive_all:
    # a0 = buffer, a1 = socket, a2 = expected_bytes
    addi sp, sp, -16 # Make space on stack
    sd s0, 8(sp)     # Save s0 (frame pointer)
    sd s1, 0(sp)     # Save s1 (socket)
    
    mv s0, a0        # s0 = buffer
    mv s1, a1        # s1 = socket
    mv a2, a2        # a2 = expected bytes
    
    li t0, 0         # t0 = total bytes received

.receive_loop:
    blt t0, a2, .continue_receive
    j .receive_done
    
.continue_receive:
    sub t1, a2, t0      # Remaining bytes
    lw t2, chunk_size
    blt t1, t2, .use_remaining
    mv t1, t2
    
.use_remaining:
    mv a0, s0           # a0 = buffer
    mv a1, s1           # a1 = socket
    mv a2, t1           # a2 = number of bytes to receive
    
    jal ra, recv
    
    blez a0, .receive_error # Connection closed
    add t0, t0, a0      # Add received bytes to total
    add s0, s0, a0      # Move buffer pointer forward
    j .receive_loop
    
.receive_done:
    mv a0, t0           # Return total bytes received
    j .receive_exit
    
.receive_error:
    li a0, -1
    
.receive_exit:
    ld s1, 0(sp)
    ld s0, 8(sp)
    addi sp, sp, 16
    ret
 

No comments: