def test_factoradic_iterate_permutations(self): for j in range3(20): elements = list(range3(3)) # factoradic = Factoradic.padded_to_length_s(number_to_factoradic(j), len(elements)) # not required (visual) factoradic = Factoradic.number_to_factoradic(j) # print j, factoradic, generate_permutation_from_factoradic_inplace(factoradic, elements) # this also works... # ... because we regenerate "elements" every loop, but generally we don't want to modify parameters print(j, factoradic, Factoradic.generate_permutation_from_factoradic(factoradic, elements))
def test_factoradic_iteration(self): f_j = Factoradic.number_to_factoradic(0) for j in range3(0, 720): # print(j, number_to_factoradic(j), "f_j", f_j) assert Factoradic.number_to_factoradic(j) == f_j, "ERROR factoradics don't match" f_j = Factoradic.next_factoradic(f_j) assert Factoradic.number_to_factoradic(j) != f_j, "ERROR consecutive factoradics shouldn't match"
def test_large_permutation(self): # example found online, with a big Mersenne prime N = (2 ** 607) - 1 print("with a Factoradic object:") print("N = (2 ** 607) - 1 # =>", N) f_1 = Factoradic(N) print("factoradic(N) =>", f_1) print("length of factoradic(N):", f_1.length()) perm_f1 = f_1.permutation_inplace(list(range3(f_1.length()))) print("permutation number factoradic(N) of the ordered list from [0, 1, 2, ... 112] =>", perm_f1) assert f_1.length() == 113 resL = "[2L, 77L, 30L, 88L, 71L, 102L, 97L, 70L, 4L, 12L, 75L, 71L, 27L, 9L, 91L, 43L, 75L, 58L, 30L, 73L," resL+= " 10L, 11L, 68L, 33L, 77L, 79L, 50L, 80L, 41L, 77L, 48L, 5L, 32L, 78L, 25L, 74L, 0L, 9L, 49L, 3L, 43L," resL+= " 14L, 6L, 20L, 2L, 6L, 61L, 14L, 29L, 7L, 37L, 41L, 5L, 15L, 30L, 54L, 26L, 0L, 41L, 19L, 29L, 50L, 6L," resL+= " 6L, 2L, 25L, 6L, 8L, 44L, 10L, 26L, 13L, 16L, 0L, 14L, 4L, 5L, 24L, 24L, 30L, 22L, 6L, 6L, 29L, 10L," resL+= " 24L, 12L, 23L, 3L, 20L, 15L, 20L, 16L, 11L, 12L, 7L, 2L, 15L, 10L, 5L, 5L, 8L, 6L, 7L, 2L, 0L, 0L," resL+= " 1L, 0L, 1L, 0L, 1L, 0L]" res = "[2, 77, 30, 88, 71, 102, 97, 70, 4, 12, 75, 71, 27, 9, 91, 43, 75, 58, 30, 73," res += " 10, 11, 68, 33, 77, 79, 50, 80, 41, 77, 48, 5, 32, 78, 25, 74, 0, 9, 49, 3, 43," res += " 14, 6, 20, 2, 6, 61, 14, 29, 7, 37, 41, 5, 15, 30, 54, 26, 0, 41, 19, 29, 50, 6," res += " 6, 2, 25, 6, 8, 44, 10, 26, 13, 16, 0, 14, 4, 5, 24, 24, 30, 22, 6, 6, 29, 10," res += " 24, 12, 23, 3, 20, 15, 20, 16, 11, 12, 7, 2, 15, 10, 5, 5, 8, 6, 7, 2, 0, 0," res += " 1, 0, 1, 0, 1, 0]" assert str(f_1) == resL or str(f_1) == res # annoying Python2-3 compatibility glitch workaround perm_str = "[2, 78, 31, 91, 73, 107, 102, 72, 5, 14, 82, 77, 30, 11, 104, 49, 87, 65, 36, 88, 13, 16, 84, 42," perm_str += " 98, 101, 61, 106, 52, 103, 60, 7, 43, 111, 34, 108, 0, 17, 69, 6, 63, 24, 12, 35, 4, 18, 99, 28," perm_str += " 53, 20, 67, 75, 15, 37, 58, 105, 54, 1, 85, 45, 64, 110, 22, 23, 9, 62, 26, 32, 112, 39, 74, 46," perm_str += " 51, 3, 50, 25, 29, 83, 86, 96, 80, 38, 40, 109, 55, 94, 59, 95, 21, 92, 76, 97, 81, 66, 70, 47," perm_str += " 19, 100, 71, 44, 48, 79, 57, 89, 27, 8, 10, 41, 33, 68, 56, 93, 90]" assert str(perm_f1) == perm_str # test the not-in-place permutation method assert perm_f1 == f_1.permutation(list(range3(f_1.length())))
def test_factoradic_iteration_timed(self): start = (2 ** 607) - 1 start **= 20 # we need a really large number to even get significant times in a modern computer end = start + 100 # avg: 0.011 per iteration in my comp. for number_to_factoradic, 0.001 for next_factoradic f_j = Factoradic.number_to_factoradic(start) profiled_number_to_factoradic = profile_time(Factoradic.number_to_factoradic) profiled_next_factoradic = profile_time(Factoradic.next_factoradic) for j in range3(start, end): # print(j, number_to_factoradic(j), "f_j", f_j) assert profiled_number_to_factoradic(j) == f_j, "ERROR factoradics don't match" f_j = profiled_next_factoradic(f_j) assert profiled_number_to_factoradic(j) != f_j, "ERROR consecutive factoradics shouldn't match" calc_prof_data() print_prof_data()
def cascade_factoradic_digits_inplace( factoradic_value): # in-place. Makes the factoradic well-formed reversed_place = 1 factoradic_value[len(factoradic_value) - 1] = 0 for i in range3(len(factoradic_value) - 2, 0, -1): reversed_place += 1 if factoradic_value[i] >= reversed_place: factoradic_value[i - 1] += factoradic_value[i] // reversed_place factoradic_value[i] %= reversed_place reversed_place += 1 while factoradic_value[0] >= reversed_place: factoradic_value.insert(0, factoradic_value[0] // reversed_place) factoradic_value[1] %= reversed_place reversed_place += 1 return factoradic_value
def test_padding_zeroes(self): f = Factoradic(5) # [2,1,0] list_no_pad = Factoradic.padded_to_length_s(f.v, f.length()) # should make no change assert f == list_no_pad, "ERROR: padding to the same length should make no change" list_f = Factoradic.padded_to_length_s(f.v, 5) assert list_f == [0,0,2,1,0] assert f.v == [2,1,0] print("PADDING", list_f, f.v) for _ in range3(100): # tests random paddings and verifies they stay consistent f = Factoradic(random.randint(0,100000000)) f_padding_size = random.randint(0,50) list_paddedf = Factoradic.padded_to_length_s(f.v, f.length() + f_padding_size) assert len(list_paddedf) == f.length() + f_padding_size assert f.v == list_paddedf[f_padding_size:]
def generate_permutation_from_factoradic_inplace( factoradic_value, elements): # modifies 'elements' in place res = [] # this bit makes the permutations cycle through: # permutation number [1, 0, 0, 0] <=> permutation number [0, 0, 0] size_diff = len(factoradic_value) - len(elements) if size_diff > 0: factoradic_value = factoradic_value[size_diff:] # factoradic_value needs to be padded to the length of "elements" if size_diff < 0: factoradic_value = Factoradic.padded_to_length_s( factoradic_value, len(elements)) # at this point, lengths must match for i in range3(len(factoradic_value)): res.append(elements.pop(factoradic_value[i])) return res
def test_increment(self): f = Factoradic(2) f.increment(5) f_ = Factoradic(f) # copy constructor f_.increment(0) assert f == Factoradic(7) assert f == f_ f0 = Factoradic(0) f0.increment(1) assert f0 == Factoradic([1,0]) assert f0 == Factoradic(0).next() f0 = Factoradic(0) f0.increment(5) assert f0 == Factoradic(5) for _ in range3(100): x = random.randint(0,10000) y = random.randint(0,10000) f_x = Factoradic(x) f_inc = Factoradic(x+y) f_x.increment(y) assert f_x == f_inc, "ERROR in random increment" f_x_pluszero = Factoradic(f_x) f_x_pluszero.increment(0) assert f_x_pluszero == f_x f_x_plusone = Factoradic(f_x) f_x_plusone.increment(1) assert f_x_plusone == f_x.next() with self.assertRaises(FactoradicException): f.increment(-1) # negatives not allowed with self.assertRaises(FactoradicException): f.increment("five") # wrong type with self.assertRaises(FactoradicException): f.increment([1,1,0]) # would be factoradic for 3, but we don't accept this at the moment (convert to int)
def test_large_permutation(self): # example found online, with a big Mersenne prime N = (2 ** 607) - 1 print("N = (2 ** 607) - 1 # =>", N) n_f = Factoradic.number_to_factoradic(N) print("factoradic(N) =>", n_f) print("length of factoradic(N):", len(n_f)) perm_n_f = Factoradic.generate_permutation_from_factoradic(n_f, list(range3(len(n_f)))) print("permutation number factoradic(N) of the ordered list from [0, 1, 2, ... 112] =>", perm_n_f) # observation: by definition, each element of perm_n_f is >= to n_f for every position, ... # ... for the canonical [0...len(n_f)] list of elements assert len(n_f) == 113 resL = "[2L, 77L, 30L, 88L, 71L, 102L, 97L, 70L, 4L, 12L, 75L, 71L, 27L, 9L, 91L, 43L, 75L, 58L, 30L, 73L," resL+= " 10L, 11L, 68L, 33L, 77L, 79L, 50L, 80L, 41L, 77L, 48L, 5L, 32L, 78L, 25L, 74L, 0L, 9L, 49L, 3L, 43L," resL+= " 14L, 6L, 20L, 2L, 6L, 61L, 14L, 29L, 7L, 37L, 41L, 5L, 15L, 30L, 54L, 26L, 0L, 41L, 19L, 29L, 50L, 6L," resL+= " 6L, 2L, 25L, 6L, 8L, 44L, 10L, 26L, 13L, 16L, 0L, 14L, 4L, 5L, 24L, 24L, 30L, 22L, 6L, 6L, 29L, 10L," resL+= " 24L, 12L, 23L, 3L, 20L, 15L, 20L, 16L, 11L, 12L, 7L, 2L, 15L, 10L, 5L, 5L, 8L, 6L, 7L, 2L, 0L, 0L," resL+= " 1L, 0L, 1L, 0L, 1L, 0L]" res = "[2, 77, 30, 88, 71, 102, 97, 70, 4, 12, 75, 71, 27, 9, 91, 43, 75, 58, 30, 73," res += " 10, 11, 68, 33, 77, 79, 50, 80, 41, 77, 48, 5, 32, 78, 25, 74, 0, 9, 49, 3, 43," res += " 14, 6, 20, 2, 6, 61, 14, 29, 7, 37, 41, 5, 15, 30, 54, 26, 0, 41, 19, 29, 50, 6," res += " 6, 2, 25, 6, 8, 44, 10, 26, 13, 16, 0, 14, 4, 5, 24, 24, 30, 22, 6, 6, 29, 10," res += " 24, 12, 23, 3, 20, 15, 20, 16, 11, 12, 7, 2, 15, 10, 5, 5, 8, 6, 7, 2, 0, 0," res += " 1, 0, 1, 0, 1, 0]" assert str(n_f) == resL or str(n_f) == res # annoying Python2-3 compatibility glitch workaround perm_str = "[2, 78, 31, 91, 73, 107, 102, 72, 5, 14, 82, 77, 30, 11, 104, 49, 87, 65, 36, 88, 13, 16, 84, 42," perm_str += " 98, 101, 61, 106, 52, 103, 60, 7, 43, 111, 34, 108, 0, 17, 69, 6, 63, 24, 12, 35, 4, 18, 99, 28," perm_str += " 53, 20, 67, 75, 15, 37, 58, 105, 54, 1, 85, 45, 64, 110, 22, 23, 9, 62, 26, 32, 112, 39, 74, 46," perm_str += " 51, 3, 50, 25, 29, 83, 86, 96, 80, 38, 40, 109, 55, 94, 59, 95, 21, 92, 76, 97, 81, 66, 70, 47," perm_str += " 19, 100, 71, 44, 48, 79, 57, 89, 27, 8, 10, 41, 33, 68, 56, 93, 90]" assert str(perm_n_f) == perm_str
def factoradic_to_number(factoradic_value): res = 0 for i in range3(len(factoradic_value) - 1): res += factoradic_value[i] * fact(len(factoradic_value) - 1 - i) return res
def is_well_formed_factoradic(factoradic_value): for i in range3(len(factoradic_value)): if int(factoradic_value[i]) >= (len(factoradic_value) - i): return False return True