def _PlonkIntoPlace(self, group, *, parents=None): """Anchors the group into the workspace. Assumes that conflicts have already been checked for. """ groups_at_this_location = list(self.GetGroupsWithSpan(Exactly(group.start_pos), Exactly(group.end_pos))) if groups_at_this_location: # Merge current group into the group at this location, as it were. This merging only # adds the underlying mapping. However, if we extend groups to make multiple # underlying mappings possible, this needs to be updated too. group_at_this_location = groups_at_this_location[ 0] # There can be only 1 if (group.object.underlying_mapping_set and not group_at_this_location.object.underlying_mapping_set): group_at_this_location.object.underlying_mapping_set = set( group.object.underlying_mapping_set) # We should also merge all pieces of the group into the # corresponding existing pieces. for x in group.items: self._PlonkIntoPlace(x) # Ignore output, we don't need it. group_at_this_location.object.AddCategoriesFrom(group.object) return group_at_this_location # Group does not exist, create one. pieces = [self._PlonkIntoPlace(x) for x in group.items] new_object = SAnchored.Create(pieces, underlying_mapping_set=set( group.object.underlying_mapping_set)) new_object.object.AddCategoriesFrom(group.object) self.groups.add(new_object) History.AddArtefact(new_object, ObjectType.WS_GROUP, "Initial creation: [%d, %d]" % (new_object.start_pos, new_object.end_pos), parents=parents) return new_object
def helper_create_group_given_spans_of_items(ws, *spans): anchored_items = [] for span in spans: if isinstance(span, int): anchored_items.append(ws.elements[span]) else: matching_groups = ws.GetGroupsWithSpan(Exactly(span[0]), Exactly(span[1])) anchored_items.append(next(matching_groups)) return SAnchored.Create(anchored_items)
def GetConflictingGroups(self, gp): """Get a list of groups conflicting with given group. Only maximal groups are returned, where the relative order is based on being a subgroup (i.e., if A is a part of B, and both conflict, B is returned; note that if A conflicts with the group under consideration, so does B). .. Note:: This can be sped up if I am keeping track of supergroups. """ if gp.is_sequence_element: return # Elements can never conflict. if gp in self.groups: return # Group already present, no conflict possible. groups_at_this_location = list(self.GetGroupsWithSpan(Exactly(gp.start_pos), Exactly(gp.end_pos))) if groups_at_this_location: # If something lies at exactly this location, it had better have # the same structure. # Can only be one. group_at_this_location = groups_at_this_location[0] if group_at_this_location.Structure() == gp.Structure(): return else: yield self.SomeMaximalSuperGroup(group_at_this_location) return # If we get here, no group at this exact location. See if any subgroup # conflicts. gp_items = set(gp.items) for subgroup in gp_items: conflicts = list(self.GetConflictingGroups(subgroup)) if conflicts: for conflict in conflicts: yield conflict return # Since we got here, any subgroup is fine individually. The only thing that may go wrong # is that there is overlap of more than one subgroup with an existing group (i.e., there # exists a (1, 2, 3, 4) and we are adding (3, 4, 5, 6)). existsing_group_items = set() for item in gp_items: # If a group exists with these ends, keep it in existing groups. for existing_group in self.GetGroupsWithSpan(Exactly(item.start_pos), Exactly(item.end_pos)): # There can be at most one existsing_group_items.add(existing_group) for other_group in self.groups: other_gp_items = set(other_group.items) overlap = existsing_group_items.intersection(other_gp_items) if len(overlap) >= 2: yield self.SomeMaximalSuperGroup(other_group)
def test_replacement(self): new_group = SAnchored.Create((SAnchored(SElement(7), (), 7, 7), SAnchored(SElement(8), (), 8, 8), SAnchored(SElement(9), (), 9, 9))) ws = Workspace() ws.InsertElements(range(0, 10)) helper_create_and_insert_groups(ws, ((1, 2, 3), (4, 5, 6), (7, 8))) existing_group = list(ws.GetGroupsWithSpan(Exactly(7), Exactly(8)))[0] # Cannot replace a group which is not the topmost. self.assertRaises(CannotReplaceSubgroupException, ws.Replace, existing_group, new_group) ws = Workspace() ws.InsertElements(range(0, 10)) helper_create_and_insert_groups(ws, (7, 8)) existing_group = list(ws.GetGroupsWithSpan(Exactly(7), Exactly(8)))[0] ws.Replace(existing_group, new_group) self.assertTrue(existing_group not in ws.groups) self.assertEqual(1, len(list(ws.GetGroupsWithSpan(Exactly(7), Exactly(9))))) # So now (7, 8, 9) is a group. Let's add a group (5, 6) and try and extend it to 5:8 helper_create_and_insert_groups(ws, (5, 6)) existing_group = list(ws.GetGroupsWithSpan(Exactly(5), Exactly(6)))[0] new_group = SAnchored.Create((SAnchored(SElement(5), (), 5, 5), SAnchored(SElement(6), (), 6, 6), SAnchored(SElement(7), (), 7, 7), SAnchored(SElement(8), (), 8, 8))) self.assertRaises(ConflictingGroupException, ws.Replace, existing_group, new_group) # The original group still exists self.assertTrue(existing_group in ws.groups)
def test_utility_functions(self): self.assertTrue( LessThan(3)(2), "2 is acceptable for the function LessThan(3)") self.assertTrue( LessThanEq(3)(2), "2 is acceptable for the function LessThanEq(3)") self.assertTrue(LessThanEq(3)(3)) self.assertFalse(LessThan(3)(3)) self.assertTrue(GreaterThan(3)(4)) self.assertTrue(GreaterThanEq(3)(4)) self.assertFalse(GreaterThan(3)(2)) self.assertTrue(Exactly(3)(3)) self.assertFalse(Exactly(3)(4))
def test_plonk_into_place(self): ws = Workspace() ws.InsertElements((7, 8, 7, 8, 9)) # Plonk an element... returns existing element. elt = SAnchored(SElement(8), (), 3, 3) self.assertEqual(ws.elements[3], ws._PlonkIntoPlace(elt)) # Plonk a group, one item of which is an existing element, one novel. The plonked group # has the existing element as a subgroup. elt0 = SAnchored(SElement(7), (), 0, 0) elt1 = SAnchored(SElement(8), (), 1, 1) elt2 = SAnchored(SElement(7), (), 2, 2) elt3 = SAnchored(SElement(8), (), 3, 3) elt4 = SAnchored(SElement(9), (), 4, 4) numeric_successor = NumericMapping(name="succ", category=Number()) numeric_sameness = NumericMapping(name="same", category=Number()) successor_mapping_based_cat = MappingBasedCategory( mapping=numeric_successor) next_ascending = StructuralMapping( category=MappingBasedCategory(mapping=numeric_successor), bindings_mapping=frozenset((('length', numeric_successor), ('start', numeric_sameness)))) gp1 = SAnchored.Create( (elt0, elt1), underlying_mapping_set={numeric_successor}) gp2 = SAnchored.Create( (elt2, elt3, elt4), underlying_mapping_set={numeric_successor}) gp3 = SAnchored.Create( (gp1, gp2), underlying_mapping_set={next_ascending}) self.assertTrue(gp1.object.IsKnownAsInstanceOf( successor_mapping_based_cat)) plonked = ws._PlonkIntoPlace(gp3) self.assertEqual(((7, 8), (7, 8, 9)), plonked.Structure()) existing_groups = list(ws.GetGroupsWithSpan(Exactly(0), Exactly(1))) self.assertEqual(existing_groups[0], plonked.items[0]) self.assertTrue(plonked.items[0].object.IsKnownAsInstanceOf( successor_mapping_based_cat)) self.assertTrue(numeric_successor in plonked.items[ 0].object.underlying_mapping_set) self.assertTrue( next_ascending in plonked.object.underlying_mapping_set)
def GetGroupDistanceMagnitude(self, left, right): """Helper for the function above.""" if right == left + 1: return 0 rightward_groups = list(self.GetGroupsWithSpan( Exactly(left + 1), LessThan(right))) if not rightward_groups: return 1 + self.GetGroupsWithSpan(left + 1, right) else: new_left = max(x.end_pos for x in rightward_groups) return 1 + self.GetGroupDistanceMagnitude(new_left, right)