/* * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * Copyright 2013 Aurelian Tutuianu * Copyright 2014 Aurelian Tutuianu * Copyright 2015 Aurelian Tutuianu * Copyright 2016 Aurelian Tutuianu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package rapaio.data; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * A mapping is a collection of row numbers used to build a mapped frame as a * wrapped selection of rows. * <p> * The mapping holds the rows which needs to be kept in the mapped frame. * <p> * If a mapped frame is built over another mapped frame, than the provided * mapping at creation time will be transformed into a mapped of the * solid frame which is referenced by the wrapped frame. * <p> * User: <a href="mailto:padreati@yahoo.com">Aurelian Tutuianu</a> */ public interface Mapping extends Serializable { // static builders /** * @return an empty mapping */ static Mapping empty() { return new ListMapping(); } /** * Builds a mapping having the mapped values specified as parameter, * the list of values being used as reference inside mapping. * * @param mapping list of mapped values * @return new mapping which wraps the given list of indexed values */ static Mapping wrap(List<Integer> mapping) { return new ListMapping(mapping, false); } /** * Builds a mapping having the mapped values given as a list of indexed values, * a copy of the list of values is used. * * @param mapping list of mapped values * @return new mapping which is build on a copy of the list of values */ static Mapping copy(List<Integer> mapping) { return new ListMapping(mapping, false); } /** * Builds a mapping having the mapped values given as an array of indexed values, * a copy of the values is used. * * @param mapping array of mapped values * @return new mapping which is build on a copy of the array of values */ static Mapping copy(int... mapping) { return new ListMapping(mapping); } static Mapping range(int end) { return range(0, end); } static Mapping range(int start, int end) { return new IntervalMapping(start, end); } /** * @return the size of mapping */ int size(); /** * Gets mapped index for given position * * @param pos given position * @return mapped value */ int get(int pos); /** * Adds at the end of mapping a mapped index * * @param row mapped index */ void add(int row); /** * Adds at the end of mapping the given indexes contained in collection * * @param rows collection of row numbers to be added to the mapping */ void addAll(Collection<Integer> rows); /** * Removes the element from the given position. * * @param pos position of the element which will be removed */ void remove(int pos); /** * Removes all elements from the mapping * * @param positions collection with positions which will be removed */ void removeAll(Collection<Integer> positions); /** * Removes all elements from mapping */ void clear(); /** * Builds a stream of indexes values * * @return a stream of indexed values */ IntStream rowStream(); /** * Builds an array of values with rows indexes */ default int[] toArray() { return rowStream().toArray(); } } final class ListMapping implements Mapping { private static final long serialVersionUID = 5485844129188037454L; private final List<Integer> mapping; ListMapping() { this.mapping = new ArrayList<>(); } ListMapping(int[] rows) { mapping = IntStream.of(rows).boxed().collect(Collectors.toList()); } ListMapping(List<Integer> mapping, boolean copy) { this.mapping = copy ? mapping.subList(0, mapping.size()) : mapping; } public int size() { return mapping.size(); } public int get(int pos) { if (mapping.size() > pos) return mapping.get(pos); throw new IllegalArgumentException("Value at pos " + pos + " does not exists"); } public void add(int pos) { mapping.add(pos); } public void addAll(Collection<Integer> pos) { mapping.addAll(pos); } @Override public void remove(int pos) { mapping.remove(pos); } @Override public void removeAll(Collection<Integer> positions) { positions.forEach(mapping::remove); } @Override public void clear() { mapping.clear(); } public IntStream rowStream() { return mapping.stream().mapToInt(i -> i); } } final class IntervalMapping implements Mapping { private static final long serialVersionUID = -7421133121383028265L; private final int start; private final int end; private boolean onList = false; private ListMapping listMapping; IntervalMapping(int start, int end) { this.start = start; this.end = end; } @Override public int size() { if (onList) return listMapping.size(); return end - start; } @Override public int get(int pos) { if (onList) return listMapping.get(pos); return pos + start; } @Override public void add(int row) { if (!onList) { onList = true; listMapping = new ListMapping(IntStream.range(start, end).toArray()); } listMapping.add(row); } @Override public void addAll(Collection<Integer> rows) { if (!onList) { onList = true; listMapping = new ListMapping(IntStream.range(start, end).toArray()); } listMapping.addAll(rows); } @Override public void remove(int pos) { if (!onList) { onList = true; listMapping = new ListMapping(IntStream.range(start, end).toArray()); } listMapping.remove(pos); } @Override public void removeAll(Collection<Integer> positions) { if (!onList) { onList = true; listMapping = new ListMapping(IntStream.range(start, end).toArray()); } listMapping.removeAll(positions); } @Override public void clear() { if (!onList) { onList = true; listMapping = new ListMapping(IntStream.range(start, end).toArray()); } listMapping.clear(); } @Override public IntStream rowStream() { if (onList) return listMapping.rowStream(); return IntStream.range(start, end); } }