# Copyright (C) Wesley Phoa, February 2000

import copy
import operator
from math import *
from types import *
from Numeric import *
from LinearAlgebra import *
import pythoncom

class TestServer:

	_public_methods_ = ['Statistics', 'ExtendedStatistics']

	_reg_progid_ = "Python.TestServer"
	_reg_clsid_ = "{9BF82FE0-E7F4-11D3-B626-0000F6C7C6C4}"
	_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER

	# internal methods
	# note that results of computations are cached in attributes

	def analyze_multiple_time_series(self, data):
		"""Argument should be a 2D Numeric array; cols are series."""

		# compute means
		num_dates = len(data)
		self.means = sum(data) / num_dates

		# detrend time series
		data = data - array(num_dates * [self.means])

		# compute vols
		vars = (num_dates * sum(data * data) - \
			sum(data) * sum(data)) / (num_dates * num_dates)
		self.vols = sqrt(vars)

		# transpose so that each time series is a row
		data = transpose(data)
		num_series = len(data)

		# construct [sigma_i * sigma_j] matrix
		temp2 = zeros( (num_series, num_series), Float)
		for i in range(num_series):
			for j in range(num_series):
				temp2[i][j] = self.vols[i] * self.vols[j]

		# construct covariance matrix
		covs = zeros( (num_series, num_series), Float)
		for i in range(num_series):
			for j in range(num_series):
				covs[i][j] = sum(data[i] * data[j]) / num_dates

		# compute correlations
		self.correls = covs / temp2

	def find_principal_components(self):
		"""Uses cached results from analyze_multiple_time_series."""

		self.PCweights, self.PCvectors = \
			eigenvectors(self.correls)
		self.PCweights = self.PCweights / sum(self.PCweights)

	# public interface methods

	def Statistics(self, data):
		"""Argument should be rectangular tuple-of-tuples."""

		data = array(data)
		self.analyze_multiple_time_series(data)

		return map(lambda val1, val2, vect: [val1] + [val2] + vect,
			list(self.means), list(self.vols),
			map(list, list(self.correls)))

	def ExtendedStatistics(self, data):
		"""Argument should be rectangular tuple-of-tuples."""

		data = array(data)
		self.analyze_multiple_time_series(data)
		self.find_principal_components()

		return map(lambda val1, val2, vect1, val3, vect2: 
			[val1] + [val2] + vect1 + [val3] + vect2, 
			list(self.means), list(self.vols),
			map(list, list(self.correls)),
			list(self.PCweights),
			map(list, list(self.PCvectors)))

if __name__=='__main__':

	print "Invoking registration utility..."
	import win32com.server.register
	win32com.server.register.UseCommandLine(TestServer)