#include "semq_dispatch.h"
#include "semq_phase_atan2.h"
#include <math.h>
#include <stddef.h>
#include <stdint.h>
static uint32_t phase_output_size(uint32_t dim, uint32_t n_bins, uint32_t packed) {
(void)n_bins;
if (dim == 0u) {
return 0u;
}
if ((dim & 1u) != 0u) {
return 0u;
}
const uint32_t pairs = dim / 2u;
if (packed != 0u) {
if ((pairs & 1u) != 0u) {
return 0u;
}
return pairs / 2u;
}
return pairs;
}
static semq_status_t phase_encode(
const float* input,
uint32_t dim,
uint32_t n_bins,
uint32_t packed,
semq_code_t* output) {
const uint32_t out_bytes = phase_output_size(dim, n_bins, packed);
if (out_bytes == 0u) {
return SEMQ_ERR_INVALID_INPUT;
}
const uint32_t pairs = dim / 2u;
if (packed == 0u) {
for (uint32_t p = 0u; p < pairs; ++p) {
output[p] = semq_phase_sector(
input[(size_t)p * 2u + 1u],
input[(size_t)p * 2u + 0u],
n_bins);
}
} else {
for (uint32_t p = 0u; p < pairs; p += 2u) {
const uint8_t lo = semq_phase_sector(
input[(size_t)p * 2u + 1u],
input[(size_t)p * 2u + 0u],
n_bins);
const uint8_t hi = semq_phase_sector(
input[(size_t)(p + 1u) * 2u + 1u],
input[(size_t)(p + 1u) * 2u + 0u],
n_bins);
output[p / 2u] = (semq_code_t)(lo | (uint8_t)(hi << 4u));
}
}
return SEMQ_OK;
}
static double invert_poly(double target) {
double lo = 0.0, hi = 1.0;
for (int i = 0; i < 64; i++) {
const double mid = 0.5 * (lo + hi);
if (semq_phase_atan_poly_unit(mid) < target) lo = mid; else hi = mid;
}
return 0.5 * (lo + hi);
}
static void direction_for(double theta, double* x, double* y) {
const int neg_y = theta < 0.0;
double m = neg_y ? -theta : theta;
const int neg_x = m > SEMQ_PI_HALF;
if (neg_x) m = SEMQ_PI - m;
const double top = semq_phase_atan_poly_unit(1.0);
double ax, ay;
if (m <= top) {
ax = 1.0;
ay = invert_poly(m);
} else {
ay = 1.0;
ax = invert_poly(SEMQ_PI_HALF - m);
}
const double norm = sqrt(ax * ax + ay * ay);
ax /= norm;
ay /= norm;
*x = neg_x ? -ax : ax;
*y = neg_y ? -ay : ay;
}
void semq_phase_representative(uint32_t n_bins, uint32_t sector, double out[2]) {
const double bin_width = (2.0 * SEMQ_PI) / (double)n_bins;
const double theta = -SEMQ_PI + ((double)sector + 0.5) * bin_width;
direction_for(theta, &out[0], &out[1]);
}
static semq_status_t phase_reconstruct(
const semq_code_t* codes,
uint32_t dim,
uint32_t n_bins,
uint32_t packed,
double* output) {
if (phase_output_size(dim, n_bins, packed) == 0u) {
return SEMQ_ERR_INVALID_INPUT;
}
const uint32_t pairs = dim / 2u;
for (uint32_t p = 0u; p < pairs; ++p) {
uint8_t sector;
if (packed == 0u) {
sector = codes[p];
} else {
const uint32_t byte_idx = p / 2u;
const uint32_t nibble = p & 1u;
sector = (uint8_t)((codes[byte_idx] >> (4u * nibble)) & 0x0Fu);
}
semq_phase_representative(n_bins, sector, output + (size_t)p * 2u);
}
return SEMQ_OK;
}
const semq_phase_backend_t SEMQ_PHASE_SCALAR_BACKEND = {
.encode = phase_encode,
.reconstruct = phase_reconstruct,
};