edu.brown.utils.UniqueCombinationIterator.java Source code

Java tutorial

Introduction

Here is the source code for edu.brown.utils.UniqueCombinationIterator.java

Source

/***************************************************************************
 *  Copyright (C) 2012 by H-Store Project                                  *
 *  Brown University                                                       *
 *  Massachusetts Institute of Technology                                  *
 *  Yale University                                                        *
 *                                                                         *
 *  http://hstore.cs.brown.edu/                                            *
 *                                                                         *
 *  Permission is hereby granted, free of charge, to any person obtaining  *
 *  a copy of this software and associated documentation files (the        *
 *  "Software"), to deal in the Software without restriction, including    *
 *  without limitation the rights to use, copy, modify, merge, publish,    *
 *  distribute, sublicense, and/or sell copies of the Software, and to     *
 *  permit persons to whom the Software is furnished to do so, subject to  *
 *  the following conditions:                                              *
 *                                                                         *
 *  The above copyright notice and this permission notice shall be         *
 *  included in all copies or substantial portions of the Software.        *
 *                                                                         *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        *
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     *
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR      *
 *  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  *
 *  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  *
 *  OTHER DEALINGS IN THE SOFTWARE.                                        *
 ***************************************************************************/
package edu.brown.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.apache.commons.collections15.map.ListOrderedMap;
import org.apache.commons.collections15.set.ListOrderedSet;
import org.apache.log4j.Logger;

/**
 * @author pavlo
 * @param <E>
 */
public class UniqueCombinationIterator<E> implements Iterator<Set<E>> {
    private static final Logger LOG = Logger.getLogger(UniqueCombinationIterator.class);

    private final List<E> data;
    private final int combo_size;
    private final int num_elements;
    private final Vector<Integer> last;
    private ListOrderedSet<E> next = null;
    private Boolean finished = null;
    private int attempt_ctr = 0;

    public UniqueCombinationIterator(Collection<E> data, int size) {
        this.data = (data instanceof List ? (List<E>) data : new ArrayList<E>(data));
        this.combo_size = size;
        this.num_elements = this.data.size();

        // Initialize last counters
        this.last = new Vector<Integer>();
        this.last.setSize(this.combo_size);
        this.initializeLast(0);
    }

    private void initializeLast(int start) {
        for (int i = 0; i < this.combo_size; i++) {
            this.last.set(i, start + i);
        }
    }

    @Override
    public boolean hasNext() {
        if (this.finished == null) {
            try {
                this.getNext();
            } catch (RuntimeException ex) {
                LOG.info(this);
                throw ex;
            }
        }
        assert (this.finished != null);
        return (this.finished == false);
    }

    @Override
    public Set<E> next() {
        boolean valid = this.hasNext();
        Set<E> ret = (this.finished == false ? this.next : null);
        this.next = null;

        // If we're suppose to have more, then queue it up!
        if (valid) {
            this.getNext();
        }
        return (ret);
    }

    /**
     * Find the next unique combination
     */
    private void getNext() {
        assert (this.next == null);
        final boolean trace = LOG.isTraceEnabled();
        final boolean debug = LOG.isDebugEnabled();

        if (debug)
            LOG.debug("Finding next combination [call=" + (this.attempt_ctr++) + "]");

        boolean valid = false;
        Vector<Integer> buffer = null;
        for (int i = this.last.get(0); i < this.num_elements; i++) {
            if (trace)
                LOG.trace("\n" + this);

            buffer = new Vector<Integer>();
            buffer.setSize(this.combo_size);
            buffer.set(0, i);

            // We have a new combination!
            if (this.calculateCombinations(buffer, 1)) {
                if (trace)
                    LOG.trace("Found new combination: " + buffer);
                valid = true;
                break;
            }
            if (trace)
                LOG.trace("No combination found that starts with index #" + i);
            buffer = null;
            this.initializeLast(i + 1);
        } // FOR

        if (trace)
            LOG.trace("VALID = " + valid);
        if (valid) {
            assert (this.combo_size == buffer.size());
            this.next = new ListOrderedSet<E>();
            for (int i = 0; i < this.combo_size; i++) {
                this.next.add(this.data.get(buffer.get(i)));
            } // FOR
            if (trace)
                LOG.trace("NEXT = " + this.next);

            // Increase the last position's counter so that it is different next
            // time
            this.last.set(this.combo_size - 1, this.last.lastElement() + 1);
            if (trace)
                LOG.trace("NEW LAST = " + this.last);

            this.finished = false;
        } else {
            this.finished = true;
        }
    }

    private boolean calculateCombinations(List<Integer> buffer, int position) {
        // If we're at the last position, then just return true
        if (position == this.combo_size)
            return (true);

        // Get the last element counter for this position
        int last_value = this.last.get(position);

        // Now if we go past the total number of elements, then we need to
        // return false
        // Make sure that we reset ourselves to be one more than our preceding
        // position
        for (int i = last_value; i < this.num_elements; i++) {
            this.last.set(position, i);
            buffer.set(position, i);
            if (this.calculateCombinations(buffer, position + 1)) {
                return (true);
            }
        } // FOR
          // Note that we have to increase ourselves by two, because we know
          // that the preceding position
          // is going to get increased by one, so we want to make sure we're one
          // more than that
          // But everyone else should just be one more than the preceeding
        int base_value = this.last.get(position - 1) + 1;
        for (int i = position; i < this.combo_size; i++) {
            this.last.set(position, base_value + (i == position ? 1 : 0));
        }
        return (false);
    }

    @Override
    public void remove() {
        assert (false);
    }

    /**
     * Helper method that returns this iterator wrapped in an Iterable
     * 
     * @param <E>
     * @param data
     * @param combo_size
     * @return
     */
    public static <E> Iterable<Set<E>> factory(Collection<E> data, int combo_size) {
        return (CollectionUtil.iterable(new UniqueCombinationIterator<E>(data, combo_size)));
    }

    @Override
    public String toString() {
        Map<String, Object> m = new ListOrderedMap<String, Object>();
        m.put("DATA", this.data);
        m.put("COMBO_SIZE", this.combo_size);
        m.put("NUM_ELEMENTS", this.num_elements);
        m.put("FINISHED", this.finished);
        m.put("LAST", this.last);
        m.put("NEXT", this.next);

        return StringBoxUtil.box(StringUtil.formatMaps("=", m));
    }
}