read(`code_utilities.txt`): read(`code_dbfiles.txt`): read(`code_filters.txt`): read(`code_selectors.txt`): Help_finsubgames:=proc() print(` SGFSGseq(S,cap) , SGFSGseqext(S,cap,L) `): print(` HasPeriodT(L,T) , GetPeriodLengths(L) `): print(` GetPeriods(L) , IsPossibleEventualPeriod(per,S) `): print(` IsPossibleEventualPeriodAssumptions(per,S) `): print(` HasEventualPeriodT(L,T,k) `): print(` GetEventualPeriodLengths(L,k) `): print(` GetEventualPeriods(L,k) `): print(` NormalizeEventualPeriod(L,per) `): print(` GetPeriodAndPrefix(S) , CanExpandToInclude(S,s) `): print(` GetContractions(S) , GetMinimalContractions(S) `): print(` GetFormattedData(S) `): print(` GeneratePeriodData(cap,out,append:=true) `): print(` PeriodFieldMaximizer(n,file,relprime,perlen) `): print(` LongestPeriod(n,file:=false) `): print(` LongestPeriodRelPrime(n,file:=false) `): print(` LongestPrefix(n,file:=false) `): print(` LongestPrefixRelPrime(n,file:=false) `): print(` PruneFile(infile,outfile,filter) `): print(` CountFile(file,filter) `): print(` CountFileNoReset(file,filter,stopcond:=false) `): print(` AverageFile(file,filter,selector) `): print(` AverageFileNoReset(file,filter,selector,stopcond:=false) `): print(` ExtractFile(file,filter,selector) `): print(` ExtractFileNoReset(file,filter,selector,stopcond:=false) `): print(` ParallelCountAverageExtractFileNoReset(file,countfilters,avgfilters,avgselectors,extfilters,extselectors,stopcond) `): print(` ParallelCountAverageFileNoReset(file,countfilters,avgfilters,avgselectors,stopcond) `): end: #Sprague-Grundy sequence for a given subtraction set S #(first cap+1 elements of the sequence) #The return value is a list whose (i+1)-st element is SG(i) SGFSGseq:=proc(S,cap) local ret,i,j,vals: ret:=[0]: for i from 1 to cap do vals:={}: for j in S do if i-j>=0 then vals:=vals union {ret[i-j+1]}: fi: od: ret:=[op(ret),mex(vals)]: od: return ret: end: #Sprague-Grundy sequence for a given subtraction set S #(first cap+1 elements of the sequence) #The return value is a list whose (i+1)-st element is SG(i) #The argument L is a prefix of the sequence of value you don't #want to recompute SGFSGseqext:=proc(S,cap,L) local ret,i,j,vals: ret:=L: for i from nops(L) to cap do vals:={}: for j in S do if i-j>=0 then vals:=vals union {ret[i-j+1]}: fi: od: ret:=[op(ret),mex(vals)]: od: return ret: end: #Does list L have a period of length T? HasPeriodT:=proc(L,T) local i: for i from T+1 to nops(L) do if L[i-T]<>L[i] then return false: fi: od: return true: end: #Period lengths of list L #Does not include periods longer than L GetPeriodLengths:=proc(L) local i,ret: ret:={}: for i from 1 to nops(L) do if HasPeriodT(L,i) then ret:=ret union {i}: fi: od: return ret: end: #Periods of list L #Does not include periods longer than L GetPeriods:=proc(L) local i,ret: ret:={}: for i in GetPeriodLengths(L) do ret:=ret union {[op(1..i,L)]}: od: return ret: end: #Is is possible that per is an eventual period of the subtraction #game with subtraction set S? IsPossibleEventualPeriod:=proc(per,S) local chk,i,vals,j: if S={} then return evalb({seq(per[i],i=1..nops(per))}={0}): fi: chk:=[op(per),op(per)]: while nops(chk)<=2*max(S) do chk:=[op(chk),op(per)]: od: for i from max(S)+1 to nops(chk) do vals:={}: for j in S do vals:=vals union {chk[i-j]}: od: if mex(vals)<>chk[i] then return false: fi: od: return true: end: #Is is possible that per is an eventual period of the subtraction #game with subtraction set S? #Assumptions: period already known to occur as an eventual period #at least twice for some value #If this is not true, this may not work! IsPossibleEventualPeriodAssumptions:=proc(per,S) local chk, i,vals,j: if S={} then return evalb({seq(per[i],i=1..nops(per))}={0}): elif nops(chk)>=max(S)+1 then return true: fi: chk:=per: while nops(chk)<=2*max(S) do chk:=[op(chk),op(per)]: od: for i from max(S)+1 to 2*max(S) do vals:={}: for j in S do vals:=vals union {chk[i-j]}: od: if mex(vals)<>chk[i] then return false: fi: od: return true: end: #Does list L have an eventual period of length T? #Period must repeat at least k times for it to count HasEventualPeriodT:=proc(L,T,k) local i: if nops(L) < k*T then return false: fi: for i from nops(L) by -1 to nops(L)-k*T+T+1 do if L[i-T]<>L[i] then return false: fi: od: return true: end: #Eventual period lengths of list L #Periods must repeat at least k times for them to count #Periods lengths returned in ascending order GetEventualPeriodLengths:=proc(L,k) local i,ret: ret:=[]: for i from 1 to nops(L) do if HasEventualPeriodT(L,i,k) then ret:=[op(ret),i]: fi: od: return ret: end: #Eventual periods of list L #Periods must repeat at least k times for them to count #Periods returned in increasing length order GetEventualPeriods:=proc(L,k,startpos:=false) local i,ret: ret:=[]: for i in GetEventualPeriodLengths(L,k) do if startpos then ret:=[op(ret),[[op(nops(L)-i+1..nops(L),L)], nops(L)-i]]: else ret:=[op(ret),[op(nops(L)-i+1..nops(L),L)]]: fi: od: return ret: end: #Given an eventual period per of L that is a suffix of L, #return a cyclic shift of it #that is a period of the entire periodic part of L #and return the remaining prefix NormalizeEventualPeriod:=proc(L,per) local i: i:=nops(L): while i>=nops(per) do if [op(i-nops(per)+1..i,L)]<>per then break: fi: i:=i-nops(per): od: while i>0 do if L[i]<>L[i+nops(per)] then break: fi: i:=i-1: od: return [op(i+1..i+nops(per),L)],[op(1..i,L)]: end: #Given a subtraction set S, determine its (normalized) period #and its prefix GetPeriodAndPrefix:=proc(S) local L,cap,periods,per,k, capconst,perconst:#Always good to avoid magic numbers! option remember:#So we can call this multiple times without #needing to recompute capconst:=2: perconst:=2: if S={} then return [0],[]: fi: cap:=capconst*max(S): L:=SGFSGseq(S,cap): while true do periods:=GetEventualPeriods(L,perconst): for per in periods do if IsPossibleEventualPeriodAssumptions(per,S) then k:=max(2,ceil(2*max(S)/nops(per))): if HasEventualPeriodT(L,nops(per),k) then return NormalizeEventualPeriod(L,per): fi: fi: od: cap:=2*cap: L:=SGFSGseqext(S,cap,L): od: end: #Can we replace S by S union {s} and obtain the same SG values? #This uses a naive approach (since when we call this we will have #already called the necessary other functions) #If this were not the case, we could simply check if SG(i)<>SG(i-s) #always, as this would suffice CanExpandToInclude:=proc(S,s): return evalb(GetPeriodAndPrefix(S)= GetPeriodAndPrefix(S union {s})): end: #Get all subsets of S that yield the same SG values GetContractions:=proc(S) local ret,s: option remember: ret:={S}: for s in S do if CanExpandToInclude(S minus {s},s) then ret:=ret union GetContractions(S minus {s}): fi: od: return ret: end: #Get all subsets of S that yield the same SG values #such that no proper subset of them yield those SG values GetMinimalContractions:=proc(S) local ret,s: option remember: ret:={S}: for s in S do if CanExpandToInclude(S minus {s},s) then ret:=(ret minus {S}) union GetMinimalContractions(S minus {s}): fi: od: return ret: end: #Given a subtraction set S, return its period data in the form: #S [numbers in set in increasing order] #F [prefix] #P [period] #G [prefix length] #L [period length] #E [minimal contraction 1] #E [minimal contraction 2] #etc. #(here, brackets do not denote lists, but rather data #field descriptions) GetFormattedData:=proc(S) local E,get: get:=GetPeriodAndPrefix(S): E:=GetMinimalContractions(S): return EncapsulateData(S,get[2],get[1],nops(get[2]), nops(get[1]),E): end: #Generate period data on all subtraction sets whose elements #are <=cap #Output the data to file out #If append is true and file out exists already, #start where you left off GeneratePeriodData:=proc(cap,out,append:=true) local ctr,i,fd,S, appval,line,spline,spl,chrd: appval:=false: if out<>terminal then if append and Exists(out) then fd:=fopen(out,READ): try: Position(fd,infinity): while true do Position(fd,Position(fd)-2): chrd:=ReadCharacter(fd): if chrd="S" then Position(fd,Position(fd)+1): line:=ReadLine(fd): spline:=Split(line): appval:=parse(sprintf("%a",spline)): appval:=[seq(parse(appval[i]), i=1..nops(appval))]: break: fi: od: finally: fclose(fd): end try: fd:=fopen(out,APPEND): else fd:=fopen(out,WRITE): fi: else fd:=out: fi: try: ctr:=InitCounter(cap): if appval<>false then for i in appval do ctr[i]:=1: od: i:=1: ctr:=IncCounter(ctr): fi: while ctr<>false do S:=CounterToSet(ctr): fprintf(fd,"%s\n\n", GetFormattedData(S)): ctr:=IncCounter(ctr): od: finally: if out<>terminal then fclose(fd): fi: end try: end: #Auxiliary function called by the next few functions #n=max of subtraction set #file=file to look in (or false if compute on the fly) #relpime=true if sets must be relatively prime, false otherwise #perlen=true if we are interested in period length, false if #prefix length #NOTE: in file mode, may open the file in DB mode #will not close it PeriodFieldMaximizer:=proc(n,file,relprime,perlen) local ctr,S, ret,i,sets,val,fd,data,endedonce,started: ret:=0: sets:={}: endedonce:=false: started:=false: if file<>false then fd:=OpenDB(file): #try: while true do: data:=NextEntry(fd): if data=false then if endedonce then return FAIL: else endedonce:=true: ResetDB(file): fi: fi: S:=data[1]: if max(S) > n and started then break: elif max(S) <> n then continue: elif (not relprime) or igcd(op(S))=1 then started:=true: if perlen then val:=data[5] else val:=data[4] fi: if val > ret then sets:={S}: ret:=val: elif val=ret then sets:=sets union {S}: fi: fi: od: #finally: # fclose(fd): #end try: else ctr:=InitCounter(n-1): while ctr<>false do S:={n} union CounterToSet(ctr): if (not relprime) or igcd(op(S))=1 then if perlen then val:=nops(GetPeriodAndPrefix(S)[1]): else val:=nops(GetPeriodAndPrefix(S)[2]): fi: if val > ret then sets:={S}: ret:=val: elif val=ret then sets:=sets union {S}: fi: fi: ctr:=IncCounter(ctr): od: fi: return ret,sets: end: #Get the longest period for a game with subtraction set's maximum #element equal to n #If file is false, it computes the periods. If file is true, #it gets them from the specified file #Also return all subtraction sets with the maximum period length LongestPeriod:=proc(n,file:=false): return PeriodFieldMaximizer(n,file,false,true): end: #Get the longest period for a game with subtraction set's maximum #element equal to n and the gcd of the elements equal to 1 #If file is false, it computes the periods. If file is true, #it gets them from the specified file #Also return all subtraction sets with the maximum period length LongestPeriodRelPrime:=proc(n,file:=false) return PeriodFieldMaximizer(n,file,true,true): end: #Get the longest prefix for a game with subtraction set's maximum #element equal to n #If file is false, it computes the prefixes. If file is true, #it gets them from the specified file #Also return all subtraction sets with the maximum prefix length LongestPrefix:=proc(n,file:=false): return PeriodFieldMaximizer(n,file,false,false): end: #Get the longest prefix for a game with subtraction set's maximum #element equal to n and the gcd of the elements equal to 1 #If file is false, it computes the prefixes. If file is true, #it gets them from the specified file #Also return all subtraction sets with the maximum prefix length LongestPrefixRelPrime:=proc(n,file:=false) return PeriodFieldMaximizer(n,file,true,false): end: ##DATA #LongestPeriod #1: 2, {{1}} #2: 4, {{2}} #3: 6, {{3}} #4: 8, {{4}} #5: 10, {{5}} #6: 12, {{6}} #7: 22, {{2, 5, 7}} #8: 25, {{1, 4, 7, 8}} #9: 28, {{2, 3, 6, 7, 9}} #10: 58, {{2, 3, 6, 7, 9, 10}} #11: 54, {{3, 8, 11}} #12: 87, {{2, 3, 5, 6, 7, 9, 12}} #13: 114, {{2, 3, 6, 7, 10, 13}} #14: 144, {{3, 4, 5, 9, 10, 11, 12, 14}} #15: 189, {{3, 4, 8, 10, 11, 12, 15}} #16: 336, {{3, 4, 5, 7, 9, 11, 12, 16}} #17: 306, {{2, 3, 4, 7, 8, 10, 13, 14, 16, 17}} #18: 456, {{3, 4, 6, 7, 11, 12, 14, 18}}] #LongestPeriodRelPrime #1: 2, {{1}} #2: 3, {{1, 2}} #3: 5, {{2, 3}} #4: 7, {{3, 4}, {1, 3, 4}} #5: 9, {{4, 5}} #6: 11, {{5, 6}, {1, 5, 6}, {2, 5, 6}, {1, 3, 5, 6}} #7: 22, {{2, 5, 7}} #8: 25, {{1, 4, 7, 8}} #9: 28, {{2, 3, 6, 7, 9}} #10: 58, {{2, 3, 6, 7, 9, 10}} #11: 54, {{3, 8, 11}} #12: 87, {{2, 3, 5, 6, 7, 9, 12}} #13: 114, {{2, 3, 6, 7, 10, 13}} #14: 144, {{3, 4, 5, 9, 10, 11, 12, 14}} #15: 189, {{3, 4, 8, 10, 11, 12, 15}} #16: 336, {{3, 4, 5, 7, 9, 11, 12, 16}} #17: 306, {{2, 3, 4, 7, 8, 10, 13, 14, 16, 17}} #18: 456, {{3, 4, 6, 7, 11, 12, 14, 18}}] #LongestPrefix #1: 0, all #2: 0, all #3: 0, all #4: 0, all #5: 0, all #6: 0, all #7: 8, {{2, 4, 7}} #8: 13, {{2, 3, 5, 8}} #9: 27, {{2, 3, 6, 8, 9}} #10: 30, {{3, 7, 8, 10}} #11: 41, {{2, 3, 8, 10, 11}} #12: 144, {{3, 4, 8, 9, 10, 12}} #13: 131, {{2, 3, 5, 6, 7, 9, 12, 13}} #14: 265, {{2, 3, 10, 11, 12, 14}} #15: 363, {{2, 3, 5, 6, 9, 10, 12, 15}} #16: 357, {{2, 3, 5, 8, 11, 13, 15, 16}} #17: 413, {{2, 4, 5, 6, 7, 10, 11, 12, 13, 14, 16, 17}} #18: 557, {{1, 4, 5, 10, 13, 14, 17, 18}}] #(Line 1704036 in data.txt) #Prune a DB file, infile, and writes the result to outfile #Prunes using the function filter, which must take 6 arguments #in the order S,P,F,G,L,E and return true or false PruneFile:=proc(infile,outfile,filter) local fd,data,ofd: fd:=OpenDB(infile): ResetDB(fd): data:=NextEntry(fd): try: ofd:=fopen(outfile,WRITE): while data<>false do if filter(data) then fprintf(ofd,"%s\n\n", EncapsulateData(data)): fi: data:=NextEntry(fd): od: finally: fclose(ofd): end try: end: #Count entries in a DB file, file, satisfying the function filter, #which must take 6 arguments #in the order S,P,F,G,L,E and return true or false CountFile:=proc(file,filter) local fd,data,ret: fd:=OpenDB(file): ResetDB(fd): data:=NextEntry(fd): ret:=0: while data<>false do if filter(data) then ret:=ret+1: fi: data:=NextEntry(fd): od: return ret: end: #Count entries in a DB file, file, satisfying the function filter, #which must take 6 arguments #in the order S,P,F,G,L,E and return true or false #does not reset the DB #stops looking when found at least one and stopcond fails to hold #stopcond defaults to filter CountFileNoReset:=proc(file,filter,stopcond:=false) local fd,data, ret,found,scd: fd:=OpenDB(file): data:=NextEntry(fd): if stopcond=false then scd:=filter: else scd:=stopcond: fi: ret:=0: found:=false: while data<>false do if filter(data) then ret:=ret+1: found:=true elif found and not scd(data) then PrevEntry(fd): break: fi: data:=NextEntry(fd): od: return ret: end: #Average entries in a DB file, file, (the fields specified by #selector) satisfying the function filter, #which must take 6 arguments #in the order S,P,F,G,L,E and return true or false #(selector takes the same 6 arguments and must return #an number) #returns FAIL if would divide by 0 AverageFile:=proc(file,filter,selector) local fd,data,ret,count: fd:=OpenDB(file): ResetDB(fd): data:=NextEntry(fd): ret:=0: count:=0: while data<>false do if filter(data) then ret:=ret+selector(data): count:=count+1: fi: data:=NextEntry(fd): od: if count=0 then return FAIL: fi: return ret/count: end: #Average entries in a DB file, file, (the fields specified by #selector) satisfying the function filter, #which must take 6 arguments #in the order S,P,F,G,L,E and return true or false #(selector takes the same 6 arguments and must return #an number) #does not reset the DB #stops looking when found at least one and stopcond fails to hold #stopcond defaults to filter #returns FAIL if would divide by 0 AverageFileNoReset:=proc(file,filter,selector,stopcond:=false) local fd,data,ret,found,scd,count: fd:=OpenDB(file): data:=NextEntry(fd): if stopcond=false then scd:=filter: else scd:=stopcond: fi: ret:=0: count:=0: found:=false: while data<>false do if filter(data) then ret:=ret+selector(data): count:=count+1: found:=true elif found and not scd(data) then PrevEntry(fd): break: fi: data:=NextEntry(fd): od: if count=0 then return FAIL: fi: return ret/count: end: #Extract from a DB file, file #Uses the function filter, which must take 6 arguments #in the order S,P,F,G,L,E and return true or false, to decide #what entries to extract #Outputs the result of applying selector to each of those entries #(selector takes the same 6 arguments) ExtractFile:=proc(file,filter,selector) local fd,data,ret: fd:=OpenDB(file): ResetDB(fd): data:=NextEntry(fd): ret:=[]: while data<>false do if filter(data) then ret:=[op(ret),selector(data)]: fi: data:=NextEntry(fd): od: return ret: end: #Extract from a DB file, file #Uses the function filter, which must take 6 arguments #in the order S,P,F,G,L,E and return true or false, to decide #what entries to extract #Outputs the result of applying selector to each of those entries #(selector takes the same 6 arguments) #does not reset the DB #stops looking when found at least one and stopcond fails to hold #stopcond defaults to filter ExtractFileNoReset:=proc(file,filter,selector,stopcond:=false) local fd,data,ret,found,scd: fd:=OpenDB(file): data:=NextEntry(fd): if stopcond=false then scd:=filter: else scd:=stopcond: fi: ret:=[]: found:=false: while data<>false do if filter(data) then ret:=[op(ret),selector(data)]: found:=true elif found and not scd(data) then PrevEntry(fd): break: fi: data:=NextEntry(fd): od: return ret: end: #Do averages/counts/extracts in parallel #countfilters is a list of filters for counting #avgfilters and avgselectors are lists of the same lengths of #filters/selectors for averaging #extfilters and extselectors are lists of the same lengths of #filters/selectors for extracting #everything uses stopcond as a stop condition #returns FAIL if would divide by 0 ParallelCountAverageExtractFileNoReset:=proc(file,countfilters,avgfilters,avgselectors,extfilters,extselectors,stopcond) local fd,data,rets,found,counts,nums,i,filter,any, divz,exts: fd:=OpenDB(file): data:=NextEntry(fd): rets:=[seq(0,i=1..nops(avgfilters))]: counts:=[seq(0,i=1..nops(avgselectors))]: nums:=[seq(0,i=1..nops(countfilters))]: exts:=[seq([],i=1..nops(extfilters))]: found:=false: while data<>false do any:=false: for i from 1 to nops(countfilters) do filter:=countfilters[i]: if filter(data) then nums[i]:=nums[i]+1: found:=true: any:=true: fi: od: for i from 1 to nops(avgfilters) do filter:=avgfilters[i]: if filter(data) then rets[i]:=rets[i]+avgselectors[i](data): counts[i]:=counts[i]+1: found:=true: any:=true: fi: od: for i from 1 to nops(extfilters) do filter:=extfilters[i]: if filter(data) then exts[i]:=[op(exts[i]),extselectors[i](data)]: found:=true: any:=true: fi: od: if found and not any and not stopcond(data) then PrevEntry(fd): break: fi: data:=NextEntry(fd): od: divz:=proc(rt,ct): if ct=0 then return FAIL: fi: return rt/ct: end: return nums,[seq(divz(rets[i],counts[i]),i=1..min(nops(rets), nops(counts)))],exts: end: #Here for backwards compatibility ParallelCountAverageFileNoReset:=proc(file,countfilters,avgfilters,avgselectors,stopcond): return ParallelCountAverageExtractFileNoReset(file, countfilters,avgfilters,avgselectors,[],[],stopcond): end: ##DATA: #1, 1, 2, 4, 7, 12, 25, 39, 70, 136, 248, 428, 802, 1650, 2888, #6025, 11358, 23114, 45612 (number of non-contractible games #with n as maximum move) #1, 1, 2, 4, 7, 11, 20, 27, 45, 70, 114, 154, 257, 373, 587, #883, 1239, 1803, 2625 (number of non-contractible games #with n as maximum move that achieve the theoretical maximum #SG value) #1, 2, 3, 6, 8, 15, 22, 36, 56, 96, 153, 237, 389, 646, 1018, #1644, 2398, 3908, 5880 (number of games with n as maximum move #that achieve the theoretical maximum SG value) #1, 4, 10, 26, 73, 170, 384, 866, 1730, 4105, 8394, 18330, 38146 #(number of non-contractible games with n as a maximum move with #a nonempty prefix in their Nim sequence) [first term n=7] #1, 4, 10, 29, 90, 223, 541, 1355, 2780, 6892, 15289, 34172, 73655 #(number of games with n as a maximum move with #a nonempty prefix in their Nim sequence) [first term n=7] #[All these sequences appear to be growing exponentially] # #0., 0., 0., 0., 0., 0., 0.1250000000, 0.3515625000, 0.6210937500, 0.8437500000, 1.617187500, 2.506835938, 3.593994141, 4.867675781, 5.643615723, 8.099121094, 10.16693115, 13.00215149, 15.87391281 #(average prefix length as a function of maximum move) #2., 3.500000000, 4.250000000, 5.875000000, 6.187500000, 8.218750000, 8.625000000, 10.35156250, 11.27343750, 12.85351562, 13.69140625, 16.00732422, 16.62060547, 18.49768066, 19.96502686, 22.05773926, 23.08746338, 25.37014008, 26.84245300 #(average period length as a function of maximum move) #0., 0., 0., 0., 0., 0., 0.3200000000, 1.153846154, 2.271428571, 2.904411765, 5.669354839, 9.901869159, 14.23815461, 17.98424242, 23.13642659, 31.57493776, 39.86916711, 51.34316864, 62.57131895 # (average prefix length as a function of maximum move, #non-contractible games) #2., 3., 4.500000000, 6., 7.428571429, 8.916666667, 10.44000000, 11.76923077, 13.12857143, 15.28676471, 16.97580645, 19.76635514, 21.22194514, 24.43454545, 26.36045706, 30.22804979, 32.48873041, 36.29497274, 40.25596334 (average period length as a function of #maximum move, non-contractible games) #8., 11.25000000, 15.90000000, 15.19230769, 19.26027397, 24.92941176, 29.73697917, 34.26558891, 38.62312139, 46.34323995, 53.94734334, 64.74337152, 74.81788392 (average prefix length as a function of #maximum move, non-contractible games with a prefix) #3., 7.500000000, 10.90000000, 11.92307692, 14.24657534, 21.41176471, 21.25000000, 24.16050808, 26.40578035, 30.93861145, 32.97676912, 37.06072013, 40.91676716 (average period length as a function #of maximum move, non-contractible games with a prefix) #8., 11.25000000, 15.90000000, 14.89655172, 18.40000000, 23.02242152, 27.21072089, 29.42878229, 33.26079137, 38.50725479, 43.58035189, 49.87176636, 56.49651755 (average prefix length as a function of #maximum move, games with a prefix) #3., 7.500000000, 10.90000000, 11.13793103, 13.35555556, 20.06726457, 19.64140481, 21.52767528, 24.10287770, 27.65917005, 28.75021257, 32.01934332, 34.37748965 (average period length as a function #of maximum move, games with a prefix)