def process_one_series(self, assumption, spec): provisional_total = 0 totals = [] seen_total_hint = False for pos in range(0, len(assumption), 1): if self.success: break if spec[pos] == spec.CRUFT: continue number = assumption[pos] if not seen_total_hint and number == 0 and number.info['category'] == 'Total': if assumption.zero_ok( pos, spec ): seen_total_hint = True spec[pos] = spec.GRAND_TOTAL new_candidate = assumption.condense(spec) spec.pop(pos) self.candidates.append( new_candidate ) continue if number != 0 and spec[pos] != spec.NON_TOTAL: #print 'pos/key: %s, number: %s, spec[pos]: %s' % (pos,number,spec[pos]) if number == provisional_total: if number.info['category'] == 'Total': seen_total_hint = True spec[pos] = spec.TOTAL totals.append( provisional_total ) provisional_total = 0 number_of_totals = 0 for key in spec.keys(): if spec[key] == spec.TOTAL or spec[key] == spec.NON_TOTAL: number_of_totals += 1 if number.info['category'] == 'Total' and number_of_totals == 1: new_candidate = assumption.condense(spec) self.candidates.append( new_candidate ) continue if sum(totals) and number == sum(totals) + provisional_total: spec[pos] = spec.GRAND_TOTAL # # Aha! Condense affected the spec. Now _that_ is evil. # self.candidates.append( assumption.condense(spec) ) break # # XXX Untested if not seen_total_hint and number.info['category'] == 'Total': seen_total_hint = True nstr = str(number.str) if len(nstr) % 2 == 0: if nstr[len(nstr)/2:] == nstr[:len(nstr)/2]: nstr = nstr[:len(nstr)/2] self.fallback = ambiguousNumber( nstr, number.info.copy()) if number.info['category'] == 'Total': seen_total_hint = True provisional_total += number
def combine(self, start, end): ''' Combine a slice of numbers. Numbers are combined in reverse order, concatenating them as strings, then returning them as a single ambiguousNumber. An attempt to combine a string with an embedded negative sign will return None. ''' numlist = self.strs()[start:end] numlist.reverse() try: return ambiguousNumber( ''.join( numlist ), self.info.copy() ) except ValueError: return None
def init_nums(self, nums): if isinstance(nums, ListType): return nums elif isinstance(nums, ambiguousNumber): return [nums] elif isinstance(nums, StringTypes): nums = nums.split() nums.reverse() l = [] for num in nums: l.append( ambiguousNumber( num, self.info ) ) return l else: raise ClusterArgumentError(('First','nums','<ambiguousCluster>, <ambiguousNumber> or <string>'))
def condense(self, oldspec): """ Return an unambiguous structured list of numbers Starting with an assumption, build a new list that does not contain cruft numbers, and consists of component numbers, totals, and a grand total. The starting assumption may take one of three forms. It may end in a grand total of zero. It may end in a bare total. or it may end in a grand total. Each case is processed as required to produce the final structured list of numbers. """ new = assumptionObject([]) spec = copy(oldspec) aggressive_delete = True force_totals = False # # Compose a new list of numbers, containing no cruft, and # no trailers. append = False for pos in range(len(self) - 1, -1, -1): if spec[pos] == spec.CRUFT: continue if not append and spec[pos] == spec.NON_TOTAL: continue if spec[pos]: append = True if append: new.append(ambiguousNumber(self[pos].str, self[pos].info.copy())) if spec[pos]: new[-1].update(state=spec[pos]) new.reverse() # # We're not interested in zeros for pos in range(len(new) - 2, -1, -1): if new[pos] == 0: new.pop(pos) # # Just in case if new[-1] == 0: new[-1].update(state=spec.GRAND_TOTAL) for pos in range(len(new) - 2, -1, -1): new.pop(pos) # # Properly formed lists should be okay as they are. elif new[-1].state() == spec.GRAND_TOTAL: pass # # Normalize lists that include ordinary components in # the grand total. elif new[-1].state() == spec.TOTAL: new[-1].update(state=spec.GRAND_TOTAL) force_totals = True for pos in range(len(new) - 2, -1, -1): if new[pos].state(): force_totals = False if force_totals: num = new[pos].str info = new[pos].info.copy() new.insert(pos + 1, ambiguousNumber(num, info)) new[pos + 1].update(state=spec.TOTAL) else: print new print "last value: %d, state of last value: %d" % (new[-1], new[-1].state()) raise ThisCannotHappenException return new