Last active
February 27, 2016 10:28
-
-
Save gebrkn/8b73949295853bdaf2b9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################################ | |
# that's what you guys wrote | |
################################################ | |
import itertools as it | |
import operator as op | |
def test_evgeny_kluev_A(a, b): | |
equals = it.imap(op.eq, a, b) # compare lists | |
e1, e2 = it.tee(equals) | |
l = it.chain(e1, [True]) | |
r = it.chain([True], e2) | |
borders = it.imap(op.ne, l, r) # delimit equal/non-equal intervals | |
ranges = list(it.islice(it.compress(it.count(), borders), 5)) | |
if len(ranges) == 4: | |
n1, m1 = ranges[0], ranges[1] | |
n2, m2 = ranges[2], ranges[3] | |
elif len(ranges) == 2: | |
n1, m1 = ranges[0], len(a) // 2 | |
n2, m2 = len(a) // 2, ranges[1] | |
else: | |
return None | |
if n1 == len(a) - m2 and m1 == len(a) - n2 \ | |
and a[n1:m1] == b[n2:m2] and b[n1:m1] == a[n2:m2]: | |
return (n1, m1) | |
def test_evgeny_kluev_B(a, b): | |
equals = it.imap(op.eq, a, b[:len(a) // 2]) # compare half-lists | |
e1, e2 = it.tee(equals) | |
l = it.chain(e1, [True]) | |
r = it.chain([True], e2) | |
borders = it.imap(op.ne, l, r) # delimit equal/non-equal intervals | |
ranges = list(it.islice(it.compress(it.count(), borders), 2)) | |
if len(ranges) != 2: | |
return None | |
n, m = ranges[0], ranges[1] | |
nr, mr = len(a) - n, len(a) - m | |
if a[n:m] == b[mr:nr] and b[n:m] == a[mr:nr] \ | |
and a[m:mr] == b[m:mr] and a[nr:] == b[nr:]: | |
return (n, m) | |
def test_evgeny_kluev_C(a, b): | |
length = len(a) | |
half = length // 2 | |
mismatches = it.imap(op.ne, a, b[:half]) # compare half-lists | |
try: | |
n = next(it.compress(it.count(), mismatches)) | |
nr = length - n | |
mr = a.index(b[n], half, nr) | |
m = length - mr | |
except StopIteration: return None | |
except ValueError: return None | |
if a[n:m] == b[mr:nr] and b[n:m] == a[mr:nr] \ | |
and a[m:mr] == b[m:mr] and a[nr:] == b[nr:]: | |
return (n, m) | |
def test_roottwo(a, b): | |
sz = len(a) | |
ds = list(it.compress(it.count(), map(op.ne, a[:sz//2], b[:sz//2]))) | |
n,m = ds[0], ds[-1]+1 | |
if a[n:m] == b[sz-m:sz-n] and b[n:m] == a[sz-m:sz-n] and len(ds) == m - n: | |
return n,m | |
else: | |
return None | |
def test_tom_karzes(a, b): | |
cnt = len(a) | |
ml = [a[i] == b[i] for i in range(cnt)] | |
sl = [i for i in range(cnt) if (i == 0 or ml[i-1]) and not ml[i]] | |
el = [i+1 for i in range(cnt) if not ml[i] and (i == cnt-1 or ml[i+1])] | |
assert(len(sl) == len(el)) | |
range_cnt = len(sl) | |
if range_cnt == 1: | |
start1 = sl[0] | |
end2 = el[0] | |
if (end2 - start1) % 2 != 0: | |
return None | |
end1 = (start1 + end2) // 2 | |
start2 = end1 | |
elif range_cnt == 2: | |
start1, start2 = sl | |
end1, end2 = el | |
else: | |
return None | |
if end1 - start1 != end2 - start2: | |
return None | |
if start1 != cnt - end2: | |
return None | |
if a[start1:end1] != b[start2:end2]: | |
return None | |
if b[start1:end1] != a[start2:end2]: | |
return None | |
return start1, end1 | |
def test_alexis(A, B): | |
same = True | |
for i, (a, b) in enumerate(zip(A,B)): | |
if same and a != b: # Found the start of a presumed transposition | |
same = False | |
n = i | |
firstval = a # First element of the transposed piece | |
elif (not same) and (a == b or b == firstval): # end of the transposition | |
m = i | |
break | |
# Construct the transposed string, compare it to B | |
origin = A[n:m] | |
if n == 0: # swap begins at the edge | |
dest = A[-m:] | |
B_expect = dest + A[m:-m] + origin | |
else: | |
dest = A[-m:-n] | |
B_expect = A[:n] + dest + A[m:-m] + origin + A[-n:] | |
if B_expect == B: | |
return n, m | |
def test_ian(A, B): | |
Al = A[:len(A)//2] | |
Ar = A[len(A)//2:] | |
Bl = B[:len(B)//2] | |
Br = B[len(B)//2:] | |
la = len(Al) | |
L = len(A) | |
for i in range(len(Al)): | |
lai = la - i | |
for j in range(1, lai + 1): | |
k = lai - j | |
if Al[i] != Br[k] or Ar[k] != Bl[i]: #the key for efficient computation is here: do not proceed unnecessarily | |
continue | |
n = i | |
m = i + j | |
if A[n:m] == B[L-m:L-n] and B[n:m] == A[L-m:L-n]: #possibly symmetric | |
if A[0:n] == B[0:n] and A[m:L-m] == B[m:L-m] and A[L-n:] == B[L-n:]: | |
return n, m | |
return None | |
def test_viraptor(A, B): | |
L = len(A) | |
Br = B[L//2:]+B[:L//2] | |
same_full = [a==b for (a,b) in zip(A, Br)] | |
same_part = [a+b for (a,b) in zip(same_full[L//2:], same_full[:L//2])] | |
for n, vn in enumerate(same_part): | |
if vn != 2: | |
continue | |
m = n | |
for vm in same_part[n+1:]: | |
if vm != 2: | |
break | |
m+=1 | |
if m>n: | |
return n, m + 1 | |
def test_austin_hastings(a, b): | |
assert len(a) == len(b) | |
assert set(a) == set(b) | |
assert len(set(a)) == len(a) | |
length = len(a) | |
assert length % 2 == 0 | |
half = length // 2 | |
b_loc = {bi:n for n,bi in enumerate(b)} | |
for n,ai in enumerate(a[:half]): | |
L_n = length - 1 - n # L - n | |
L_m = b_loc[ai] # L - m (speculative) | |
if L_m < half: # Sanity: bail if on wrong side | |
continue | |
m = b_loc[a[L_n]] # If A[n] starts range, A[m] ends it. | |
if m < n or m > half: # Sanity: bail if backwards or wrong side | |
continue | |
left = slice(n, m+1) | |
right = slice(L_m, L_n+1) | |
if a[left] == b[right] and \ | |
b[left] == a[right]: | |
return n, m + 1 | |
return None | |
def test_peter_wood(A, B): | |
n = next(index for (index, (a, b)) in enumerate(zip(A, B)) | |
if a != b) | |
m = n + sum(1 for a, b in zip(A, B) if a != b) / 2 | |
if A[n:m] == B[-m:-n] and B[n:m] == A[-m:-n]: | |
return n, m | |
def test_jared_goguen(a, b): | |
L = len(a) | |
H = L//2 | |
n, m = 0, H-1 | |
# find the first difference in the left-side | |
while n < H: | |
if a[n] != b[n]: break | |
n += 1 | |
else: return | |
# find the last difference in the left-side | |
while m > -1: | |
if a[m] != b[m]: break | |
m -= 1 | |
else: return | |
# for slicing, we want end_index+1 | |
m += 1 | |
# compare each slice for equality | |
# order: beginning, block 1, block 2, middle, end | |
if (a[0:n] == b[0:n] and \ | |
a[n:m] == b[L-m:L-n] and \ | |
b[n:m] == a[L-m:L-n] and \ | |
a[m:L-m] == b[m:L-m] and \ | |
a[L-n:L] == b[L-n:L]): | |
return n, m | |
def test_jason_herbburn(a, b): | |
i_zip = list(enumerate(zip(a, b))) | |
llen = len(a) | |
hp = llen // 2 | |
def find_index(i_zip): | |
for i, (x, y) in i_zip: | |
if x != y: | |
return i | |
return i_zip[0][0] | |
# n and m are determined by the unmoved items: | |
n = find_index(i_zip[:hp]) | |
p = find_index(i_zip[hp:]) | |
m = llen - p | |
q = llen - n | |
# Symmetric? | |
if a[:n] + a[p:q] + a[m:p] + a[n:m] + a[q:] != b: | |
return None | |
return n, m | |
################################################ | |
# test facility | |
################################################ | |
from itertools import permutations | |
from time import time | |
def symmetries(A): | |
""" Enumerate all symmetries for A.""" | |
L = len(A) | |
k = L // 2 | |
d = {} | |
for n in range(k): | |
for m in range(n + 1, k + 1): | |
b = list(A) | |
b[n:m], b[L-m:L-n] = b[L-m:L-n], b[n:m] | |
d[''.join(b)] = n, m | |
return d | |
def test(A, syms, fn): | |
# test positives | |
for B, q in syms.items(): | |
p = fn(list(A), list(B)) | |
assert p == q, [p, 'should be equal to', q, 'for', B] | |
# test negatives | |
for p in permutations(A): | |
B = ''.join(p) | |
if A == B or B in syms: | |
continue | |
p = fn(list(A), list(B)) | |
assert not p, [p, 'should be None', 'for', B] | |
def run(fns, A, print_err=True): | |
print '\ntesting', A, 'L=', len(A) | |
syms = symmetries(A) | |
for name, fn in fns: | |
ts = time() | |
try: | |
test(A, syms, fn) | |
except AssertionError as e: | |
if print_err: | |
print '\t', name, 'ERR', e.message | |
except Exception as e: | |
if print_err: | |
print '\t', name, 'Exception', e.message | |
else: | |
print '\t', name, 'ok in', '%.4fs' % (time() - ts) | |
fns = sorted(x for x in vars().items() if x[0].startswith('test_')) | |
# correctness | |
for A in '01', '0123', '012345', '01234567': | |
run(fns, A, print_err=True) | |
# timings | |
for A in ['0123456789']: | |
run(fns, A, print_err=False) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment