package accrue.analysis.defuse;

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 DefUseAbsVal implements AbstractValue<DefUseAbsVal> {
    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;
    }

    protected final Set<EdgeIdentifier> rd;
    protected final int hashCode;
    public static final DefUseAbsVal EMPTY = new DefUseAbsVal() {
        @Override
        public boolean isBottom() {
            return true;
        }
    };

    public DefUseAbsVal() {
        this(Collections.<EdgeIdentifier> emptySet());
    }

    public DefUseAbsVal(Set<EdgeIdentifier> rd) {
        this.rd = memoize(rd);
        hashCode = rd.hashCode();
    }

    @Override
    public String toString() {
        return "DefUseAbsVal[" + rd + "]";
    }

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

    public Set<EdgeIdentifier> reachingDefinitions() {
        return rd;
    }

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

    @Override
    public boolean leq(DefUseAbsVal that) {
        return that.rd.containsAll(this.rd);
    }

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

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

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

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

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