{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "##This code computes various d invariants of large surgery, as well as the invariants Y_c\n",
    "##There are two classes, CF and CFKinfty\n",
    "##The class CF is a class which represents a free f.g. chain complex over F[U]\n",
    "## The class CFKinfty represents a knot-like chain complex resembing CFK-infty\n",
    "##The most important methods are\n",
    "##CFKinfty()### (to initialize)\n",
    "##Tensor([C_1,C_2,C_3,...,C_n])### (to tensor CFKinfty complexes together)\n",
    "##PrintYGrid(C)### computes and prints all the Y_{c,g} invariants\n",
    "##C.dual() computes the dual knot complex of a CFKinfty complex C\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "class CF(object):\n",
    "    #gen should be a list of Maslov gradings (of the generators over F[U])\n",
    "    #diff is a list of lists, of the same length as gen.\n",
    "    #e.g. [[],[1,2],[3],[]]. in diff[i] we put all of the indices of the intersection points such that there is \n",
    "    # a nonzero differential from i to j (the U-powers are filled in later).\n",
    "    # We assume that the complex inputted has HF^infty equal to one tower,\n",
    "    # supported in even gradings\n",
    "    def __init__(self,gen,diff):\n",
    "        self.gen=gen\n",
    "        self.diff=diff\n",
    "        self.rank=len(gen)\n",
    "        \n",
    "        \n",
    "        \n",
    "class CFKinfty(object):\n",
    "    #gen should be a list of 3 element lists of the form [gr,i,j] represent the generators of CFK^infty.\n",
    "    #An element [gr,i,j] represents x u^i v^j (so i and j are opposite from Ozsvath and Szabo's convention)\n",
    "    #these are generators of the Alexander grading 0 version, so we assume that A(x)+j-i=0.\n",
    "    #Here, gr is the Maslov (or homological) grading.\n",
    "    #They can be any generators, so, e.g., incrementing i and j both by 1 is ok and has no effect.\n",
    "    #diff is a list of lists, of the same length as gen.\n",
    "    #diff[i] is a list of integers, and j is in diff[i] if and only if there is an arrow from i to j (with some weighting)\n",
    "    #(The weighting need not be entered, since it is determined by the grading)\n",
    "    #length of diff and gen should be equal.\n",
    "    def __init__(self, gen, diff):\n",
    "        self.gen=gen\n",
    "        self.diff=diff\n",
    "        self.rank=len(gen)\n",
    "    #the following returns the generators and differential for CF of the large surgeries complex\n",
    "    def AComplex(self,indexn):\n",
    "        NewGens=[]\n",
    "        for oldg in self.gen:\n",
    "            oldgrading=oldg[0]\n",
    "            indexi=oldg[1]\n",
    "            indexj=oldg[2]\n",
    "            newgrading=oldgrading+2*min(indexi,indexj+indexn)\n",
    "            NewGens.append(newgrading)\n",
    "            diffcopy=deepcopy(self.diff)\n",
    "        Complex=CF(NewGens,diffcopy)\n",
    "        return Complex\n",
    "    def dual(self):\n",
    "        NewGens=[[-x[0],-x[1],-x[2]] for x in self.gen]\n",
    "        NewDiff=[[] for i in range(self.rank)]\n",
    "        for i in range(self.rank):\n",
    "            for j in self.diff[i]:\n",
    "                NewDiff[j].append(i)\n",
    "        return CFKinfty(NewGens,NewDiff)\n",
    "    \n",
    "#input CFK^infty-cx\n",
    "#output is a CF-minus cx\n",
    "#n>=0 assumed\n",
    "#could instead just tensor C with S_{-n} (negative staircase with n length 1 steps)\n",
    "def StairCaseCx(C, n):\n",
    "    if n==0:\n",
    "        return C.AComplex(0)\n",
    "    #henceforth we assume n>0\n",
    "    OldGens=C.gen\n",
    "    OldDiff=C.diff\n",
    "    NewGens=[]\n",
    "    rank=C.rank\n",
    "    NewDiff=[[] for x in range((2*n+1)*rank)]\n",
    "    #i enumerates all of the summands of S-n\n",
    "    for i in range(2*n+1):\n",
    "        #i in this range corresponds to the summand A_{-n+i}, so we set s=-n+i\n",
    "        s=n-i\n",
    "        for x in OldGens:\n",
    "            #we now add generators, with shifts in grading as in AComplex, as well as with the formal shifts\n",
    "            grnew=x[0]+2*min(x[1],x[2]+s)-s+n\n",
    "            NewGens.append(grnew)\n",
    "        for j in range(rank):\n",
    "            for y in C.diff[j]:\n",
    "            #add the internal differentials\n",
    "                NewDiff[i*rank+j].append(i*rank+y)\n",
    "    #we now add the cone arrows to A_n (left most)\n",
    "    for i in range(rank):\n",
    "        NewDiff[i].append(i+rank)\n",
    "    # now add the cone arrows for the right most arrow A_{-n}\n",
    "    for i in range(rank):\n",
    "        NewDiff[(2*n)*rank+i].append((2*n-1)*rank+i)\n",
    "    for i in range(1,n):\n",
    "        #we now add the remaining cone arrows\n",
    "        for j in range(rank):\n",
    "            NewDiff[2*i*rank+j].append((2*i-1)*rank+j)\n",
    "            NewDiff[2*i*rank+j].append((2*i+1)*rank+j)\n",
    "    return CF(NewGens,NewDiff)\n",
    "\n",
    "# Input is a CFK-infty complex and an integer n. Output is the CF complex A_n\n",
    "def AComplex(C,n):\n",
    "    return C.AComplex(n)\n",
    "    \n",
    "    \n",
    "#input is two CFKinfty complexes\n",
    "#Output is their knot-tensor product.\n",
    "#NOTE: There is a function which tensors a list of CFK complexes together (As many as one likes)\n",
    "#See the function Tensor, below\n",
    "def Tensor2(C1,C2):\n",
    "    gen1=C1.gen\n",
    "    gen2=C2.gen\n",
    "    diff1=C1.diff\n",
    "    diff2=C2.diff\n",
    "    rank1=C1.rank\n",
    "    rank2=C2.rank\n",
    "    newGens=[]\n",
    "    newDiffs=[[] for x in range(rank1*rank2)]\n",
    "    for i in range(rank1):\n",
    "        gi=gen1[i]\n",
    "        for j in range(rank2):\n",
    "            gj=gen2[j]\n",
    "            gigj=[sum(x) for x in zip(gi, gj)]\n",
    "            newGens.append(gigj)\n",
    "            for k in diff1[i]:\n",
    "                newDiffs[i*rank2+j].append(k*rank2+j)\n",
    "            for m in diff2[j]:\n",
    "                newDiffs[i*rank2+j].append(i*rank2+m)\n",
    "    return CFKinfty(newGens,newDiffs)\n",
    "\n",
    "#input is a knot complex, output is V_0,V_1,V_2,...\n",
    "def Vlist(C):\n",
    "    VInvariants=[]\n",
    "    Index=0\n",
    "    VInvariants.append(-d(C.AComplex(0))/2)\n",
    "    while(not VInvariants[-1]== 0):\n",
    "        Index=Index+1\n",
    "        VInvariants.append(-d(C.AComplex(Index))/2)\n",
    "    return VInvariants\n",
    "\n",
    "# Outputs a list of the Y, similar to Vlist.\n",
    "def Ylist(C):\n",
    "    YInvariants=[]\n",
    "    Index=0\n",
    "    YInvariants.append(-d(StairCaseCx(C,0))/2)\n",
    "    while(not YInvariants[-1]== 0):\n",
    "        Index=Index+1\n",
    "        YInvariants.append(-d(StairCaseCx(C,Index))/2)\n",
    "    return YInvariants\n",
    "    \n",
    "\n",
    "#input is a list of CFKinfty complexes, e.g. Tensor([C_1,C_2,C_3,...,C_n])\n",
    "def Tensor(Complexes):\n",
    "    if len(Complexes)==1:\n",
    "        return Complexes[0]\n",
    "    C=Complexes[0]\n",
    "    for j in range(1, len(Complexes)):\n",
    "        C=Tensor2(C,Complexes[j])\n",
    "    return C\n",
    "        \n",
    "\n",
    "#The following is a helper function:\n",
    "#takes a list and deletes elements that appear twice (and leaves one if they appear an odd number of times)\n",
    "def mod2(L):\n",
    "    K=deepcopy(L)\n",
    "    for x in K:\n",
    "        if L.count(x)>1:\n",
    "            L.remove(x)\n",
    "            L.remove(x)\n",
    "    return L\n",
    "    \n",
    "\n",
    "#The following is a helper function:\n",
    "#Input is old differential\n",
    "#outputs new differential\n",
    "#note, this changes the old differential\n",
    "#We assume that x_i and x_j have the same parity\n",
    "#We assume g(x_j)>g(x_i)\n",
    "#The change of basis is that x_i is replaced by x_i+U^k x_j\n",
    "#x_j is left the same\n",
    "def BasisChange(diff,i,j):\n",
    "    #first changes differentials going to i an j\n",
    "    for x in diff:\n",
    "        if i in x:\n",
    "            x.append(j)\n",
    "            mod2(x)\n",
    "    for y in diff[j]:\n",
    "        diff[i].append(y)\n",
    "        mod2(diff[i])\n",
    "    return diff\n",
    "\n",
    "#The following is a helper function:\n",
    "#The following deletes all arrows with a power of U^i, where i=power\n",
    "# It does this via a change of basis, and then deleting 2 step complexes\n",
    "#Note, the program assumes that there are no arrows weighted by U^n for n<power\n",
    "#(In applications, we first delete the U^0 arrows, then the U^1 arrows then U^2, etc.)\n",
    "#Also, it changes Diff and Gen (i.e. it does not return new ones, but modifies the ones that are passed)\n",
    "def DeleteArrows(Diff,Gen, power):\n",
    "    rank=len(Gen)\n",
    "    #toDelete contains all the generators which will be deleted at the end\n",
    "    toDelete=[]\n",
    "    for x in range(rank):\n",
    "        KnownShortArrow=false\n",
    "        ySpecial=0\n",
    "        for y in Diff[x]:\n",
    "            if (Gen[y]-Gen[x]+1)/2==power:\n",
    "                KnownShortArrow=true\n",
    "                ySpecial=y\n",
    "                break\n",
    "        if KnownShortArrow:\n",
    "            #move ySpecial to the end\n",
    "            specialindex=Diff[x].index(ySpecial)\n",
    "            Diff[x].pop(specialindex)\n",
    "            Diff[x].append(ySpecial)\n",
    "            #Does a change of basis to make Diff[x] consist only of a single item (yspecial)\n",
    "            while len(Diff[x])>1:\n",
    "                BasisChange(Diff, ySpecial,Diff[x][0])\n",
    "            for xOther in range(rank):\n",
    "                if ySpecial in Diff[xOther] and not xOther==x:\n",
    "                    BasisChange(Diff, xOther,x)\n",
    "            toDelete.append(ySpecial)\n",
    "            toDelete.append(x)\n",
    "    toDelete.sort(reverse=True)\n",
    "    # deletes all the entries of toDelete, and shifts Diff correctly each time\n",
    "    for i in toDelete:\n",
    "        del Diff[i]\n",
    "        del Gen[i]\n",
    "        for Dxs in Diff:\n",
    "             for j in range(len(Dxs)):\n",
    "                if Dxs[j]>i:\n",
    "                    Dxs[j]=Dxs[j]-1\n",
    "            \n",
    "#input is a CF complex\n",
    "def d(C):\n",
    "    Diff=deepcopy(C.diff)\n",
    "    Gen=deepcopy(C.gen)\n",
    "    power=0\n",
    "    while(len(Diff)>1):\n",
    "        DeleteArrows(Diff,Gen,power)\n",
    "        power=power+1\n",
    "    return Gen[0]\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "T23Gens=[[-2,0,1],[-2,1,0],[-1,0,0]]\n",
    "T23Diff=[[],[],[0,1]]\n",
    "T23=CFKinfty(T23Gens,T23Diff)\n",
    "\n",
    "Fig8Gens=[[0,0,0],[-1,0,1],[-1,1,0],[0,0,0],[0,0,0]]\n",
    "Fig8Diff=[[1,2],[3],[3],[],[]]\n",
    "Fig8=CFKinfty(Fig8Gens,Fig8Diff)\n",
    "\n",
    "T38Gens=[[-14,7,0],[-13,6,0],[-14,6,2],[-13,5,2],[-14,5,4],[-13,4,4],[-14,4,5],[-13,2,5],[-14,2,6],[-13,0,6],[-14,0,7]]\n",
    "T38Diff=[[],[0,2],[],[2,4],[],[4,6],[],[6,8],[],[8,10],[]]\n",
    "T38=CFKinfty(T38Gens,T38Diff)\n",
    "\n",
    "T213Gens=[[-12,6,0],[-11,5,0],[-12,5,1],[-11,4,1],[-12,4,2],[-11,3,2],[-12,3,3],[-11,2,3],[-12,2,4],[-11,1,4],[-12,1,5],[-11,0,5],[-12,0,6]]\n",
    "T213Diff=[[],[0,2],[],[2,4],[],[4,6],[],[6,8],[],[8,10],[],[10,12],[]]\n",
    "T213=CFKinfty(T213Gens,T213Diff)\n",
    "\n",
    "T45Gens=[[-12,0,6],[-11,0,5],[-12,3,5],[-11,3,3],[-12,5,3],[-11,5,0],[-12,6,0]]\n",
    "T45Diff=[[],[0,2],[],[2,4],[],[4,6],[]]\n",
    "T45=CFKinfty(T45Gens,T45Diff)\n",
    "\n",
    "T56Gens=[[-20, 0, 10],[-19, 0, 9],[-20, 4, 9],[-19, 4, 7],[-20, 7, 7],[-19, 7, 4],[-20, 9, 4],[-19, 9, 0],[-20, 10, 0]]\n",
    "T56Diff=[[], [0, 2], [], [2, 4], [], [4, 6], [], [6, 8], []]\n",
    "T56=CFKinfty(T56Gens,T56Diff)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[3, 3, 2, 2, 2, 1, 1, 1, 0]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Vlist(Tensor([T23,T38]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "SageMath 9.1",
   "language": "sage",
   "name": "sagemath"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}