input: Write a function `find_matches` that takes three parameters: a `query` string, a list of `choices` strings, and an integer `threshold`. The function should convert the query and all choices to lower case, calculate the Levenshtein distance between the query and each choice, and return a list of choices whose distance is less than or equal to the threshold.
  
  assert find_matches("apple", ["apples", "pineapple", "apply", "appetite"], 2)==["apples", "apply"]
  assert find_matches("cat", ["car", "cap", "cart", "catch"], 1)==["car", "cart", "catch"]
  assert find_matches("python", ["pebble", "putain", "potholes", "pseudonym"], 3)==[]
  assert find_matches("hello", ["Hello", "help", "hell", "hero"], 1)==["hello", "hell"]
  assert find_matches("", [], 0)==[]
output: def find_matches(query, choices, threshold):
      def levenshtein_distance(s1, s2):
          if len(s1) < len(s2):
              return levenshtein_distance(s2, s1)
          if len(s2) == 0:
              return len(s1)
          previous_row = range(len(s2) + 1)
          for i, c1 in enumerate(s1):
              current_row = [i + 1]
              for j, c2 in enumerate(s2):
                  insertions = previous_row[j + 1] + 1
                  deletions = current_row[j] + 1
                  substitutions = previous_row[j] + (c1 != c2)
                  current_row.append(min(insertions, deletions, substitutions))
              previous_row = current_row
          return previous_row[-1]
  
      query = query.lower()
      result = []
      for choice in choices:
          if levenshtein_distance(query, choice.lower()) <= threshold:
              result.append(choice)
      return result