/*
* GeneralDataType.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.evolution.datatype;
import dr.util.Identifiable;
import java.util.*;
/**
* Implements a general DataType for any number of states
*
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: GeneralDataType.java,v 1.11 2005/05/24 20:25:56 rambaut Exp $
*/
public class GeneralDataType extends DataType implements Identifiable {
public static final String GENERAL_DATA_TYPE = "generalDataType";
public static final String DESCRIPTION = GENERAL_DATA_TYPE;
public static final int TYPE = GENERAL;
public static final GeneralDataType INSTANCE = new GeneralDataType();
// for BEAUti trait PartitionSubstitutionModel
public GeneralDataType() {}
/**
* Unlike the other standard data types, this general one has a public
* constructor so multiple instances can be created.
*
* @param stateCodes the codes of the states
*/
public GeneralDataType(final String[] stateCodes) {
for (int i = 0; i < stateCodes.length; i++) {
State state = new State(i, stateCodes[i]);
states.add(state);
stateMap.put(stateCodes[i], state);
}
stateCount = states.size();
this.ambiguousStateCount = 0;
}
/**
* Unlike the other standard data types, this general one has a public
* constructor so multiple instances can be created.
*
* @param stateCodes the codes of the states
*/
public GeneralDataType(final Collection<String> stateCodes) {
int i = 0;
for (String code : stateCodes) {
State state = new State(i, code);
states.add(state);
stateMap.put(code, state);
i++;
}
stateCount = states.size();
this.ambiguousStateCount = 0;
}
/**
* Add an alias (a state code that represents a particular state).
* Note that all this does is put an extra entry in the stateNumbers
* array.
*
* @param alias a string that represents the state
* @param code the state number
*/
public void addAlias(String alias, String code) {
State state =stateMap.get(code);
if (state == null) {
throw new IllegalArgumentException("DataType doesn't contain the state, " + code);
}
stateMap.put(alias, state);
}
/**
* Add an ambiguity (a state code that represents multiple states).
*
* @param code a string that represents the state
* @param ambiguousStates the set of states that this code refers to.
*/
public void addAmbiguity(String code, String[] ambiguousStates) {
int n = ambiguousStateCount + stateCount;
int[] indices = new int[ambiguousStates.length];
int i = 0;
for (String stateCode : ambiguousStates) {
State state =stateMap.get(stateCode);
if (state == null) {
throw new IllegalArgumentException("DataType doesn't contain the state, " + stateCode);
}
indices[i] = state.number;
i++;
}
State state = new State(n, code, indices);
states.add(state);
ambiguousStateCount++;
stateMap.put(code, state);
}
@Override
public char[] getValidChars() {
return null;
}
/**
* Get state corresponding to a code
*
* @param code string code
* @return state
*/
public int getState(String code) {
if (code.equals("?")) {
return getUnknownState();
}
if (!stateMap.containsKey(code)) {
return -1;
}
return stateMap.get(code).number;
}
/**
* Override this function to cast to string codes...
* @param c character
*
* @return
*/
public int getState(char c) {
return getState(String.valueOf(c));
}
/**
* Get state corresponding to an unknown
*
* @return state
*/
public int getUnknownState() {
return stateCount + ambiguousStateCount;
}
/**
* Get state corresponding to a gap
*
* @return state
*/
public int getGapState() {
return getUnknownState();
}
/**
* Get character corresponding to a given state
*
* @param state state
* @return corresponding code
*/
public String getCode(int state) {
return states.get(state).code;
}
/**
* returns an array containing the non-ambiguous states
* that this state represents.
*/
public int[] getStates(int state) {
return states.get(state).ambiguities;
}
/**
* returns an array containing the non-ambiguous states that this state represents.
*/
public boolean[] getStateSet(int state) {
boolean[] stateSet = new boolean[stateCount];
if (state < states.size()) {
State s = states.get(state);
for (int i = 0; i < stateCount; i++) {
stateSet[i] = false;
}
for (int i = 0, n = s.ambiguities.length; i < n; i++) {
stateSet[s.ambiguities[i]] = true;
}
} else if (state == states.size()) {
for (int i = 0; i < stateCount; i++) {
stateSet[i] = true;
}
} else {
throw new IllegalArgumentException("invalid state index");
}
return stateSet;
}
/**
* description of data type
*
* @return string describing the data type
*/
public String getDescription() {
if (id != null) {
return id;
} else {
return DESCRIPTION;
}
}
/**
* type of data type
*
* @return integer code for the data type
*/
public int getType() {
return TYPE;
}
// **************************************************************
// Identifiable IMPLEMENTATION
// **************************************************************
private String id = null;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
private List<State> states = new ArrayList<State>();
private Map<String, State> stateMap = new TreeMap<String, State>();
private class State {
int number;
String code;
int[] ambiguities;
State(int number, String code) {
this.number = number;
this.code = code;
this.ambiguities = new int[]{number};
}
State(int number, String code, int[] ambiguities) {
this.number = number;
this.code = code;
this.ambiguities = ambiguities;
}
}
}