<?php

namespace RistrettoApps\Spawnster\Services;

/**
 * Provides methods for vector operations.
 *
 * @package    Spawnster
 * @subpackage Spawnster/Services
 * @author     Ristretto Apps
 */
class VectorService {

	/**
	 * Calculate the magnitude of the vector.
	 *
	 * @since     1.0.0
	 * @param     array $vector    The vector.
	 * @return    float            The magnitude of the vector.
	 */
	public static function magnitude( array $vector ): float {
		$sum = 0;

		foreach ( $vector as $value ) {
			$sum += $value * $value;
		}

		return sqrt( $sum );
	}

	/**
	 * Normalize the vector.
	 *
	 * @since     1.0.0
	 * @param     array      $vector       The vector to normalize.
	 * @param     float|null $magnitude    The magnitude of the vector. If not provided, it will be calculated.
	 * @param     float      $epsilon      The epsilon value to use for normalization.
	 * @return    array                    The normalized vector.
	 */
	public static function normalize( array $vector, float $magnitude = null, float $epsilon = 1e-10 ): array {
		$magnitude = ! empty( $magnitude ) ? $magnitude : self::magnitude( $vector );

		if ( 0 === $magnitude ) {
				$magnitude = $epsilon;
		}

		foreach ( $vector as $key => $value ) {
				$vector[ $key ] = $value / $magnitude;
		}

		return $vector;
	}

	/**
	 * Convert a vector to a hexadecimal string.
	 *
	 * @since     1.0.0
	 * @param     string $binary_string    The binary string.
	 * @return    string                   The hexadecimal string.
	 */
	public static function binary_to_hexadecimal( string $binary_string ): string {
		$hex = '';
		foreach ( str_split( $binary_string, 8 ) as $char ) {
				$hex .= strtoupper( dechex( bindec( strrev( $char ) ) ) );
		}

		$hex = str_pad( $hex, ceil( strlen( $hex ) / 4 ) * 4, '0', STR_PAD_LEFT );

		return $hex;
	}

	/**
	 * Convert a vector to a hexadecimal string.
	 *
	 * @since     1.0.0
	 * @param     array $vector    The vector.
	 * @return    string           The hexadecimal string.
	 */
	public static function vector_to_hex( array $vector ): string {
		$binary = '';
		foreach ( $vector as $value ) {
				$binary .= $value > 0 ? '1' : '0';
		}

		$padded = str_pad( $binary, ceil( strlen( $binary ) / 8 ) * 8, '0', STR_PAD_LEFT );
		$hex    = self::binary_to_hexadecimal( $padded );
		$hex    = strtoupper( substr( str_pad( $hex, 96, '0', STR_PAD_RIGHT ), 0, 96 ) );

		return $hex;
	}

	/**
	 * Calculate the cosine similarity between two vectors.
	 *
	 * @since    1.0.0
	 * @param    array $vector1    The first vector.
	 * @param    array $vector2    The second vector.
	 * @return   float             The cosine similarity between the two vectors.
	 */
	public static function cosine_similarity( $vector1, $vector2 ): float {
		$dot_product = 0.0;
		$norm_a      = 0.0;
		$norm_b      = 0.0;
		$length      = count( $vector1 );

		for ( $i = 0; $i < $length; $i++ ) {
			$dot_product += $vector1[ $i ] * $vector2[ $i ];
			$norm_a      += pow( $vector1[ $i ], 2 );
			$norm_b      += pow( $vector2[ $i ], 2 );
		}

		return $dot_product / ( sqrt( $norm_a ) * sqrt( $norm_b ) );
	}
} 