/*
* Copyright 2011 Uwe Krueger.
*
* 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 com.mandelsoft.mand.mapping;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.mandelsoft.mand.MandelRaster;
/**
*
* @author Uwe Krueger
*/
public class StatisticMapper2 extends MapperSupport {
static public boolean debug=false;
static public final int VERSION=1;
private double factor;
private double limit;
public StatisticMapper2()
{ this(0);
}
public StatisticMapper2(double factor)
{ this(factor,1.0);
}
public StatisticMapper2(double factor, double limit)
{ this.factor=factor;
this.limit=limit;
}
public String getName()
{
return "Statistic";
}
public String getParamDesc()
{
return "f="+factor+",l="+limit;
}
public double getFactor()
{
return factor;
}
public double getLimit()
{
return limit;
}
///////////////////////////////////////////////////////////////
// statistic
///////////////////////////////////////////////////////////////
protected class Histogram extends RasterInfo {
int[] histogram;
Histogram(MandelRaster r)
{ super(r);
}
@Override
protected void analyseRaster(MandelRaster r)
{
super.analyseRaster(r);
histogram=new int[getSize()];
int[][] raster=r.getRaster();
for (int y=0; y<r.getRY(); y++) {
for (int x=0; x<r.getRX(); x++) {
int i=raster[y][x];
if (i>0) {
histogram[i-minIt]++;
}
}
}
}
int histMax()
{ int max=0;
for (int i=0; i<histogram.length; i++) {
if (histogram[i]>max) max=histogram[i];
}
return max;
}
/////////////////////////////////////////////////////////////////////////
// joining method
/////////////////////////////////////////////////////////////////////////
int[] points;
int[] accu;
int last;
int setupColors()
{
points=new int[histogram.length];
accu=new int[histogram.length];
last=-1;
int cnt=0;
int cur=histogram.length-1;
for (int i=histogram.length-1; i>=0; i--) {
if ((accu[i]=histogram[i])>0) {
if (last<0) {
last=i;
}
else {
points[cur]=i;
}
cur=i;
cnt++;
}
}
points[cur]=-1;
return cnt;
}
private class Pointer {
int cur;
int prev;
}
Pointer minUsedColor()
{ Pointer p=new Pointer();
int cur=points.length-1;
int prev=-1;
p.cur=cur;
p.prev=prev;
while (cur>=0) {
if (accu[cur]<accu[p.cur]) {
p.cur=cur;
p.prev=prev;
}
prev=cur;
cur=points[cur];
}
return p;
}
int joinNext(Pointer p)
{
if (p.cur==1 || p.prev==1)
if (debug) System.out.println("join next "+p.cur);
accu[p.cur]+=accu[points[p.cur]];
points[p.cur]=points[points[p.cur]];
return p.cur;
}
int joinPrev(Pointer p)
{
if (p.cur==1 || p.prev==1)
if (debug) System.out.println("join prev "+p.cur+" ("+p.prev+")");
accu[p.prev]+=accu[p.cur];
points[p.prev]=points[p.cur];
return p.prev;
}
int joinColor()
{ Pointer p=minUsedColor();
return joinColor(p);
}
int joinColor(Pointer p)
{
if (points[p.cur]<0) {
return joinPrev(p);
}
else if (p.prev<0) {
return joinNext(p);
}
else if (accu[points[p.cur]]<accu[p.prev]) {
return joinNext(p);
}
else {
return joinPrev(p);
}
}
int compressColors(int n, int[] mapping, BreakCondition c)
{
int num=setupColors();
int max=histMax();
int bound=(int)(limit*max);
while (num>n) {
Pointer p=minUsedColor();
if (c!=null && c.done(num, accu[p.cur])) break;
int cur=joinColor(p);
if (bound-accu[cur]>0) {
accu[cur]+=(bound-accu[cur])*factor;
}
num--;
}
if (debug) System.out.println("p(0)->"+points[0]);
if (debug) System.out.println("p(1)->"+points[1]);
if (debug) System.out.println("compressed");
int cur=points.length-1;
int it=cur;
n=num;
while (it>=0) {
if (it==points[cur]) {
cur=points[cur];
n--;
}
if (n<=0) {
throw new IllegalStateException(
"need more colors than expected (it="+it+")");
}
mapping[it--]=n;
}
return num;
}
}
public interface BreakCondition {
public boolean done(int num, int accu);
}
///////////////////////////////////////////////////////////////
// mapping
///////////////////////////////////////////////////////////////
public Mapping createMapping(MandelRaster raster, int colmapsize)
{ Histogram info=new Histogram(raster);
int[] mapping=new int[info.getSize()];
int s=createMapping(info,colmapsize,mapping);
// System.out.println("0->"+mapping[0]);
// System.out.println("1->"+mapping[1]);
// System.out.println("2->"+mapping[2]);
return new Mapping(info.getMinIt(),info.getMaxIt(),s,mapping);
}
protected int createMapping(Histogram info, int colmapsize, int[] mapping)
{
return info.compressColors(colmapsize-1,mapping,null)+1;
// return colmapsize;
}
///////////////////////////////////////////////////////////////
// io info
///////////////////////////////////////////////////////////////
@Override
protected int getDefaultVersion()
{
return VERSION;
}
@Override
protected boolean validVersion(int v)
{
return 1<=v && v<=VERSION;
}
/////////////////////////////////////////////////////////////////////////
// IO
/////////////////////////////////////////////////////////////////////////
protected void _write(DataOutputStream dos, int v) throws IOException
{
switch (v) {
case 1:
writeV1(dos);
break;
default:
throw new IOException("unknown cyclic mapping version "+v);
}
}
protected void writeV1(DataOutputStream dos) throws IOException
{ dos.writeDouble(factor);
dos.writeDouble(limit);
}
@Override
protected void _read(DataInputStream dis, int v) throws IOException
{
switch (v) {
case 1:
readV1(dis);
break;
default:
throw new IOException("unknown cyclic mapping version "+v);
}
}
protected void readV1(DataInputStream dis) throws IOException
{ factor=dis.readDouble();
limit=dis.readDouble();
}
}