docs: @Override
    public List<VariableMatch> matchVariables(VariableNumMap inputVariables) {
      // All of the fixed variables must be matched in order to return anything.
      if (!inputVariables.containsAll(fixedVariables)) {
        return Collections.emptyList();
      }
      // Special case: if there aren't any templates, then only the fixed
      // variables should be returned.
      if (plateStarts.length == 0) {
        return Arrays.asList(VariableMatch.identity(fixedVariables));
      }
  
      int maxPlateReplication = -1;
      int[] inputVarNums = inputVariables.getVariableNumsArray();
      int numVars = inputVarNums.length;
      int numMatchers = plateStarts.length;
      for (int i = 0; i < numMatchers; i++) {
        for (int j = 0; j < numVars; j++) {
          if (inputVarNums[j] >= plateStarts[i] && inputVarNums[j] < plateEnds[i]) {
            int withinPlateIndex = inputVarNums[j] - plateStarts[i];
            int plateReplicationNum = withinPlateIndex / plateReplicationSizes[i];
            int replicationIndex = plateReplicationNum - plateMatchIndexOffsets[i];
            maxPlateReplication = Math.max(replicationIndex, maxPlateReplication);
          }
        }
      }
  
      // Aggregate pattern matches for each variable.
      int[] templateVarNums = templateVariables.getVariableNumsArray();
      int[][] variableMatches = new int[maxPlateReplication + 1][];
      for (int i = 0; i < numMatchers; i++) {      
        for (int j = 0; j < numVars; j++) {
          if (inputVarNums[j] >= plateStarts[i] && inputVarNums[j] < plateEnds[i]) {
            // The variable is within the plate we're matching.
            int withinPlateIndex = inputVarNums[j] - plateStarts[i];
            int plateReplicationNum = withinPlateIndex / plateReplicationSizes[i];
            int varOffset = withinPlateIndex % plateReplicationSizes[i];
            if (varOffset == plateVarOffsets[i]) {
              // The variable matches the pattern.
              int replicationIndex = plateReplicationNum - plateMatchIndexOffsets[i];
              if (variableMatches[replicationIndex] == null) {
                variableMatches[replicationIndex] = new int[templateVarNums.length];
                Arrays.fill(variableMatches[replicationIndex], -1);
              }
              variableMatches[replicationIndex][i] = inputVarNums[j];
            }
          }
        }
      }
  
      // Eliminate any partial matches which do not contain a match for every
      // variable prefix.
      List<VariableMatch> validMatches = Lists.newArrayList();
      int numMatches = variableMatches.length;
      for (int i = 0; i < numMatches; i++) {
        int[] matchBuilder = variableMatches[i];
        if (matchBuilder != null && Ints.indexOf(matchBuilder, -1) == -1) {
          VariableNumMap matchedVariables = inputVariables.intersection(matchBuilder);
          VariableRelabeling fixedVariableRelabeling = VariableRelabeling.identity(fixedVariables);
  
          int[] templateCopy = Arrays.copyOf(templateVarNums, templateVarNums.length);
  
          IntBiMap mapping = IntBiMap.fromUnsortedKeyValues(matchBuilder, templateCopy);
          VariableRelabeling matchedRelabeling = new VariableRelabeling(matchedVariables,
              templateVariables, mapping);
          VariableMatch match = new VariableMatch(matchedVariables.union(fixedVariables),
            templateVariables.union(fixedVariables), fixedVariableRelabeling.union(matchedRelabeling));
  
          validMatches.add(match);
        }
      }
      return validMatches;
    }