package yi.ds.fold;

import java.util.*;
import yi.util.io.*;

/**
 * DataFolder folds the given data to several set (n) of data (randomly).  Each
 * set contain two parts: one having (n-1)/n data (eg. training data) and
 * another having 1/n data (eg. testing data)
 */

public class DataFolder {
  /**
   * Generate an ArrayList containing all data in the given collection with
   * random order
   * @param data
   * @return
   */
  public static ArrayList randomrize(Collection data) {
    int size = data.size();

    // first assign the data to an ArrayList to speed up the random access
    ArrayList data_copy = new ArrayList(data);

    // shuffle the data
    // use a fixed random number for experiment
    Random r = new Random(0);
    Collections.shuffle(data_copy, r);
    return data_copy;
  }

  /**
   * Randomrize the data, and then generate n set of data stored in ArrayList.
   * Each set contain two parts: one having (n-1)/n data (eg. training data) and
   * another having 1/n data (eg. testing data).  The data type of the element
   * in Arraylist is TwoLinkedListData.
   * @param data
   * @param fold_num
   * @return
   */
  public static ArrayList nFold(Collection data, int fold_num) {
    ArrayList rand_data = randomrize(data);
    int size = rand_data.size();

    ArrayList rst = new ArrayList(fold_num);

    for (int i=0; i<fold_num; i++) { // each fold
      LinkedList bigger = new LinkedList();
      LinkedList smaller = new LinkedList();
      // the data are divided to three part:
      // [0/n, i/n) goes into the bigger data set
      // [i/n, (i+1)/n) goes into the smaller data set
      // [(i+1)/n, n) goes into the bigger data set
      int up_limit = (int)((long)size * i / fold_num);
      for (int j=0; j<up_limit; j++) {
        bigger.add(rand_data.get(j));
      }
      int lower_limit = up_limit;
      up_limit = (int)((long)size * (i+1) / fold_num);
      for (int j=lower_limit; j<up_limit; j++) {
        smaller.add(rand_data.get(j));
      }
      lower_limit = up_limit;
      for (int j=lower_limit; j<size; j++) {
        bigger.add(rand_data.get(j));
      }

      rst.add(new TwoLinkedListData(bigger, smaller));
    }

    return rst;
  }

  public static void main(String[] args) {
    int size = 18;
    int fold = 4;
    ArrayList data = new ArrayList(size);
    for (int i=0; i<size; i++) {
      data.add(new Integer(i));
    }

    ArrayList folds = DataFolder.nFold(data, fold);

    for (int i = 0; i < fold; i++) {
      TwoLinkedListData twolist = (TwoLinkedListData) folds.get(i);
      System.out.println("fold #" + i);
      System.out.println("training set: ");
      OutputResult.printIntCollection(System.out, twolist.first);
      System.out.println("testing set: ");
      OutputResult.printIntCollection(System.out, twolist.second);
    }
  }

}