66from python_toolbox import nifty_collections
77
88
9- ###############################################################################
10-
119_length_of_recurrent_perm_space_cache = {}
1210
1311def calculate_length_of_recurrent_perm_space (k , fbb ):
@@ -26,18 +24,40 @@ def calculate_length_of_recurrent_perm_space(k, fbb):
2624 cache = _length_of_recurrent_perm_space_cache
2725 if not isinstance (fbb , nifty_collections .FrozenBagBag ):
2826 fbb = nifty_collections .FrozenBagBag (fbb )
27+ ### Checking for edge cases: ##############################################
28+ # #
2929 if k == 0 :
3030 return 1
3131 elif k == 1 :
3232 assert fbb
33- # (Works because `FrozenBagBag` has a functioning `__bool__`,
34- # unlike Python's `Counter`.)
33+ # (Works because `FrozenBagBag` has a functioning `__bool__`, unlike
34+ # Python's `Counter`.)
3535 return fbb .n_elements
36+ # #
37+ ### Finished checking for edge cases. #####################################
38+
3639 try :
3740 return cache [(k , fbb )]
3841 except KeyError :
3942 pass
43+
44+ # This is a 2-phase algorithm, similar to recursion but not really
45+ # recursion since we don't want to abuse the stack.
46+ #
47+ # In the first phase, we get all the sub-FBBs that we need to solve for to
48+ # get a solution for this FBB, and then for these sub-FBBs we get the
49+ # sub-sub-FBBs we need to solve in order to solve them, and we continue
50+ # until we reach trivial FBBs.
51+ #
52+ # In the second phase, we'll go over the levels of FBBs, starting with the
53+ # simplest ones and making our way up to the original FBB. The simplest
54+ # FBBs will be solved trivially, and then as they get progressively more
55+ # complex, each FBB will be solved using the solutions of its sub-FBB.
56+ # Every solution will be stored in the global cache.
57+
4058
59+ ### Doing phase one, getting all sub-FBBs: ################################
60+ # #
4161 levels = []
4262 current_fbbs = {fbb }
4363 while len (levels ) < k and current_fbbs :
@@ -47,8 +67,11 @@ def calculate_length_of_recurrent_perm_space(k, fbb):
4767 for fbb_ in current_fbbs if (k_ , fbb_ ) not in cache }
4868 )
4969 current_fbbs = set (itertools .chain (* levels [- 1 ].values ()))
50-
51- # The last level is calculated. Time to make our way up.
70+ # #
71+ ### Finished doing phase one, getting all sub-FBBs. #######################
72+
73+ ### Doing phase two, solving FBBs from trivial to complex: ################
74+ # #
5275 for k_ , level in enumerate (reversed (levels ), (k - len (levels ) + 1 )):
5376 if k_ == 1 :
5477 for fbb_ , sub_fbb_bag in level .items ():
@@ -59,6 +82,8 @@ def calculate_length_of_recurrent_perm_space(k, fbb):
5982 (cache [(k_ - 1 , sub_fbb )] * factor for
6083 sub_fbb , factor in sub_fbb_bag .items ())
6184 )
85+ # #
86+ ### Finished doing phase two, solving FBBs from trivial to complex. #######
6287
6388 return cache [(k , fbb )]
6489
@@ -84,18 +109,40 @@ def calculate_length_of_recurrent_comb_space(k, fbb):
84109 cache = _length_of_recurrent_comb_space_cache
85110 if not isinstance (fbb , nifty_collections .FrozenBagBag ):
86111 fbb = nifty_collections .FrozenBagBag (fbb )
112+ ### Checking for edge cases: ##############################################
113+ # #
87114 if k == 0 :
88115 return 1
89116 elif k == 1 :
90117 assert fbb
91118 # (Works because `FrozenBagBag` has a functioning `__bool__`,
92119 # unlike Python's `Counter`.)
93120 return fbb .n_elements
121+ # #
122+ ### Finished checking for edge cases. #####################################
123+
94124 try :
95125 return cache [(k , fbb )]
96126 except KeyError :
97127 pass
98128
129+ # This is a 2-phase algorithm, similar to recursion but not really
130+ # recursion since we don't want to abuse the stack.
131+ #
132+ # In the first phase, we get all the sub-FBBs that we need to solve for to
133+ # get a solution for this FBB, and then for these sub-FBBs we get the
134+ # sub-sub-FBBs we need to solve in order to solve them, and we continue
135+ # until we reach trivial FBBs.
136+ #
137+ # In the second phase, we'll go over the levels of FBBs, starting with the
138+ # simplest ones and making our way up to the original FBB. The simplest
139+ # FBBs will be solved trivially, and then as they get progressively more
140+ # complex, each FBB will be solved using the solutions of its sub-FBB.
141+ # Every solution will be stored in the global cache.
142+
143+
144+ ### Doing phase one, getting all sub-FBBs: ################################
145+ # #
99146 levels = []
100147 current_fbbs = {fbb }
101148 while len (levels ) < k and current_fbbs :
@@ -105,8 +152,11 @@ def calculate_length_of_recurrent_comb_space(k, fbb):
105152 for fbb_ in current_fbbs if (k_ , fbb_ ) not in cache }
106153 )
107154 current_fbbs = set (itertools .chain (* levels [- 1 ].values ()))
155+ # #
156+ ### Finished doing phase one, getting all sub-FBBs. #######################
108157
109- # The last level is calculated. Time to make our way up.
158+ ### Doing phase two, solving FBBs from trivial to complex: ################
159+ # #
110160 for k_ , level in enumerate (reversed (levels ), (k - len (levels ) + 1 )):
111161 if k_ == 1 :
112162 for fbb_ , sub_fbbs in level .items ():
@@ -116,6 +166,8 @@ def calculate_length_of_recurrent_comb_space(k, fbb):
116166 cache [(k_ , fbb_ )] = sum (
117167 (cache [(k_ - 1 , sub_fbb )] for sub_fbb in sub_fbbs )
118168 )
169+ # #
170+ ### Finished doing phase two, solving FBBs from trivial to complex. #######
119171
120172 return cache [(k , fbb )]
121173
0 commit comments