from numpy import *
import time
from sys import *
from random import *


#  Generate with the modular program, a stepwise process.
#  The idea is to freeze and reverse the modular structure of the virus,
#  hypothetically, inserting a prime at each stage.
#  so the modular structure is reversed with each modular sequence in
#  this stepwise process, with incremental primes.
#  we also need an initial prime to kick it out of place, although this may enable it, we would need a very specific prime
#  as in the number of modular steps that exist within the virus.

#  it must be very specific, as in a prime number of bases
#  at the end we need one, then three that 'commute' in the sense that primes may do so
#  and another prime.  These three primes must be evenly spaced, along the 1/logarithmic distribution.

#  These change the modular structure into a diamond, which is more symmetric, and
#  which leads to an inversion of its symmetry along the axis upon which it acts.

#  The inserted primes cause jump dislocations in the aperiodic structure, weakening it,
#  and retromorphing it into a symmetric and defused molecule that essentially cannot replicate
#  As well, the symmetry means the immune system will have an easier time defeating the virus.


##    print("Total number of sets at size:",l,"is:",sump)
##    print("Total number of new sets at size:",l,"is:",sump-sumo)
##    print("Ratio of new contained sets to old contained sets:",float(new_set_contains)/float(previous_set_contains))        





class Aperiodic:
    def __init__(self,limit):
        self.limit = limit

        self.a = 0
        self.k = 0      # Prime sequence initialization.
        self.l = 0
        self.n = 0
        self.m = 0
        self.o = 0
        self.op = 0
        self.g = 0
        self.p = 0
        self.r = 0
        self.mo = self.limit
        self.irr = 0
        self.irro = 0
        self.kappa = 1.0
        self.length = self.limit
        self.setsize = self.length
        self.N = self.setsize
        self.convergecount = 12

        self.maxN = self.limit
        self.N = self.maxN
        self.continual = list()

                                         # Modular structure initialization of variables
        self.M = self.limit

        self.sumo = 1                    # new total number of sets with property of commensurability
        self.sump = 1                    # new total number of sets with or without property of commensurability

        self.totalwithproperty = 1
        self.total = 1

        self.set_ratio = 1
        self.set_with = 0                # average number of sets each new set contains
        self.net_without = 1             # average number of sets each previous set contained

        self.agreement_ratio = 1
        self.property_ratio = 1
        self.kappa = 1
        self.beta = 1


        self.v = 0
        self.u = 0

        self.var1 = 0
        self.var2 = 0
                                        # Modular ring variable initialization
                                        # up to this total ring size
                                        # Accumulated average of ratios of combinatorial sets with the property of commensurability to total sets within sets.
                                        # This is equivalent to finding, those sets that agree (or ones contained in each other), in combinatorial number.
                                        # This ratio is the number of sets without this property to those with and without this property
                                        # These are the contained and containing because it is the number with the agreement condition to those with or without it.
                                        # Each set takes part in both processes of containment and being contained.  The total is the number of sets.
                                        # This is the proportion of sets that are neither contained nor the container.
                                        # This is with the agreement condition.
    
        self.limit = self.M

        self.As=list()                   # list of natural numbers
        self.Bs=list()                   # list of primes
        self.Cs=list()                   # list of product numbers

        self.As.append(1)
        self.As.append(2)

        self.Bs.append(1)
        self.Bs.append(2)

        self.Cs.append(1)
        self.Cs.append(2)

        self.flag = 0

        self.continual = list()




    def modular_ring(self,M):
        self.continual = list()
        
        for n in xrange(1,M+1):
            l1=self.lcm(n,M)/n
            l2=self.lcm(M-n+1,M)/(M-n+1)

            if l1 == l2:
                self.continual.append("F")
            else:
                self.continual.append("T")

        return self.continual



    def su(self,element1,element2):
        if element1 == "T" and element2 == "T":
            return('T')
        if element1 == "T" and element2 == "F":
            return('O')
        if element1 == "T" and element2 == "O":
            return('O')
        if element1 == "T" and element2 == "C":
            return('T')
            
        if element1 == "F" and element2 == "T":
            return('O')
        if element1 == "F" and element2 == "F":
            return('F')
        if element1 == "F" and element2 == "O":
            return('O')
        if element1 == "F" and element2 == "C":
            return('F')
            
        if element1 == "O" and element2 == "T":
            return('O')
        if element1 == "O" and element2 == "F":
            return('O')
        if element1 == "O" and element2 == "O":
            return('O')
        if element1 == "O" and element2 == "C":
            return('O')

        if element1 == "C" and element2 == "T":
            return('T')
        if element1 == "C" and element2 == "F":
            return('F')
        if element1 == "C" and element2 == "O":
            return('O')  
        if element1 == "C" and element2 == "C":
            return('C')


    def ci(self,element1,element2):
        if element1 == "T" and element2 == "T":
            return('T')
        if element1 == "T" and element2 == "F":
            return('C')
        if element1 == "T" and element2 == "O":
            return('T')
        if element1 == "T" and element2 == "C":
            return('C')
            
        if element1 == "F" and element2 == "T":
            return('C')
        if element1 == "F" and element2 == "F":
            return('F')
        if element1 == "F" and element2 == "O":
            return('F')
        if element1 == "F" and element2 == "C":
            return('C')
            
        if element1 == "O" and element2 == "T":
            return('T')
        if element1 == "O" and element2 == "F":
            return('F')
        if element1 == "O" and element2 == "O":
            return('O')
        if element1 == "O" and element2 == "C":
            return('C')

        if element1 == "C" and element2 == "T":
            return('C')
        if element1 == "C" and element2 == "F":
            return('C')
        if element1 == "C" and element2 == "O":
            return('C')  
        if element1 == "C" and element2 == "C":
            return('O')


    def gcd(self,c,d):
        while d:
            c,d=d,c%d
        return c

    def lcm(self,a,b):
        return a*b/self.gcd(a,b)

    def fac(self,N):
        m=1
        for x in xrange(1,int(N)):
            m=m*(int(N)-x+1)
        return m

    def choose(self,a,b):
        return int(self.fac(a)/(self.fac(b)*self.fac(a-b)))

    def product(self,newlist):
        product = "O"
        for m in xrange(len(newlist)-1):
            product = su(newlist[m],product)
        return product

    def multiplySU(self,listone,listtwo):
        output = list()
        for m in xrange(maxN-1):
            output.append(su(listone[m],listtwo[m]))
        return output

    def multiplyCI(self,listone,listtwo):
        output = list()
        for m in xrange(maxN-1):
            output.append(ci(listone[m],listtwo[m]))
        return output

    def reverseMap(self,inputlist):
        output = list()
        for m in xrange(maxN-1):
            element = inputlist[m]
            if element == "T":
                elementout = "F"
            if element == "F":
                elementout = "T"
            if element == "O":
                elementout = "C"
            if element == "C":
                elementout = "O"
            output.append(elementout)
        return output

    def sectionProduct(self,length,inputlist):
        output = list()
        for m in xrange(maxN-1):
            mini = list()
            for l in xrange(length):
                if (m+l) > maxN-2:
                    mini.append(inputlist[m+l-maxN])
                else:
                    mini.append(inputlist[m+l])
            element = product(mini)
            output.append(element)
        return output

    def sectionProductBehind(self,length,inputlist):
        output = list()
        for m in xrange(maxN-1):
            mini = list()
            for l in xrange(length):
                if (m-l) < 0:
                    mini.append(inputlist[m-l+maxN])
                else:
                    mini.append(inputlist[m-l])
            element = product(mini)
            output.append(element)
        return output

    def intersection(self,list1,list2):
        outlist = list()
        for x in xrange(len(list2)):
            if list1.count(list2[x]) == 0:
                pass
            else:
                if outlist.count(list2[x]) == 0:
                    outlist.append(list2[x])
                else:
                    pass
        return outlist

    def subtraction(self,list1,list2):               ## list 2 from list 1
        outlist = list()
        for x in xrange(len(list1)):
            outlist.append(list1[x])
        for x in xrange(len(list2)):
            if outlist.count(list2[x]) == 0:
                pass
            else:
                outlist.pop(outlist.index(list2[x]))
        return outlist

    def copylist(self,list1):
        outlist = list()
        for x in xrange(len(list1)):
            outlist.append(list1[x])
        return outlist

    def primenumber(self,w):  
        for x in xrange(len(self.As)):
            for y in xrange(len(self.As)):
                if self.As[x] != 1 and self.As[y] != 1:
                    b = self.As[x]*self.As[y]

                    if b > self.limit:
                        pass
                    else:            
                        if self.Cs.count(b) == 0:
                            self.Cs.append(b)

        r = self.subtraction(self.subtraction(self.As,self.intersection(self.Cs,self.As)),self.Bs)
        r.sort()

        if len(r)==0:
            pass
        else:
            if self.Bs.count(r[0])==0:
                self.Bs.append(r[0])
                self.flag = 1
        if self.As.count(w+1) == 0:
            self.As.append(w+1)
            
        self.As.sort()
        self.Cs.sort()            
        self.Bs.sort()

        if self.flag == 1:
            self.flag = 0
            hg = int(self.Bs[len(self.Bs)-1])
            return(hg)
        else:
            return 0

    def go(self,y,z):
        for x in xrange(self.m):
            self.op = y**(self.op)/(1-y**(self.op)/z)
        return self.op

    def naturalnumbers(self,M):
        for l in xrange(1,M+1):
            for n in xrange(1,int((l+1)/2)+1):
                self.k += 1

                l1=self.lcm(n,l)/n
                l2=self.lcm(l-n+1,l)/(l-n+1)

                a = self.choose(l,n-1)
                b = self.choose(l,l-n-1)

                if n == (l+1)/2:        
                    self.total += 1
                else:
                    self.total += 2
                    
                self.sump += (a*b)
                
                if l1==l2 and n != (l+1)/2:
                    self.totalwithproperty += 2
                    self.sumo += 2*(a*b)
                if l1==l2 and n == (l+1)/2:
                    self.totalwithproperty += 1
                    self.sumo += (a*b)

                self.kappa = float(self.sump-self.sumo)/float(self.sump)
                self.beta = float(self.totalwithproperty)/float(self.total)
                
                self.set_ratio = self.set_ratio + self.kappa
                self.property_ratio = self.property_ratio + self.beta

#                print("Property Ratio (pi):",1/(self.property_ratio/self.k),"Set Ratio (pi*?):",1/(self.set_ratio/self.k),"Their Ratio (?):",self.set_ratio/self.property_ratio)
                prime = self.primenumber(n)
                mr = self.modular_ring(n)
#                if prime == 0:
#                    pass
#                else:
#                    print(prime)

                self.go(1/(self.set_ratio/self.k),1/(self.property_ratio/self.k))
                self.m += 1
                self.sequence_eval(1/(self.set_ratio/self.k))
                

    def sequence_eval(self,exp):
            self.o += 1
            self.k = self.o
            self.l = 1	    # with l = 1 e(e-1), with l = 2
            self.j = 1
            self.b = 0
            self.h = 0
            self.p = 0
            self.n = self.k
            self.irr = 0
            
            while self.b < self.convergecount:
                self.b += 1
                self.p += 1
                self.index = (self.p*self.a+self.n)%len(self.continual)
                if self.continual[self.index] == "T":
                    self.n += 1
                if self.continual[self.index] == "F":
                    self.n -= 1
                self.l += 1
                if self.n < 1:
                    self.n = -self.n
                    self.n += 1
                if self.l < 0:
                    self.l = -self.l
                self.irr = self.irr + float(self.j)*exp**(-self.mo*self.l/self.n)
            
            if self.irr != 0.0:
                self.inv = 1/self.irr
            else:
                self.inv = 1
                
            print("Number:",self.irr,"o:",self.o,"Inverse Number:",self.inv)




M = 1731
G = 23589

instance = Aperiodic(G)
instance.naturalnumbers(M)


# The structure presupposes the existence of a beginning.
# Yet, the first step cannot be purely a beginning.
# For this we need two things in tandem, the third, is before, and concealed in the sequence.
# to be aperiodic it must begin with a negation of the sequence, this can only happen if it is
# one step beyond that which we take to be the beginning to be judged by the aperiodic sequence.
# This implies that our last steps, or the first of the aperiodic sequence as we destroy it, requires one step in reverse...
    







