001/*
002        AUTOMATICALLY GENERATED BY jTemp FROM
003        /Users/jsh2/Work/openimaj/target/checkout/core/core-feature/src/main/jtemp/org/openimaj/feature/Multidimensional#T#FV.jtemp
004*/
005/**
006 * Copyright (c) 2011, The University of Southampton and the individual contributors.
007 * All rights reserved.
008 *
009 * Redistribution and use in source and binary forms, with or without modification,
010 * are permitted provided that the following conditions are met:
011 *
012 *   *  Redistributions of source code must retain the above copyright notice,
013 *      this list of conditions and the following disclaimer.
014 *
015 *   *  Redistributions in binary form must reproduce the above copyright notice,
016 *      this list of conditions and the following disclaimer in the documentation
017 *      and/or other materials provided with the distribution.
018 *
019 *   *  Neither the name of the University of Southampton nor the names of its
020 *      contributors may be used to endorse or promote products derived from this
021 *      software without specific prior written permission.
022 *
023 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
024 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
025 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
026 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
027 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
028 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
029 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
030 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
032 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
033 */
034package org.openimaj.feature;
035
036import java.io.DataInput;
037import java.io.DataOutput;
038import java.io.IOException;
039import java.io.PrintWriter;
040import java.io.Serializable;
041import java.util.Scanner;
042
043/**
044 * Basic byte multidimensional feature vector implementation
045 *
046 * @author Jonathon Hare
047 */
048public class MultidimensionalByteFV extends ByteFV implements Serializable, Cloneable, FeatureVector {
049        private static final long serialVersionUID = 1L;
050
051        /**
052         * Array containing the number of bins in each dimension 
053         */
054        public int nbins[];
055        
056        /**
057         * Construct an empty feature vector
058         */
059        public MultidimensionalByteFV() {}
060
061        /**
062         * Construct empty FV with given dimensions
063         * @param nbins the number of bins in each dimension
064         */
065        public MultidimensionalByteFV(int... nbins) {
066                super();
067                
068                if(nbins.length == 0) throw new IllegalArgumentException("must be at least one dimension");
069
070                this.nbins = nbins;
071                int sz=nbins[0];
072                for (int i=1; i<nbins.length; i++) sz *= nbins[i];
073                values = new byte[sz];
074        }
075        
076        /**
077         * Construct from flattened values array and dimensions 
078         * @param values the flat array of values
079         * @param nbins the number of bins in each dimension
080         */
081        public MultidimensionalByteFV(byte [] values, int...nbins) {
082                super();
083                
084                if(nbins.length == 0) throw new IllegalArgumentException("must be at least one dimension");
085                this.values = values;
086                this.nbins = nbins;
087        }
088        
089        /**
090         * Construct from 2-dimensional array 
091         * @param values the 2d array
092         */
093        public MultidimensionalByteFV(byte [][] values) {
094                this((values.length == 0 ? 0 : values[0].length), values.length);
095                
096                for (int j=0; j<values.length; j++) {
097                        for (int i=0; i<values[0].length; i++) {
098                                this.values[i + j*values[0].length] = values[j][i];
099                        }
100                }
101        }
102        
103        /**
104         * Construct from 3-dimensional array 
105         * @param values the 3d array
106         */
107        public MultidimensionalByteFV(byte [][][] values) {
108                this(values[0][0].length, values[0].length, values.length);
109                
110                for (int k=0; k<values.length; k++) {
111                        for (int j=0; j<values[0].length; j++) {
112                                for (int i=0; i<values[0][0].length; i++) {
113                                        this.values[i + j*values[0][0].length + k*values[0][0].length*values[0].length] = values[k][j][i];
114                                }
115                        }
116                }
117        }
118        
119        /**
120         * Get the underlying flat-vector representation 
121         * @return a flat 1d representation of the feature
122         */
123        @Override
124        public byte [] getVector() {
125                return values;
126        }
127        
128        /**
129         * Get the element at the given flat index
130         * @param x the flattened element index
131         * @return the value corresponding to x
132         */
133        @Override
134        public byte get(int x) {
135                return values[x];
136        }
137
138        /**
139         * Set the element at the given flat index
140         * @param value the value to set
141         * @param x the flattened element index
142         */
143        @Override
144        void set(byte value, int x) {
145                values[x] = value;
146        }
147        
148    /**
149     * Convert the given coordinate into a flat index value
150     * @param coords the coordinate
151     * @return the flattened index
152     */
153    public int getIndex(int... coords) {
154        if (coords.length != nbins.length)
155                        throw new IllegalArgumentException("given dimensions mismatch");
156
157                int idx = coords[0];
158        for (int i=1; i<coords.length; i++) {
159                        int mult = nbins[0];
160                        for (int j=1; j<i; j++)
161                                mult *= nbins[j];
162                    
163            idx += coords[i] * mult;
164                }
165
166                return idx;
167    }
168
169    /**
170     * Set the the element at the given n-dimensional coordinate
171     * @param value the value to set
172     * @param coords the coordinates
173     */
174    public void set(byte value, int... coords) {
175        values[getIndex(coords)] = value;
176    }
177
178        /**
179         * Get the element at the given n-dimensional coordinate
180         * @param coords the coordinates
181         * @return the value at the given coordinates
182         */
183        public byte get(int... coords) {
184        int idx = getIndex(coords);
185                
186                return values[idx];
187        }
188        
189        @Override
190        public MultidimensionalByteFV clone() {
191                MultidimensionalByteFV model = (MultidimensionalByteFV) super.clone();
192                model.nbins = nbins.clone();
193                return model;
194        }
195
196        @Override
197        public String toString() {
198                String ret = this.getClass().getName();
199                for (int dim : nbins) ret += "[" + dim + "]";
200                ret += " {";
201
202                if (nbins.length == 2) {
203                        ret += "\n";
204                        for (int i=0; i<nbins[1]; i++) {
205                                for (int j=0; j<nbins[0]; j++) {
206                                        ret += String.format("%2.4f", values[j + i*nbins[0]]);
207                                        //                                              if (j != nbins[0]) {
208                                        ret += ", ";
209                                        //                                              }
210                                }
211                                ret += "\n";
212                        }
213                } else {
214                        for (int i=0; i<values.length; i++) {
215                                ret += String.format("%2.3f", values[i]);
216                                if (i != values.length-1) ret += ", ";
217                        }
218                }
219
220                ret += "}";
221
222                return ret;
223        }
224
225        /**
226         * Convert the FV to a DoubleFV representation
227         * @return the DoubleFV representation
228         */
229        @Override
230        public MultidimensionalDoubleFV asDoubleFV() {
231                return new MultidimensionalDoubleFV(asDoubleVector(), nbins.clone());
232        }
233                
234        /**
235         * Compare this FV to another with the given method.
236         *
237         * @param h the feature to compare against.
238         * @param method the method to compare with. 
239         * @return a score determined by the comparison method.
240         */
241        public double compare(MultidimensionalByteFV h, ByteFVComparison method) {
242                return method.compare(this, h);
243        }
244        
245        @Override
246        public void writeBinary(DataOutput out) throws IOException {
247                out.writeInt(nbins.length);
248                for (int i=0; i<nbins.length; i++) out.writeInt(nbins[i]);
249                for (int i=0; i<values.length; i++) out.writeByte(values[i]);
250        }
251
252        @Override
253        public void writeASCII(PrintWriter out) throws IOException {
254                out.print(nbins.length + " ");
255                for (int i=0; i<nbins.length; i++) out.print(nbins[i] + " ");
256                out.println();
257                for (int i=0; i<values.length; i++) out.print( values[i] + " ");
258                out.println();
259        }
260
261        @Override
262        public void readBinary(DataInput in) throws IOException {
263                int len = in.readInt();
264                
265                if (nbins == null || nbins.length!=len) nbins = new int[len];
266                for (int i=0; i<len; i++) nbins[i] = in.readInt();
267                
268                int sz=nbins[0];
269                for (int i=1; i<nbins.length; i++) sz *= nbins[i];
270                values = new byte[sz];
271                for (int i=0; i<sz; i++) values[i] = in.readByte();
272        }
273
274        @Override
275        public void readASCII(Scanner in) throws IOException {
276                String [] line = in.nextLine().trim().split(" ");
277                int len = Integer.parseInt(line[0]);
278                
279                if (nbins == null || nbins.length!=len) nbins = new int[len];
280                for (int i=0; i<len; i++) nbins[i] = Integer.parseInt(line[i+1]);
281                
282                int sz=nbins[0];
283                for (int i=1; i<nbins.length; i++) sz *= nbins[i];
284                values = new byte[sz];
285                line = in.nextLine().trim().split(" ");
286                for (int i=0; i<sz; i++) values[i] = Byte.parseByte(line[i]);
287        }
288
289        @Override
290        public byte[] binaryHeader() {
291                return (this.getClass().getName().substring(0,2) + "MDFV").getBytes();
292        }
293
294        @Override
295        public String asciiHeader() {
296                return this.getClass().getName() + " ";
297        }
298        
299        /**
300         * Given an index, calculate the coordinate which would map to that index using {@link #getIndex(int...)}
301         * @param index
302         * @return the coordinates
303         */
304        public int[] getCoordinates(int index){
305                int ndims = nbins.length;
306                int[] start = new int[ndims];
307                float multiply = 1;
308                for (int i : nbins) multiply *= i;
309                
310                if(index >= multiply) throw new IllegalArgumentException();
311                if(index < 0) throw new IllegalArgumentException();
312                
313                for (int i=ndims-1; i>=0; i--) {
314                        multiply/=nbins[i]; // divide the multiplier by the current slice
315                        int numberOfSlices = (int) Math.floor(index / multiply);
316                        start[i] = numberOfSlices;
317                        
318                        index = (int) (index - (numberOfSlices * multiply));
319                }
320                return start;
321        }
322}