'''
Created on Mar 16, 2014

@author: joe
'''

import random
import mathobj

matRandFnc=lambda :3*random.gauss(0.0,1.0)
vecRandFnc=lambda :random.random()/5.0-4.1

class SharedLinearity:
    def __init__(self,nIn=1,nOut=1,inS=True,
                 matRand=matRandFnc,vecRand=vecRandFnc):
        self.nInputs=nIn
        self.nOutputs=nOut
        self.inScale=inS
        self.matRand=matRand
        self.vecRand=vecRand
        
    def flush(self):
        print "<biasedlinearity>", self.nOutputs, self.nInputs
        mathobj.randomFnc=self.matRand
        mathobj.inputScale=self.inScale
        mathobj.PrintTransMatrix(self.nInputs, self.nOutputs)
        mathobj.randomFnc=self.vecRand
        mathobj.PrintVector(self.nOutputs)

class Linearity:
    def __init__(self,nIn=1,nOut=1,inS=True,
                 matRand=matRandFnc):
        self.nInputs=nIn
        self.nOutputs=nOut
        self.inScale=inS
        self.matRand=matRand
        
    def flush(self):
        print "<linearity>", self.nOutputs, self.nInputs
        mathobj.randomFnc=self.matRand
        mathobj.inputScale=self.inScale
        mathobj.PrintTransMatrix(self.nInputs, self.nOutputs)

class UpdatableBias:        
    def __init__(self,nIn=1,vecRand=vecRandFnc):
        self.nInputs=nIn
        self.nOutputs=nIn
        self.vecRand=vecRand
        
    def flush(self):
        print "<updatablebias>", self.nOutputs, self.nInputs
        mathobj.randomFnc=self.vecRand
        mathobj.PrintVector(self.nInputs)

class MiscPipe:
    def __init__(self,nIn=1,name="<pipe>"):
        self.nInputs=nIn
        self.nOutputs=nIn
        self.name=name
    
    def flush(self):
        print self.name, self.nOutputs, self.nInputs

class Distrib:
    def __init__(self,nIn=1,size=1):
        self.nInputs=nIn
        self.nOutputs=nIn
        self.size=size
    
    def flush(self):
        print "<distrib>", self.nOutputs, self.nInputs
        print self.size

class Combine:
    def __init__(self,nIn=1,size=1):
        self.nInputs=nIn
        self.nOutputs=nIn
        self.size=size
    
    def flush(self):
        print "<combine>", self.nOutputs, self.nInputs
        print self.size

class Divide:
    def __init__(self,nIn=1,divLen=[]):
        self.nInputs=nIn
        self.nOutputs=divLen[0]
        self.divLen=divLen
    
    def push(self,nxtLen):
        self.divLen+=[nxtLen];
    
    def flush(self):
        print "<divide>", self.nOutputs, self.nInputs
        print len(self.divLen),
        for Len in self.divLen:
            print Len,
        print

class Merge:
    def __init__(self,nOut=1,divLen=[]):
        self.nOutputs=nOut
        self.nInputs=divLen[0]
        self.divLen=divLen
    
    def push(self,nxtLen):
        self.divLen+=[nxtLen];
    
    def flush(self):
        print "<merge>", self.nOutputs, self.nInputs
        print len(self.divLen),
        for Len in self.divLen:
            print Len,
        print

class Reorder:
    def __init__(self,nIn=1,Order=[]):
        self.nInputs=nIn
        self.nOutputs=nIn
        self.Order=Order
    
    def push(self,nxtPos):
        self.Order+=[nxtPos];
    
    def flush(self):
        print "<reorder>", self.nOutputs, self.nInputs
        print len(self.Order),
        for Len in self.Order:
            print Len,
        print
        
class Compound:
    def __init__(self,nIn=1,nOut=1,Objs=[]):
        self.nInputs=nIn
        self.nOutputs=nOut
        self.Objs=Objs
    
    def push(self,nxtObj):
        self.Objs+=[nxtObj];
    
    def flush(self):
        print "<compound>", self.nOutputs, self.nInputs
        for Obj in self.Objs:
            Obj.flush()
        print "<endblock>"

class Discrete:
    def __init__(self,nIn=1,nOut=1,Objs=[]):
        self.nInputs=nIn
        self.nOutputs=nOut
        self.Objs=Objs
    
    def push(self,nxtObj):
        self.Objs+=[nxtObj];
    
    def flush(self):
        print "<discrete>", self.nOutputs, self.nInputs
        for Obj in self.Objs:
            Obj.flush()
        print "<endblock>"