    001package net.sf.cpsolver.ifs.util;
    002
    003import java.io.ByteArrayOutputStream;
    004import java.io.File;
    005import java.io.FileInputStream;
    006import java.io.IOException;
    007import java.io.OutputStream;
    008import java.io.PrintStream;
    009import java.util.ArrayList;
    010import java.util.Collection;
    011import java.util.Date;
    012import java.util.Iterator;
    013import java.util.List;
    014import java.util.Map;
    015import java.util.Properties;
    016import java.util.Random;
    017import java.util.StringTokenizer;
    018import java.util.TreeSet;
    019
    020import org.apache.log4j.Level;
    021import org.apache.log4j.Logger;
    022import org.apache.log4j.PropertyConfigurator;
    023
    024/**
    025 * Several auxiliary static methods.
    026 *
    027 * @version IFS 1.2 (Iterative Forward Search)<br>
    028 *          Copyright (C) 2006 - 2010 Tomáš Müller<br>
    029 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    030 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    031 * <br>
    032 *          This library is free software; you can redistribute it and/or modify
    033 *          it under the terms of the GNU Lesser General Public License as
    034 *          published by the Free Software Foundation; either version 3 of the
    035 *          License, or (at your option) any later version. <br>
    036 * <br>
    037 *          This library is distributed in the hope that it will be useful, but
    038 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    039 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    040 *          Lesser General Public License for more details. <br>
    041 * <br>
    042 *          You should have received a copy of the GNU Lesser General Public
    043 *          License along with this library; if not see
    044 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    045 */
    046public class ToolBox {
    047    private static long sSeed = System.currentTimeMillis();
    048    private static Random sRandom = new Random(sSeed);
    049
    050    /** Returns random number (int) from the set 0 .. limit - 1 */
    051    public static int random(int limit) {
    052        return (int) (random() * limit);
    053    }
    054
    055    /** Returns random element from the given set of elements */
    056    public static <E> E random(Collection<E> set) {
    057        switch (set == null ? 0 : set.size()) {
    058            case 0:
    059                return null;
    060            case 1:
    061                return set.iterator().next();
    062            case 2:
    063                Iterator<E> i = set.iterator();
    064                if (sRandom.nextBoolean()) i.next();
    065                return i.next();
    066            default:
    067                List<E> v = (set instanceof List<?> ? (List<E>) set : new ArrayList<E>(set));
    068                return v.get(random(v.size()));
    069        }
    070    }
    071
    072    /**
    073     * Returns a randomly generated subset of the given set
    074     *
    075     * @param set
    076     *            set
    077     * @param part
    078     *            probability of selection of an element into the resultant
    079     *            subset
    080     */
    081    public static <E> Collection<E> subSet(Collection<E> set, double part) {
    082        return subSet(set, part, 1);
    083    }
    084
    085    /** Swaps two elements in the list */
    086    private static <E> void swap(List<E> list, int first, int second) {
    087        E o = list.get(first);
    088        list.set(first, list.get(second));
    089        list.set(second, o);
    090    }
    091
    092    /**
    093     * Returns a randomly generated subset of the given set
    094     *
    095     * @param set
    096     *            set
    097     * @param part
    098     *            probability of selection of an element into the resultant
    099     *            subset
    100     * @param minSize
    101     *            minimal size of the returned subset
    102     */
    103    public static <E> Collection<E> subSet(Collection<E> set, double part, int minSize) {
    104        if (set.size() <= minSize || part >= 1.0)
    105            return set;
    106        ArrayList<E> subSet = new ArrayList<E>(set);
    107        int size = set.size();
    108        int numberToSelect = Math.max(minSize, (int) (part * set.size()));
    109        for (int idx = 0; idx < numberToSelect; idx++) {
    110            swap(subSet, idx, idx + (int) (random() * (size - idx)));
    111        }
    112        return subSet.subList(0, numberToSelect);
    113    }
    114
    115    /** Trim a string to have given length */
    116    public static String trim(String s, int length) {
    117        if (s.length() > length)
    118            return s.substring(0, length);
    119        StringBuffer sb = new StringBuffer(s);
    120        while (sb.length() < length)
    121            sb.append(" ");
    122        return sb.toString();
    123    }
    124
    125    /** Multiline representation of a colection */
    126    public static String col2string(Collection<?> col, int tab) {
    127        StringBuffer tabsb = new StringBuffer();
    128        while (tabsb.length() < 2 * tab)
    129            tabsb.append("  ");
    130        StringBuffer sb = new StringBuffer("[\n");
    131        for (Iterator<?> i = col.iterator(); i.hasNext();) {
    132            sb.append(tabsb + "  " + i.next() + (i.hasNext() ? "," : "") + "\n");
    133        }
    134        sb.append(tabsb + "]");
    135        return sb.toString();
    136    }
    137
    138    /** Multiline representation of a dictionary */
    139    public static <K, V> String dict2string(Map<K, V> dict, int tab) {
    140        StringBuffer tabsb = new StringBuffer();
    141        while (tabsb.length() < 2 * tab)
    142            tabsb.append("  ");
    143        StringBuffer sb = new StringBuffer("[\n");
    144        TreeSet<K> keys = new TreeSet<K>(dict.keySet());
    145        for (K key : keys) {
    146            V value = dict.get(key);
    147            sb.append(tabsb + "  " + key + ": " + value + "\n");
    148        }
    149        sb.append(tabsb + "]");
    150        return sb.toString();
    151    }
    152
    153    /**
    154     * Root mean square
    155     *
    156     * @param n
    157     *            number of tests
    158     * @param x
    159     *            total value of all tests
    160     * @param x2
    161     *            total value^2 of all tests
    162     */
    163    public static double rms(int n, double x, double x2) {
    164        double var = x2 / n;
    165        double mean = x / n;
    166        return Math.sqrt(Math.abs(var - mean * mean));
    167    }
    168
    169    /** Merge source with target */
    170    public static <E> void merge(List<E> target, Collection<E> source) {
    171        for (E o : source) {
    172            if (!target.contains(o))
    173                target.add(o);
    174        }
    175    }
    176
    177    /** Returns intersection of two collections */
    178    public static <E> List<E> intersect(Collection<E> source1, Collection<E> source2) {
    179        List<E> target = new ArrayList<E>();
    180        for (E o : source1) {
    181            if (!source2.contains(o))
    182                target.add(o);
    183        }
    184        return target;
    185    }
    186
    187    /**
    188     * Sets seeds for {@link ToolBox#getRandom()} and {@link ToolBox#random()}
    189     * methods.
    190     */
    191    public static void setSeed(long seed) {
    192        sSeed = seed;
    193        sRandom = new Random(sSeed);
    194    }
    195
    196    /** Gets current seed */
    197    public static long getSeed() {
    198        return sSeed;
    199    }
    200
    201    /** Gets random number generator */
    202    public static Random getRandom() {
    203        return sRandom;
    204    }
    205
    206    /** Generates random double number */
    207    public static double random() {
    208        return sRandom.nextDouble();
    209    }
    210
    211    /** Configurates log4j loging */
    212    public static void configureLogging() {
    213        Properties props = new Properties();
    214        props.setProperty("log4j.rootLogger", "DEBUG, A1");
    215        props.setProperty("log4j.appender.A1", "org.apache.log4j.ConsoleAppender");
    216        props.setProperty("log4j.appender.A1.layout", "org.apache.log4j.PatternLayout");
    217        props.setProperty("log4j.appender.A1.layout.ConversionPattern", "%-5p %c{2}: %m%n");
    218        props.setProperty("log4j.logger.net", "INFO");
    219        props.setProperty("log4j.logger.net.sf.cpsolver", "DEBUG");
    220        props.setProperty("log4j.logger.org", "INFO");
    221        PropertyConfigurator.configure(props);
    222    }
    223
    224    /**
    225     * Configurates log4j loging
    226     *
    227     * @param logDir
    228     *            output folder
    229     * @param properties
    230     *            some other log4j properties
    231     */
    232    public static String configureLogging(String logDir, Properties properties) {
    233        return configureLogging(logDir, properties, false);
    234    }
    235
    236    public static String configureLogging(String logDir, Properties properties, boolean timeInFileName) {
    237        return configureLogging(logDir, properties, timeInFileName, true);
    238    }
    239
    240    /**
    241     * Configurates log4j loging
    242     *
    243     * @param logDir
    244     *            output folder
    245     * @param properties
    246     *            some other log4j properties
    247     * @param timeInFileName
    248     *            if true log file is named debug_yyyy-MM-dd_(HH.mm.ss).log, it
    249     *            is named debug.log otherwise
    250     */
    251    public static String configureLogging(String logDir, Properties properties, boolean timeInFileName,
    252            boolean includeSystemOuts) {
    253        String time = new java.text.SimpleDateFormat("yyyy-MM-dd_(HH.mm.ss)", java.util.Locale.US).format(new Date());
    254        (new File(logDir)).mkdirs();
    255        String fileName = logDir + File.separator + (timeInFileName ? "debug_" + time : "debug") + ".log";
    256        Properties props = (properties != null ? properties : new Properties());
    257        if (!props.containsKey("log4j.rootLogger")) {
    258            props.setProperty("log4j.rootLogger", "debug, LogFile");
    259            if (timeInFileName)
    260                props.setProperty("log4j.appender.LogFile", "org.apache.log4j.FileAppender");
    261            else {
    262                props.setProperty("log4j.appender.LogFile", "org.apache.log4j.DailyRollingFileAppender");
    263                props.setProperty("log4j.appender.LogFile.DatePattern", "'.'yyyy-MM-dd");
    264            }
    265            props.setProperty("log4j.appender.LogFile.File", fileName);
    266            props.setProperty("log4j.appender.LogFile.layout", "org.apache.log4j.PatternLayout");
    267            props.setProperty("log4j.appender.LogFile.layout.ConversionPattern",
    268                    "%d{dd-MMM-yy HH:mm:ss.SSS} [%t] %-5p %c{2}> %m%n");
    269        }
    270        PropertyConfigurator.configure(props);
    271        Logger log = Logger.getRootLogger();
    272        log.info("-----------------------------------------------------------------------");
    273        log.info("IFS debug file");
    274        log.info("");
    275        log.info("Created: " + new Date());
    276        log.info("");
    277        log.info("System info:");
    278        log.info("System:      " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " "
    279                + System.getProperty("os.arch"));
    280        log.info("CPU:         " + System.getProperty("sun.cpu.isalist") + " endian:"
    281                + System.getProperty("sun.cpu.endian") + " encoding:" + System.getProperty("sun.io.unicode.encoding"));
    282        log.info("Java:        " + System.getProperty("java.vendor") + ", " + System.getProperty("java.runtime.name")
    283                + " " + System.getProperty("java.runtime.version", System.getProperty("java.version")));
    284        log.info("User:        " + System.getProperty("user.name"));
    285        log.info("Timezone:    " + System.getProperty("user.timezone"));
    286        log.info("Working dir: " + System.getProperty("user.dir"));
    287        log.info("Classpath:   " + System.getProperty("java.class.path"));
    288        log.info("");
    289        if (includeSystemOuts) {
    290            System.setErr(new PrintStream(new LogOutputStream(System.err, Logger.getLogger("STDERR"), Level.ERROR)));
    291            System.setOut(new PrintStream(new LogOutputStream(System.out, Logger.getLogger("STDOUT"), Level.DEBUG)));
    292        }
    293        return fileName;
    294    }
    295
    296    /**
    297     * Loads data properties. If there is INCLUDE property available, it is
    298     * interpreted as semi-colon separated list of porperty files which should
    299     * be also loaded (works recursively).
    300     *
    301     */
    302    public static DataProperties loadProperties(File propertyFile) {
    303        FileInputStream is = null;
    304        try {
    305            DataProperties ret = new DataProperties();
    306            is = new FileInputStream(propertyFile);
    307            ret.load(is);
    308            is.close();
    309            is = null;
    310            if (ret.getProperty("INCLUDE") != null) {
    311
    312                StringTokenizer stk = new StringTokenizer(ret.getProperty("INCLUDE"), ";");
    313                while (stk.hasMoreTokens()) {
    314                    String aFile = stk.nextToken();
    315                    System.out.println("  Loading included file '" + aFile + "' ... ");
    316                    if ((new File(aFile)).exists())
    317                        is = new FileInputStream(aFile);
    318                    if ((new File(propertyFile.getParent() + File.separator + aFile)).exists())
    319                        is = new FileInputStream(propertyFile.getParent() + File.separator + aFile);
    320                    if (is == null)
    321                        System.err.println("Unable to find include file '" + aFile + "'.");
    322                    ret.load(is);
    323                    is.close();
    324                    is = null;
    325                }
    326                ret.remove("INCLUDE");
    327            }
    328            return ret;
    329        } catch (Exception e) {
    330            System.err.println("Unable to load property file " + propertyFile);
    331            e.printStackTrace();
    332            return new DataProperties();
    333        } finally {
    334            try {
    335                if (is != null)
    336                    is.close();
    337            } catch (IOException e) {
    338            }
    339        }
    340    }
    341
    342    public static boolean equals(Object o1, Object o2) {
    343        return (o1 == null ? o2 == null : o1.equals(o2));
    344    }
    345
    346    private static class LogOutputStream extends OutputStream {
    347        private Logger iLogger = null;
    348        private Level iLevel = null;
    349        private OutputStream iOldOutputStream;
    350        private ByteArrayOutputStream iOut = new ByteArrayOutputStream();
    351
    352        public LogOutputStream(OutputStream oldOutputStream, Logger logger, Level level) {
    353            iLogger = logger;
    354            iLevel = level;
    355            iOldOutputStream = oldOutputStream;
    356        }
    357
    358        @Override
    359        public void write(int b) throws IOException {
    360            iOldOutputStream.write(b);
    361            if (b == '\r')
    362                return;
    363            if (b == '\n') {
    364                iOut.flush();
    365                iLogger.log(iLevel, new String(iOut.toByteArray()));
    366                iOut.reset();
    367            } else
    368                iOut.write(b);
    369        }
    370    }
    371
    372    public static <E> List<E> toList(E... obj) {
    373        List<E> ret = new ArrayList<E>(obj == null ? 0 : obj.length);
    374        if (obj != null)
    375            for (E e: obj)
    376                ret.add(e);
    377        return ret;
    378    }
    379}