package accrue.analysis.antidep;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import accrue.analysis.interprocanalysis.EdgeIdentifier;
import accrue.analysis.interprocvarcontext.AbstractValue;

/** 
 * Track the uses and definitions of locations.
 * @author owen
 *
 */
public class AntiDepAbsVal implements AbstractValue<AntiDepAbsVal> {

    private static Map<Set<EdgeIdentifier>, Set<EdgeIdentifier>> memos =
            new HashMap<Set<EdgeIdentifier>, Set<EdgeIdentifier>>();

    private static Set<EdgeIdentifier> memoize(Set<EdgeIdentifier> ru) {
        Set<EdgeIdentifier> set = memos.get(ru);
        if (set == null) {
            memos.put(ru, ru);
            set = ru;
        }
        return set;
    }

    /**
     * Uses reaching this node.
     */
    protected final Set<EdgeIdentifier> ru;

    public static final AntiDepAbsVal EMPTY = new AntiDepAbsVal() {
        @Override
        public boolean isBottom() {
            return true;
        }
    };

    public AntiDepAbsVal() {
        this.ru = Collections.emptySet();
    }

    public AntiDepAbsVal(Set<EdgeIdentifier> ru) {
        if (ru == null) throw new NullPointerException();
        this.ru = memoize(ru);
    }

    @Override
    public String toString() {
        return "AntiDepAbsVal[" + ru + "]";
    }

    public AntiDepAbsVal addReachingUse(EdgeIdentifier d) {
        Set<EdgeIdentifier> rd = new HashSet<EdgeIdentifier>(this.ru);
        rd.add(d);
        rd = memoize(rd);
        return new AntiDepAbsVal(rd);
    }

    public Set<EdgeIdentifier> reachingUses() {
        return ru;
    }

    @Override
    public AntiDepAbsVal widen(AntiDepAbsVal that) {
        return upperBound(that);
    }

    @Override
    public boolean leq(AntiDepAbsVal that) {
        return that.ru.containsAll(this.ru);
    }

    @Override
    public AntiDepAbsVal upperBound(AntiDepAbsVal that) {
        if (that == null || that == this) return this;

        Set<EdgeIdentifier> joinUses = new HashSet<EdgeIdentifier>(this.ru);
        joinUses.addAll(that.ru);
        joinUses = memoize(joinUses);
        return new AntiDepAbsVal(joinUses);
    }

    @Override
    public boolean isBottom() {
        return false;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof AntiDepAbsVal) {
            AntiDepAbsVal that = (AntiDepAbsVal) obj;
            return this.ru.equals(that.ru);
        }
        else return false;
    }

    @Override
    public int hashCode() {
        return ru.hashCode();
    }

}
