001package com.hammurapi.extract;
002
003import java.util.Arrays;
004import java.util.Set;
005
006/**
007 * Result of comparison of two extractors/predicates.
008 * @author Pavel Vlasov
009 *
010 */
011public class ComparisonResult {
012        
013        // Not mapping constants
014        public static final ComparisonResult EQUAL_NM =  new ComparisonResult(Type.EQUAL, null);
015        public static final ComparisonResult UNEQUAL_NM =  new ComparisonResult(Type.UNEQUAL, null);
016        public static final ComparisonResult LESS_RESTRICTIVE_NM =  new ComparisonResult(Type.LESS_RESTRICTIVE, null);
017        public static final ComparisonResult MORE_RESTRICTIVE_NM =  new ComparisonResult(Type.MORE_RESTRICTIVE, null);
018        public static final ComparisonResult NOT_EQUAL_NM =  new ComparisonResult(Type.NOT_EQUAL, null);
019        public static final ComparisonResult OPPOSITE_NM =  new ComparisonResult(Type.OPPOSITE, null);
020        public static final ComparisonResult OPPOSITE_LESS_RESTRICTIVE_NM =  new ComparisonResult(Type.OPPOSITE_LESS_RESTRICTIVE, null);
021        public static final ComparisonResult OPPOSITE_MORE_RESTRICTIVE_NM =  new ComparisonResult(Type.OPPOSITE_MORE_RESTRICTIVE, null);
022        
023        /**
024         * Result of extractor comparison.
025         * @author Pavel Vlasov.
026         *
027         */
028        public enum Type {
029                /**
030                 * Extractors are equal.
031                 */
032                EQUAL,
033                /**
034                 * Extractors are not equal, i.e. no conclusion can be drawn about values
035                 * returned by extractors.
036                 */
037                NOT_EQUAL,
038                
039                // Enums below are applicable only to predicates.
040                /**
041                 * This predicate is less restrictive than the parameter
042                 * predicate. E.g. x<=5 is less restrictive than x<5. 
043                 */
044                LESS_RESTRICTIVE,
045                /**
046                 * This predicate is more restrictive than the parameter
047                 * predicate. E.g. x<6 is more restrictive than x<7. 
048                 */
049                MORE_RESTRICTIVE,
050                /**
051                 * When this predicate evaluates to true, the parameter
052                 * predicate evaluates to false and vice-versa.
053                 */
054                OPPOSITE,
055                /**
056                 * The opposite of this predicate is less restrictive than 
057                 * the parameter predicate. 
058                 * E.g. x>5 opposite is x<=5 and it is is less restrictive than x<5.
059                 */             
060                OPPOSITE_LESS_RESTRICTIVE,
061                /**
062                 * The opposite of this predicate is more restrictive than 
063                 * the parameter predicate. 
064                 * E.g. x>5 opposite is x<=5 and it is is more restrictive than x<7. 
065                 */             
066                OPPOSITE_MORE_RESTRICTIVE,
067                
068                // Enums applicable to extractors and constants.
069                
070                /**
071                 * Value extracted by the first extractor is less than value extracted by the second extractor
072                 * for any combination of parameters.
073                 */
074                LESS_THAN,
075                
076                /**
077                 * Value extracted by the first extractor is less than or equal to the value extracted by the second extractor
078                 * for any combination of parameters.
079                 */
080                LESS_EQUAL,
081                
082                /**
083                 * Value extracted by the first extractor is more than or equal to the value extracted by the second extractor
084                 * for any combination of parameters.
085                 */
086                MORE_EQUAL,
087                
088                /**
089                 * Value extracted by the first extractor is more than value extracted by the second extractor
090                 * for any combination of parameters.
091                 */
092                MORE_THAN,
093                                                
094                /**
095                 * Value extracted by the first extractor is not equal to the value extracted by the second extractor
096                 * for any combination of parameters. Do not mistake this with NOT_EQUAL. For boolean values (predicates) UNEQUAL is
097                 * equivalent to OPPOSITE.
098                 */
099                UNEQUAL
100        }
101
102        private ComparisonResult.Type type;
103        private int[] indexMap;
104        
105        public ComparisonResult(ComparisonResult.Type type, int[] indexMap) {
106                this.type = type;
107                this.indexMap = indexMap;
108        }
109        
110        /**
111         * @return mapping of indices of parameter extractor to this extractor. E.g. extractors
112         * <code>arg[0] &lt; arg[1]</code> and <code>arg[1] &gt; arg[0]</code> are equal with index mapping {1, 0}.
113         * <code>null</code> is returned for no mapping (if extractors are <code>NOT_EQUAL</code>) and for 1 to 1 mapping. 
114         * In other words, if cr = e1.compareTo(e2), 
115         * then new MappedExtractor(e1, cr.getIndexMap()).compareTo(e2).isOneToOneMapping()==true or
116         * return value of new MappedExtractor(e1, cr.getIndexMap()).extract() is the same as of e2.extract() 
117         */
118        public int[] getIndexMap() {
119                return indexMap;
120        }
121        
122        public Type getType() {
123                return type;
124        }
125        
126        public boolean isOneToOneMapping() {
127                return isOneToOneMapping(indexMap);
128        }
129        
130        public static boolean isOneToOneMapping(int[] map) {
131                if (map==null) {
132                        return true;
133                }
134                for (int i=0; i<map.length; ++i) {
135                        if (map[i]!=i && map[i]!=-1) {
136                                return false;
137                        }
138                }
139                return true;                            
140        }
141        
142        public boolean compareIndexMaps(int[] otherIndexMap) {
143                if (indexMap==null) { // Assumes 1-1 mapping
144                        if (otherIndexMap==null) {
145                                return true;
146                        }
147                        
148                        for (int i=0; i<otherIndexMap.length; ++i) {
149                                if (otherIndexMap[i]!=i) {
150                                        return false;
151                                }
152                        }
153                        return true;
154                }
155                
156                if (otherIndexMap==null) {
157                        for (int i=0; i<indexMap.length; ++i) {
158                                if (indexMap[i]!=i) {
159                                        return false;
160                                }
161                        }
162                        return true;                            
163                }
164                
165                return Arrays.equals(indexMap, otherIndexMap);
166        }
167
168        @Override
169        public String toString() {
170                return "ComparisonResult [type=" + type + ", indexMap="
171                                + Arrays.toString(indexMap) + "]";
172        }
173
174        /**
175         * Inverses index map.
176         * @param indexMap2
177         * @return
178         */
179        public static int[] inverse(int[] map, Set<Integer> parameterIndices) {
180                if (map==null) {
181                        return null;
182                }
183                
184                int dim = -1;
185                for (int i=0; i<map.length; ++i) {
186                        if (parameterIndices.contains(i)) {
187                                if (map[i]>dim) {
188                                        dim = map[i];
189                                }
190                        }
191                }
192                int[] ret = new int[dim+1];
193                
194                for (int i=0; i<map.length; ++i) {
195                        if (parameterIndices.contains(i)) {
196                                ret[map[i]] = i;
197                        }
198                }
199                return ret;
200        }
201
202        @Override
203        public int hashCode() {
204                final int prime = 31;
205                int result = 1;
206                result = prime * result + Arrays.hashCode(indexMap);
207                result = prime * result + ((type == null) ? 0 : type.hashCode());
208                return result;
209        }
210
211        @Override
212        public boolean equals(Object obj) {
213                if (this == obj)
214                        return true;
215                if (obj == null)
216                        return false;
217                if (getClass() != obj.getClass())
218                        return false;
219                ComparisonResult other = (ComparisonResult) obj;
220                if (!(isOneToOneMapping() && other.isOneToOneMapping())) {
221                        if (!Arrays.equals(indexMap, other.indexMap))
222                                return false;
223                }
224                
225                if (type != other.type)
226                        return false;
227                return true;
228        }
229        
230}