# -*- Mode: Python -*- # Exercise 2.3 from EOPLv3, taken to extremes. # one == 1 # diff == (, ) # := one | diff # 0 == (1,1) # -1 == ((1,1),1) # +1 == 0 - (-1) == ((1,1),((1,1),1)) def successor (t): return (t, ((1, 1), 1)) def predecessor (t): return (t, 1) def is_zero (t): # TBD pass def to_number (t): if t == 1: return 1 else: return to_number (t[0]) - to_number (t[1]) # generate a non-canonical random number def gen_random(): import random t = 1 # to generate a positive number, pick a direction favoring +1 # 0=-1 1=+1 2=+1 n = 100 while n: direction = random.randrange (0, 3) if direction is 0: t = predecessor (t) else: t = successor (t) n -= 1 return t # generate a randomly formed non-canonical number # (this is more truly random than the above, which does +1/-1 only) def gen_random (): import random d = random.randrange (0, 120) if d == 0: return 1 elif d % 2 == 0: return (gen_random (), 1) elif d % 2 == 1: return (1, gen_random()) def canon_n (n): if n == 0: return ZERO elif n > 0: r = ZERO while n: r = canon_successor (r) n -= 1 return r else: r = ZERO while n < 0: r = canon_predecessor (r) n += 1 return r # here is the canonical form: # positive numbers are of the form (, -1) # negative numbers are of the form (, 1) # (, -1) # -1 = (zero,1) # zero: (1,1) # one: 1 # two: (1, -1) # three: (two, -1) # four: (three, -1) # -1 = (zero, 1) # -two = (-1, 1) # -three = (-two, 1) # -four = (-three, 1) ZERO = (1,1) NEG1 = (ZERO,1) POS1 = 1 def canon_positive (t): return t == POS1 or t[1] == NEG1 def canon_negative (t): return t != POS1 and t[1] == POS1 def canon_successor (t): if t == ZERO: return POS1 elif t == NEG1: return ZERO elif t == POS1 or canon_positive (t): return (t, NEG1) else: #assert (t[1] == POS1) return t[0] def canon_predecessor (t): if t == ZERO: return NEG1 elif t == POS1: return ZERO elif t == NEG1 or canon_negative (t): return (t, POS1) else: #assert (t[1] == NEG1) return t[0] PLUS = canon_successor MINUS = canon_predecessor import sys W = sys.stderr.write def canon (t): if t == ZERO: return t elif t == NEG1: return t elif t == POS1: return t else: a, b = oa, ob = t a = canon (a) #assert (is_canon (a)) b = canon (b) #assert (is_canon (b)) while 1: #an = to_number (a) #bn = to_number (b) #print an, bn if b == NEG1: #W ('<') # is this well-formed? if a == ZERO: return POS1 elif a == NEG1: return ZERO elif canon_negative (a): # no: (-5, -1) # fix: (-3, 1) a = canon_successor (canon_successor (a)) b = POS1 else: return (a, b) elif b == POS1: #W ('>') # well-formed? if a == ZERO: # (0,1) == ((1,1),1) == NEG1 return NEG1 elif a == NEG1: # (-1,1) == -2 return canon_predecessor (NEG1) elif a == POS1: return ZERO elif canon_positive (a): a = canon_predecessor (canon_predecessor (a)) b = NEG1 else: return (a, b) else: # non-canonical, fix it. # (-,+) => becomes more negative - decrement to 1 # (+,-) => becomes more positive - increment to -1 # (+,+) => ??? # (-,-) => ??? # whatever is, it needs to be taken up or down toward 1 or -1 if canon_negative (b): # (5, -2) == 7 : (6, -1) # (-5, -2) == 3 : (-4, -1) # b = b + 1 b = canon_successor (b) # a = a + 1 a = canon_successor (a) #W ("+") elif canon_positive (b): # (5, 2) == 3 : (4, 1) # (-5, 2) == -7 : (-6, 1) # b = b - 1 b = canon_predecessor (b) # a = a + 1 a = canon_predecessor (a) #W ("-") else: raise ValueError ("impossible?") def is_canon (t): # XXX only meant for debugging if t == 1: return True elif t == (1, 1): return True elif t[1] == 1: # negative number if to_number (t[0]) > 1: return False else: return is_canon (t[0]) elif t[1] == NEG1: # positive number if to_number (t[0]) < 1: return False else: return is_canon (t[0]) else: return False if __name__ == '__main__': #bad_zero = (((((((1, 1), 1), ((1, 1), 1)), 1), 1), ((1, 1), 1)), ((1, 1), 1)) #bad_neg2 = (((((1, 1), 1), ((1, 1), 1)), 1), 1) #bad_three = (1,(((1,1),1),1)) #canon (bad_three) for i in range (300): n = gen_random() val = to_number (n) W ('[%d]' % (val,)) n2 = canon_n (val) n3 = canon (n) assert (to_number (n2) == val) assert (to_number (n3) == val) assert (n3 == n2)