<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def assign_utc(self, reading_id, uptime=None, prefer="before"):
 """Assign a utc datetime to a reading id. This method will return an object with assignment information or None if a utc value cannot be assigned. The assignment object returned contains a utc property that has the asssigned UTC as well as other properties describing how reliable the assignment is. Args: reading_id (int):
 The monotonic reading id that we wish to assign a utc timestamp to. uptime (int):
 Optional uptime that should be associated with the reading id. If this is not specified and the reading_id is found in the anchor points passed to this class then the uptime from the corresponding anchor point will be used. prefer (str):
 There are two possible directions that can be used to assign a UTC timestamp (the nearest anchor before or after the reading). If both directions are of similar quality, the choice is arbitrary. Passing prefer="before" will use the anchor point before the reading. Passing prefer="after" will use the anchor point after the reading. Default: before. Returns: UTCAssignment: The assigned UTC time or None if assignment is impossible. """


        if prefer not in ("before", "after"):
            raise ArgumentError("Invalid prefer parameter: {}, must be 'before' or 'after'".format(prefer))

        if len(self._anchor_points) == 0:
            return None

        if reading_id > self._anchor_points[-1].reading_id:
            return None

        i = self._anchor_points.bisect_key_left(reading_id)
        found_id = False
        crossed_break = False
        exact = True

        last = self._anchor_points[i].copy()
        if uptime is not None:
            last.uptime = uptime

        if last.reading_id == reading_id:
            found_id = True

            if last.utc is not None:
                return UTCAssignment(reading_id, last.utc, found_id, exact, crossed_break)

        left_assign = self._fix_left(reading_id, last, i, found_id)
        if left_assign is not None and left_assign.exact:
            return left_assign

        right_assign = self._fix_right(reading_id, last, i, found_id)
        if right_assign is not None and right_assign.exact:
            return right_assign

        return self._pick_best_fix(left_assign, right_assign, prefer)