/*******************************************************************************
* Copyright 2014 Felipe Takiyama
*
* 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 br.usp.poli.takiyama.common;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import br.usp.poli.takiyama.prv.Prv;
import br.usp.poli.takiyama.prv.RangeElement;
import br.usp.poli.takiyama.prv.StdPrv;
import br.usp.poli.takiyama.prv.Substitution;
import br.usp.poli.takiyama.prv.Term;
import br.usp.poli.takiyama.utils.Lists;
public class ConstantFactor implements Factor {
private final List<? extends Prv> variables;
private final int size;
/* ************************************************************************
* Constructors
* ************************************************************************/
private ConstantFactor(List<? extends Prv> variables) {
this.variables = Lists.listOf(variables);
this.size = getSize(variables);
}
/**
* Returns the expected size of this factor.
*/
private static int getSize(List<? extends Prv> variables) {
int size = 1;
if (variables.isEmpty()) {
size = 0;
}
for (Prv prv : variables) {
size = size * prv.range().size();
}
return size;
}
/* ************************************************************************
* Static factories
* ************************************************************************/
/**
* Returns a constant parameterized factor.
* <p>
* A constant factor returns the value 1 for all tuples in the factor.
* </p>
*
* @param variables A ordered list of {@link Prv}.
*/
public static Factor getInstance(List<? extends Prv> variables) {
return new ConstantFactor(variables);
}
/* ************************************************************************
* Getters
* ************************************************************************/
@Override
public int getIndex(Tuple<RangeElement> tuple) throws IllegalArgumentException {
if (tuple.isEmpty()) {
throw new IllegalArgumentException("This tuple is empty!");
}
int index = 0;
int r = 1;
for (int i = tuple.size() - 1; i >= 0; i--) {
index = index + r * indexOf(i, tuple);
r = r * rangeSize(i);
}
return index;
}
/**
* Returns the index of the range element that occupies the specified
* position in the tuple.
*
* @param i The position in the tuple
* @param tuple A tuple of {@link RangeElement}
* @return
*/
private int indexOf(int i, Tuple<RangeElement> tuple) {
return variables.get(i).range().indexOf(tuple.get(i));
}
/**
* Returns PRV's range size occupying the specified position in this
* factor.
*
* @param i PRV's index in this factor
* @return PRV's range size occupying the specified position in this
* factor.
*/
private int rangeSize(int i) {
return variables.get(i).range().size();
}
private RangeElement rangeElementAt(int rangeIndex, int prvIndex) {
return variables.get(prvIndex).range().get(rangeIndex);
}
@Override
public Tuple<RangeElement> getTuple(int index) {
List<RangeElement> values = new ArrayList<RangeElement>(variables.size());
for (int j = variables.size() - 1; j > 0; j--) {
int domainSize = rangeSize(j);
values.add(rangeElementAt(index % domainSize, j));
index = index / domainSize;
}
values.add(rangeElementAt(index, 0));
Collections.reverse(values);
return Tuple.getInstance(values);
}
@Override
public BigDecimal getValue(int index) {
return BigDecimal.ONE;
}
@Override
public BigDecimal getValue(Tuple<RangeElement> tuple) {
return getValue(0);
}
@Override
public Iterator<Tuple<RangeElement>> iterator() {
return new Iterator<Tuple<RangeElement>> () {
int nextElementToReturn;
@Override
public boolean hasNext() {
return nextElementToReturn != size;
}
@Override
public Tuple<RangeElement> next() {
int i = nextElementToReturn;
if (i > size) {
throw new NoSuchElementException();
}
nextElementToReturn = i + 1;
return getTuple(i);
}
/**
* Throws {@link UnsupportedOperationException}.
*/
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return size;
}
@Override
public String name() {
return "1";
}
@Override
public List<Prv> variables() {
return Lists.listOf(variables);
}
@Override
public List<BigDecimal> values() {
//System.out.println("Trying to create array of size " + size);
//System.out.println("Trying to create factor for " + variables.toString());
List<BigDecimal> values = new ArrayList<BigDecimal>(size);
Lists.fill(values, BigDecimal.ONE, size);
return values;
}
@Override
public boolean contains(Term t) {
for (Prv prv : variables) {
if (prv.contains(t)) {
return true;
}
}
return false;
}
@Override
public int occurrences(Term t) {
int count = 0;
for (Prv prv : variables) {
if (prv.contains(t)) {
count++;
}
}
return count;
}
@Override
public Prv getVariableHaving(Term t) {
Prv result = StdPrv.getInstance();
for (Prv prv : variables) {
if (prv.contains(t)) {
result = prv;
}
}
return result;
}
@Override
public boolean isSubFactorOf(Factor factor) {
return factor.variables().containsAll(variables);
}
@Override
public boolean isConstant() {
return true;
}
@Override
public boolean isEmpty() {
return variables.isEmpty();
}
@Override
public Factor apply(Substitution s) {
return ConstantFactor.getInstance(Lists.apply(s, Lists.listOf(variables)));
}
/**
* Throws {@link UnsupportedOperationException}.
*/
@Override
public Factor set(Tuple<RangeElement> tuple, BigDecimal value) {
throw new UnsupportedOperationException();
}
@Override
public Factor sumOut(Prv prv) {
List<Prv> vars = variables();
vars.remove(prv);
return ConstantFactor.getInstance(vars);
}
@Override
public Factor pow(int p, int q) {
return this;
}
@Override
public Factor multiply(Factor factor) {
return factor;
}
@Override
public Factor reorder(Factor reference) throws IllegalArgumentException {
if (!Lists.sameElements(variables(), reference.variables())) {
throw new IllegalArgumentException();
}
Factor result = ConstantFactor.getInstance(reference.variables());
return result;
}
/* ************************************************************************
* hashCode, equals and toString
* ************************************************************************/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + size;
result = prime * result
+ ((variables == null) ? 0 : variables.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ConstantFactor)) {
return false;
}
ConstantFactor other = (ConstantFactor) obj;
if (size != other.size) {
return false;
}
if (variables == null) {
if (other.variables != null) {
return false;
}
} else if (!variables.equals(other.variables)) {
return false;
}
return true;
}
@Override
public String toString() {
// StringBuilder result = new StringBuilder();
//
// if (this.variables.isEmpty()) {
// return "Empty factor";
// }
//
// String thinRule = "";
// String thickRule = "";
// String cellFormat = "%-10s"; //TODO: change to something more dynamic
// String valueCellFormat = "%-10s\n";
//
// // Create the rules - aesthetic
// for (int i = 0; i <= this.variables.size(); i++) {
// thinRule += String.format(cellFormat, "").replace(" ", "-");
// }
// thickRule = thinRule.replace("-", "=");
//
// // Top rule
// result.append(thickRule).append("\n");
//
// // Print the variables names
// for (Prv prv : variables) {
// result.append(String.format(cellFormat, prv.toString()));
// }
//
// // Value column
// result.append(String.format(cellFormat, "VALUE")).append("\n");
//
// // Mid rule
// result.append(thinRule).append("\n");
//
// // Print the contents
// for (Tuple<RangeElement> tuple : this) {
// for (int j = 0; j < tuple.size(); j++) {
// result.append(String.format(cellFormat, tuple.get(j)));
// }
// // Round the value to 6 digits
// result.append(String.format(valueCellFormat, BigDecimal.ONE));
// }
//
// // Bottom rule
// result.append(thickRule).append("\n");
//
// return result.toString();
return "";
}
}