'''
Created on Mar 20, 2012
Modified on Apr 28, 2012
@author: Dilum Bandara
@version: 0.2
@license: Apache License v2.0

   Copyright 2012 H. M. N. Dilum Bandara and Anura P. Jayasumana, Colorado State
   University

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
'''

'''
Generate resources for testing & performance analysis
'''

import sys
import random
import math


def uniform(minAtt, maxAtt, numRes, fd):
    '''
    Generate given number of resources - Use uniform distribution
    @param minAtt: Minimum attribute value  
    @param maxAtt: Maximum attribute value
    @param numRes: Number of resources to generate
    @param fd: File descripter   
    '''
    fd.write('nid\ttime\tcusAtt\n')
    for i in range(numRes):
        tmpStr = str(i) + '\t0\t' + str(random.randint(minAtt, maxAtt)) + '\n'
        fd.write(tmpStr)


def triangular(minAtt, maxAtt, mode, numRes, fd):
    '''
    Generate given number of resources - use triangular distribution given 
    low <= N <= high & mode
    @param minAtt: Minimum attribute value  
    @param maxAtt: Maximum attribute value
    @param mode: Most frequent entry 
    @param numRes: Number of resources to generate
    @param fd: File descripter   
    '''
    fd.write('nid\ttime\tcusAtt\n')
    for i in range(numRes):
        tmpStr = str(i) + '\t0\t' + str(int(random.triangular(minAtt, maxAtt, \
                                                              mode))) + '\n' 
        fd.write(tmpStr)


def gauss(minAtt, maxAtt, mean, std, numRes, fd):
    '''
    Generate given number of resources - use Gausssian distribution
    @param minAtt: Minimum attribute value  
    @param maxAtt: Maximum attribute value
    @param mean: Mean value
    @param std: Standara deviation  
    @param numRes: Number of resources to generate
    @param fd: File descripter   
    '''
    fd.write('nid\ttime\tcusAtt\n')
    for i in range(numRes):
        tmpVal = random.gauss(mean, std)
        if tmpVal < minAtt: tmpVal = minAtt #Truncate
        if tmpVal > maxAtt: tmpVal = maxAtt #Truncate
        tmpStr = str(i) + '\t0\t' + str(int(tmpVal)) + '\n' 
        fd.write(tmpStr)


def pareto(minAtt, maxAtt, alpha, numRes, fd):
    '''
    Generate given number of resources - use Pareto distribution
    @param minAtt: Minimum attribute value  
    @param maxAtt: Maximum attribute value
    @param alpha: Shape parameter
    @param numRes: Number of resources to generate
    @param fd: File descripter   
    '''
    fd.write('nid\ttime\tcusAtt\n')
    for i in range(numRes):
        tmpVal = random.paretovariate(alpha)
        if tmpVal < minAtt: tmpVal = minAtt #Truncate
        if tmpVal > maxAtt: tmpVal = maxAtt #Truncate
        tmpStr = str(i) + '\t0\t' + str(int(tmpVal)) + '\n' 
        fd.write(tmpStr)


def zipfs(alpha, numKeys, totalKeys, keyFile, numRes, fd):
    '''
    Generate files according to a Zipf's distribution & dump both resources & 
    distinct keys into files. Each key is 32'bit logn
    @param alpha: Zipf's parameter
    @param numKeys: Number of distinct keys
    @param totalKeys: Total no of keys
    @param keyFile: Name for key file
    @param numRes: Number of resources to be used in simulation 
    @param fd: File descripter    
    '''
    fd.write('nid\ttime\tcusAtt\n')
    denom = 0.0
    for i in range(numKeys):
        denom += 1/math.pow(i + 1, alpha)
    numGenKeys = 0 #Keys generated
    keys = []

    for i in range(numKeys):
        num = int(math.ceil(((1/math.pow(i + 1, alpha))/denom) * totalKeys))
        while True:
            key = random.randint(0, long(pow(2, 32)) - 1)
            if key not in keys:
                keys.append(key)
                for j in range(num):
                    tmpStr = str(numGenKeys) + '\t' + \
                            str(int(numGenKeys/numRes)) +'\t' + str(key) + '\n' 
                    numGenKeys += 1
                    fd.write(tmpStr)
                break
    
    print 'Total no of files generated', numGenKeys
    frac = ((1/pow((1), alpha))/denom)
    print 'Most popular key has: ' + str(frac * 100) + '% ' + \
                                            str(math.ceil(frac * totalKeys))    
    fdKeys = open(keyFile, 'w')
    for key in keys: fdKeys.write(str(key) + '\n')
    fdKeys.close()


def shuffle(entries):
    '''
    Shuffle queries based on Fisher-Yates shuffle. List is modified in place
    @param entries: List of entries
    '''
    for i in reversed(range(1, len(entries))):
        j = random.randrange(i + 1)
        entries[i], entries[j] = entries[j], entries[i]


def cpuSpeed(numNodes, numRes, fIn, fdOut):
    '''
    Generate CPUSpeed data set. Randomly sample given no of nodes
    @param numNodes: Number of nodes to generate 
    @param numRes: Number of resources to be used in simulation 
    @param fIn: File name of CPUSpeed dataset
    @param fdOut: File descriptor to write new resource dataset    
    '''
    nodes = []
    fdIn = open(fIn, 'r')
    for line in fdIn: #CPUSpeed, frequency
        tmp = line.split()
        for i in range(int(tmp[1])): nodes.append(float(tmp[0]))
    fdIn.close()
    shuffle(nodes) #shuffle entries    
    
    fdOut.write('nid\ttime\tCSp\n')
    numEntries = len(nodes)
    print 'No of nodes in dataset', numEntries
    numGenKeys = 0 #Keys generated
    for i in range(numNodes):
        idx = random.randint(0, numEntries - 1)
        tmpStr = str(numGenKeys) + '\t' + \
                    str(int(numGenKeys/numRes)) +'\t' + str(nodes[idx]) + '\n'
        fdOut.write(tmpStr)
        numGenKeys += 1


def cpuFree(numNodes, numRes, fIn, fdOut):
    '''
    Generate CPUFree data set. Dataset is already randomly sampled
    @param numNodes: Number of nodes to generate 
    @param numRes: Number of resources to be used in simulation 
    @param fIn: File name of CPUSpeed dataset
    @param fdOut: File descriptor to write new resource dataset    
    '''
    fdOut.write('nid\ttime\tCFree\n')

    fdIn = open(fIn, 'r')
    numGenKeys = 0
    for line in fdIn: #CPUFree
        tmpStr = str(numGenKeys) + '\t' +  str(int(numGenKeys/numRes)) + \
                                                                '\t' + line
        fdOut.write(tmpStr)
        numGenKeys += 1
    fdIn.close()    
    print 'No of nodes in dataset', numGenKeys


def cpuFreeInvert(numRes, inverTime, fIn, fdOut):
    '''
    Generate CPUFree data set. Dataset is already randomly sampled. Invert by 
    newX = 100 - x
    @param numNodes: Number of nodes to generate 
    @param numRes: Number of resources to be used in simulation
    @param invertTime: Time resource attributes will be inverted  
    @param fIn: File name of CPUSpeed dataset
    @param fdOut: File descriptor to write new resource dataset    
    '''
    fdOut.write('nid\ttime\tCFree\n')

    fdIn = open(fIn, 'r')
    numGenKeys = 0
    cpuFree = []
    for line in fdIn: #CPUFree
        tmpStr = str(numGenKeys) + '\t' +  str(int(numGenKeys/numRes)) + \
                                                                '\t' + line
        fdOut.write(tmpStr)
        numGenKeys += 1
        cpuFree.append(int(line))

    numGenKeys = 0
    for x in cpuFree: #CPUFree
        tmpStr = str(numGenKeys) + '\t' +  str(inverTime + \
                            int(numGenKeys/numRes)) + '\t' + str(100 - x) + '\n' 
        fdOut.write(tmpStr)
        numGenKeys += 1
    
    fdIn.close()
    print 'No of nodes in dataset', numGenKeys


def main(*argv):
    '''
    Main function to indicate start of program
    '''
    baseFName = 'resources_'
    assert(len(sys.argv) == 2), \
        'Wrong parameters. Use \'python genResources.py [seed]\''
    random.seed(int(sys.argv[1])) #Set seed
    fdOut = open(baseFName + sys.argv[1] + '.txt', 'w')
    
    #uniform(0, 1000, 100, fdOut)
    #triangular(0, 1000, 500, 125, fdOut)
    #gauss(0, 1000, 500, 220, 100, fdOut)
    #pareto(0, 1000, 1, 100, fdOut)
    #zipfs(0.8, 1000, 9500, 'keys.txt', 100, fdOut)
    #cpuSpeed(5000, 500, 'CPUSpeed.txt', fdOut)
    #cpuFree(5000, 500, 'CPUFree_' + sys.argv[1] + '.txt', fdOut)
    cpuFreeInvert(5000, 300, 'CPUFree_' + sys.argv[1] + '.txt', fdOut)
    fdOut.close()


if __name__ == '__main__':
    sys.exit(main(*sys.argv))
