code: public static <T> List<List<T>> collate(Iterable<T> self, int size, int step, boolean keepRemainder) {
          List<T> selfList = asList(self);
          List<List<T>> answer = new ArrayList<List<T>>();
          if (size <= 0) {
              answer.add(selfList);
          } else {
              if (step == 0) throw new IllegalArgumentException("step cannot be zero");
              for (int pos = 0; pos < selfList.size() && pos > -1; pos += step) {
                  if (!keepRemainder && pos > selfList.size() - size) {
                      break ;
                  }
                  List<T> element = new ArrayList<T>() ;
                  for (int offs = pos; offs < pos + size && offs < selfList.size(); offs++) {
                      element.add(selfList.get(offs));
                  }
                  answer.add( element ) ;
              }
          }
          return answer ;
      }
docs: Collates this iterable into sub-lists of length <code>size</code> stepping through the code <code>step</code>
  elements for each sub-list.  Any remaining elements in the iterable after the subdivision will be dropped if
  <code>keepRemainder</code> is false.
  Example:
  <pre class="groovyTestCase">
  def list = [ 1, 2, 3, 4 ]
  assert list.collate( 2, 2, true  ) == [ [ 1, 2 ], [ 3, 4 ] ]
  assert list.collate( 3, 1, true  ) == [ [ 1, 2, 3 ], [ 2, 3, 4 ], [ 3, 4 ], [ 4 ] ]
  assert list.collate( 3, 1, false ) == [ [ 1, 2, 3 ], [ 2, 3, 4 ] ]
  </pre>
  
  @param self          an Iterable
  @param size          the length of each sub-list in the returned list
  @param step          the number of elements to step through for each sub-list
  @param keepRemainder if true, any remaining elements are returned as sub-lists.  Otherwise they are discarded
  @return a List containing the data collated into sub-lists
  @throws IllegalArgumentException if the step is zero.
  @since 2.4.0