##############################################################################
#   A horribly inefficient computation of the de Rham contributing zigzags   #
#              of complex nilmanifold double complexes                       #
#                         by Jonas Stelzig                                   #
#                   All good parts are based on:                             #
#     'Sage-Math experiments in Differential and complex geometry'           #
#                        by Daniele Angella                                  #
##############################################################################


# Use: Change INPUT A, INPUT B and INPUT C according to your wishes, then press shift+Enter and go get a coffee/tea with some cake or cookies.

# Goal: Get something nonzero for gr^{d-1} gr^{d-1}H_{dR}^{d-1}.
#       If this works for some d >= 3: Very interesting!
#       If one can produce it for every d>=3: awesome!

d=3 #complex dimension. ------INPUT A-------
computeEverything=false # --------INPUT B-------
# CAUTION: Only change this to true if you have a good machine or time for a lot of cake and cookies




# an exterior algebra of the right dimension is constructed and the variable names are made available for usage

EE=ExteriorAlgebra(SR,['varphi%s' %j for j in range(d)]+['barvarphi%s' %j for j in range(d)])
EE.inject_variables()


# ----INPUT C --- Definition of differentials on EE. Only enter dell and delbar, dee is calculated automatically.
# CAUTION: No check included wether this forms actually a double complex. Might cause crashes if doesn't.

str_eq_dell={(1,4):I/2*varphi0}
str_eq_delbar={(1,4): I/2*barvarphi0}


# calculates dee from dell and delbar
s_str_dell=set(str_eq_dell.keys())
s_str_delbar=set(str_eq_delbar.keys())

str_eq_d=dict([(k,str_eq_dell[k]+str_eq_delbar[k])for k in s_str_dell&s_str_delbar] + \
              [(k,str_eq_dell[k])for k in s_str_dell-s_str_delbar] + \
              [(k,str_eq_delbar[k]) for k in s_str_delbar-s_str_dell]
             )
# checks if dee squares to zero


dell = EE.coboundary(str_eq_dell)
delbar = EE.coboundary(str_eq_delbar)
dee = EE.coboundary(str_eq_d)

if all([dee(dee(b))==0 for b in EE.gens()]):
    print 'differential well-defined'
else: print 'differential NOT well-defined'

#------------prints differentials to check if definitions are reasonable

#print "the differential dell"
#for b in EE.gens():
#    print b, "|-->", dell(b)


#print "the differential delbar "
#for b in EE.gens():
#    print b, "|-->", delbar(b)

print''
print "the total differential:"
for b in EE.gens():
    print b, "|-->", dee(b)

print''
#-----------calculate the dolbeault, conjugate dolbeault and betti numbers (in the first two cases, stuff with same total degree is summed)

#H_dell=dell.chain_complex().homology(); print "Del-cohomology-dimensions"; [H[1].dimension() for H in H_dell.items()]
#H_delbar=delbar.chain_complex().homology(); print "Delbar-cohomology-dimensions"; [H[1].dimension() for H in H_delbar.items()]
#H_d=dee.chain_complex().homology(); print "totalcohomology-dimensions"; [H[1].dimension() for H in H_d.items()]


#---------------defines the bidegree of an element (only works for pure elements)
def bidegree(elt):
    for b in elt.support():
        k=0
        for c in b:
            if c in set(range(d)):
                k += 1
    return (k,elt.degree()-k)

#---------------calculate the dimensions of the (p,q)-component or F^pA^k (not used)

def bidegr(p,q):
    if (0<=p<=d) and (0<=q<=d):
        return math.factorial(d)*math.factorial(d)/(math.factorial(d-p)*math.factorial(d-q)*math.factorial(p)*math.factorial(q))
    else: return 0


def dim_FA(k,p):
    return sum([bidegr(j,k-j) for j in range(p,k+1)])

#---------------bases for EE^{p,q}, resp. F^pEE^k, resp Fbar^pEE^k
def bigr_comp(p,q):
    base=[]
    for b in EE.basis():
        if (bidegree(b)==(p,q)):
            base.append(b)
    return base

def Fil(k,p):
    base=[]
    index=[(r,k-r) for r in range(p,k+1)]
    for b in EE.basis():
        if (bidegree(b) in index):
            base.append(b)
    return base

def Filbar(k,q):
    base=[]
    index=[(k-r,r) for r in range(q,k+1)]
    for b in EE.basis():
        if (bidegree(b) in index):
            base.append(b)
    return base


#-----------define differentials in each degree as matrix
dee_mat={j: matrix(len(EE.basis (j+1)),len(EE.basis(j)),\
                   [dee(list(EE.basis(j))[n]).interior_product(list(EE.basis(j+1))[m]).constant_coefficient()\
                   for m in range(len(EE.basis(j+1)))\
                   for n in range(len(EE.basis(j)))])\
                   for j in range(len(EE.gens())+1)}
dee_mat[-1]=matrix(1,0,[])
#-----------The main part: Calculate Boundaries, cycles, filtrations and associated gradeds

#-----------For some reason I forgot, switch here from working with EE itself
#-----------to working with an isomorphic vector space. This might be useless.

#-----------chains, boundaries and cycles

A={k: VectorSpace(SR,len(EE.basis(k))) for k in range(2*d+1)}
Z={k: A[k].subspace(dee_mat[k].transpose().kernel()) for k in range(2*d+1)};
B={k: Z[k].subspace([A[k]([dee_mat[k-1][r,s]\
                  for r in range(dee_mat[k-1].nrows())])\
                  for s in range(dee_mat[k-1].ncols())])\
              for k in range(2*d+1)};

#-----------de-Rham cohomology-----
#HdR={k: Z[k].quotient(B[k]) for k in range(2*d+1)}
print 'HdR:'
{ k: HdR[k].dimension() for k in range(2*d+1)}
print''
#-----------Filtrations on A

def FilA(k,p):
    base=[]
    for j in range(A[k].dimension()):
        if list(EE.basis(k))[j] in Fil(k,p):
            base.append(A[k].basis()[j])
    return A[k].subspace(base)

def FilbarA(k,q):
    base=[]
    for j in range(A[k].dimension()):
        if list(EE.basis(k))[j] in Filbar(k,q):
            base.append(A[k].basis()[j])
    return A[k].subspace(base)


#----------Compute bigraded object associated with filtrations on de Rham

#--auxilliary list of indices
if computeEverything==true:        #------VARIANT I------compute everything
    L=[(t,s) for t in range(2*d+1) for s in range(t+2) ]
else:                              #-----VARIANT II------ (default) just compute gr^{k-1}gr^{k-1}H_dR^{k-1}
    L=[(d-1,d-1), (d-1,d)]


F={(k,p): FilA(k,p) for (k,p) in L}
print 'F: done'

Fbar={(k,q): FilbarA(k,q) for (k,q) in L}
print 'Fbar: done'

FcapZ={(k,p): Z[k].intersection(F[k,p]) for (k,p) in L}
print 'FcapZ: done'

FbarcapZ={(k,q): Z[k].intersection(Fbar[k,q]) for (k,q) in L}
print 'FbarcapZ: done'

FcapZplusB={(k,p): Z[k].span([b for b in list(FcapZ[k,p].basis())]+[b for b in list(B[k].basis())]) for (k,p) in L}
print 'FcapZplusB: done'

FbarcapZplusB={(k,q): Z[k].span([b for b in list(FbarcapZ[k,q].basis())]+[b for b in list(B[k].basis())]) for (k,q) in L}
print 'FbarcapZplusB: done'

def gradeddeRham(k,p,q):
    Upq=   FcapZplusB[k,p].intersection(FbarcapZplusB[k,q])
    Vpincq=FcapZplusB[k,p+1].intersection(FbarcapZplusB[k,q])
    Vpqinc=FcapZplusB[k,p].intersection(FbarcapZplusB[k,q+1])
    V=Upq.subspace([b for b in list(Vpincq.basis())]+ [b for b in list(Vpqinc.basis())])
    Hkpq= Upq.quotient(V)
    return Hkpq

print''
if computeEverything==true:
    grHdR={(k,p,q) : gradeddeRham(k,p,q) for k in range(2*d+1) for p in range(k+1) for q in range (k+1)}
    grbetti={(k,p,q): grHdR[k,p,q].dimension() for k in range(2*d+1) for p in range(k+1) for q in range (k+1)};

    for k in range(2*d+1):
        print 'degree %s' %k
        {(p,q): grbetti[k,p,q] for p in range(k+1) for q in range (k+1)}
else:
    print 'b_{d-1}^{d-1,d-1}:'
    if gradeddeRham(d-1,d-1,d-1).dimension()== 0:
        print 'Zero =(.'
        print 'But try again, next time it will work!'
    else: print 'Jackpot!' 
