def _loop_range( itervar: str, inedges: List[gr.Edge], condition: sp.Expr) -> Optional[Tuple[sp.Expr, sp.Expr, sp.Expr]]: """ Finds loop range from state machine. :param itersym: String representing the iteration variable. :param inedges: Incoming edges into guard state (length must be 2). :param condition: Condition as sympy expression. :return: A three-tuple of (start, end, stride) expressions, or None if proper for-loop was not detected. ``end`` is inclusive. """ # Find starting expression and stride itersym = symbolic.symbol(itervar) if (itersym in symbolic.pystr_to_symbolic( inedges[0].data.assignments[itervar]).free_symbols and itersym not in symbolic.pystr_to_symbolic( inedges[1].data.assignments[itervar]).free_symbols): stride = (symbolic.pystr_to_symbolic( inedges[0].data.assignments[itervar]) - itersym) start = symbolic.pystr_to_symbolic( inedges[1].data.assignments[itervar]) elif (itersym in symbolic.pystr_to_symbolic( inedges[1].data.assignments[itervar]).free_symbols and itersym not in symbolic.pystr_to_symbolic( inedges[0].data.assignments[itervar]).free_symbols): stride = (symbolic.pystr_to_symbolic( inedges[1].data.assignments[itervar]) - itersym) start = symbolic.pystr_to_symbolic( inedges[0].data.assignments[itervar]) else: return None # Find condition by matching expressions end: Optional[sp.Expr] = None a = sp.Wild('a') match = condition.match(itersym < a) if match: end = match[a] - 1 if end is None: match = condition.match(itersym <= a) if match: end = match[a] if end is None: match = condition.match(itersym > a) if match: end = match[a] + 1 if end is None: match = condition.match(itersym >= a) if match: end = match[a] if end is None: # No match found return None return start, end, stride