#!/usr/bin/env python
from rpy import *
from numpy import array
from math import sqrt
from sys import exit
r.library('randomForest')

#do not convert model object into python object. 
#They cannot hardly be inverse-converted.
#These oject only used in R
r.randomForest.local_mode(NO_CONVERSION)
r.as_factor.local_mode(NO_CONVERSION)
r.factor.local_mode(NO_CONVERSION)
r.set_seed(0)	#reset randomSeed
print "randomForest bridge established"

#findminmax(data_x) returns (m,M)
#for minimum and Maximum id of SVM data
#1 2:5 3:10 20:5
#  ^        ^^
def findminmax(data):
	m, M = 1,0
	if type(data)!=list:
		data=[data]
	for row in data:
		if m > min(row):
			m = min(row)
		if M < max(row):
			M = max(row)
	return m,M

#convert my datastruction into Numeric.array 
def svm2matrix(data, mM=None):
	#handle both single(dict) and multiple(list of dict) instance
	if type(data) == dict:
		data = [data]

	#assure domain since we do not know what's the starting index of 
	#sparse SVM format
	if mM == None:
		m,M = findminmax(data)
	else:
		m,M = mM

	#create matrix
	matrix = []
	for row in data:
		mat = []
		for i in range(m,M+1):
			if row.has_key(i):
				mat.append(row[i])
			else:
				mat.append(0)	#missing data
		matrix.append(mat)
	return array(matrix)

def RFTrain(pos_x, neg_x, (mtry, mM)=(None,None)):
	if mM==None:
		mM = findminmax(pos_x+neg_x)
	# max(floor(ncol(x)/3), 1), floor(sqrt(ncol(x))))
	if mtry == None or mtry == 0:
		n_attr = mM[1] - mM[0] + 1
		mtry = max(n_attr/3 , sqrt(n_attr))
	train_x = svm2matrix(pos_x + neg_x, mM)
	train_y = r.as_factor(array([1]*len(pos_x)+[-1]*len(neg_x)))
	model = r.randomForest(train_x, train_y, mtry = mtry)
	return model,mM

def RFProb(test, model):
	rfmodel, mM =  model
	test_x = svm2matrix(test, mM)
	prob=r.predict(rfmodel, test_x, type='prob').tolist()[0][1]
	return prob
	#prediction's first entry's first class(+1)

def unitest():
	tr_p = [{1:11,2:12},{1:21,2:22}]
	tr_n = [{1:31,2:32},{1:41,2:42}]
	test = {1:41,2:42}
	model = RFTrain(tr_p, tr_n)
	v = RFProb(test, model)
	if v == 0.01:
		print "OK"
		exit(0)
	else:
		print "Failed"
		print "wrong value :", v, "(This is usually OK for random table of different OS)"
		exit(1)


if __name__ == '__main__':
	unitest()
