Formula Evaluator for XIRR function in Java - java

Facing issue with reading a cell in excel which is set with XIRR function.
I written my code in Java. Below is the code to set the formula. Please help on how can I read the value from the cell and not the formula.
cell.setCellFormula("XIRR(E2:E10, B2:B10");
CellStyle style = workbook.createCellStyle();
style.setDataFormat(workbook.createDataFormat().getFormat("0.00%"));
cell.setCellStyle(style);
Below is the error while evaluating the cell using FormulaEvaluator
org.apache.poi.ss.formula.eval.NotImplementedFunctionException: XIRR
at org.apache.poi.ss.formula.atp.AnalysisToolPak$NotImplemented.evaluate(AnalysisToolPak.java:59)
at org.apache.poi.ss.formula.UserDefinedFunction.evaluate(UserDefinedFunction.java:61)
at org.apache.poi.ss.formula.OperationEvaluatorFactory.evaluate(OperationEvaluatorFactory.java:129)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateFormula(WorkbookEvaluator.java:550)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:317)
... 18 more

Without patching apache poi with XIRR function directly calculating a result like Excel's XIRR function is possible using a User Defined Function in apache poi.
The following code provides exactly this.
It defines a class CalculateXIRR which then will be used as myXIRR function in apache poi. The CalculateXIRR uses either JXIRR - v1.0.0 (C) 2005 Gautam Satpathy or class Xirr derived from java program to calculate XIRR without using excel or any other library to calculate XIRR.
Also it provides code for test cases. At first the same test case as from the example in Excel's XIRR documentation. And then random test cases using random values and dates. Those test cases are written into an Excel workbook. Written are the result of the evaluation of the user defined myXIRR function as well as Excel's original XIRR function. So we can comparing the results.
My tests have shown that both XIRR calculation methods are pretty exact like Excel using reasonable values and dates. Only using values and dates which leads Excel's XIRR resulting in high negative percentages (lower than -60%) or very high percentages (greater than 1000%) both methods are different from Excel.
JXIRR - v1.0.0 from Gautam Satpathy is better suited to Excel as the class Xirr. The reason is pretty clear since the class Xirr will always fail if x in Math.pow((x + 1d), (dt0-dt) / 365d) is lower than -1d. If so, then the base of the Math.pow function is negative and since the exponent (dt0-dt) / 365d) is fractional, there is only a imaginary solution. This happens if Excel's XIRR is resulting in high negative percentages and the approximation tries to come from below -100%. JXIRR uses a goal seek method which seems to be more like the one of Excel itself.
Code:
import java.io.* ;
import org.apache.poi.ss.formula.functions.* ;
import org.apache.poi.ss.formula.udf.* ;
import org.apache.poi.ss.usermodel.* ;
import org.apache.poi.xssf.usermodel.* ;
import org.apache.poi.ss.formula.* ;
import org.apache.poi.ss.formula.eval.* ;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Random;
/*
https://github.com/ept/jxirr
(C) 2005 Gautam Satpathy
*/
import in.satpathy.financial.*;
public class XIRREvaluator {
private Workbook workbook;
private Sheet sheet;
private Row row;
private Cell cell;
private CellStyle percentStyle;
private CellStyle dateStyle;
private FormulaEvaluator evaluator;
private String[] labels;
private char c1;
private char c2;
private String[] formulas;
private Double[] values;
private SimpleDateFormat sdf;
private Date[] dates;
public XIRREvaluator() {
this.workbook = new XSSFWorkbook();
String[] functionNames = { "myXIRR" } ;
FreeRefFunction[] functionImpls = { new CalculateXIRR() } ;
UDFFinder udfs = new DefaultUDFFinder( functionNames, functionImpls ) ;
UDFFinder udfToolpack = new AggregatingUDFFinder( udfs ) ;
workbook.addToolPack(udfToolpack);
this.percentStyle = workbook.createCellStyle();
percentStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00%"));
this.dateStyle = workbook.createCellStyle();
dateStyle.setDataFormat(workbook.createDataFormat().getFormat("yyyy-MM-dd"));
this.evaluator = workbook.getCreationHelper().createFormulaEvaluator();
this.sheet = workbook.createSheet("Sheet1");
this.labels = new String[]{"XIRR", "myXIRR", "diff"};
this.sdf = new SimpleDateFormat("yyyy-MM-dd");
}
public void save() {
try {
workbook.write(new FileOutputStream("ExcelWorkbookXIRR.xlsx"));
workbook.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private void testCaseFromExcelDocu(int startCol, int startRow) {
/*
This provides a test case as from the example in Excel's XIRR documentation:
https://support.office.com/en-us/article/XIRR-function-de1242ec-6477-445b-b11b-a303ad9adc9d
*/
if (startCol > 24) return;
try {
c1 = (char)(65+startCol);
c2 = (char)(65+startCol+1);
formulas = new String[]{"XIRR("+c1+(startRow+4)+":"+c1+(startRow+8)+","+c2+(startRow+4)+":"+c2+(startRow+8)+")",
"myXIRR("+c1+(startRow+4)+":"+c1+(startRow+8)+","+c2+(startRow+4)+":"+c2+(startRow+8)+")",
""+c2+(startRow+1)+"-"+c2+(startRow+2)};
values = new Double[]{-10000d, 2750d, 4250d, 3250d, 2750d};
dates = new Date[]{sdf.parse("2008-01-01"), sdf.parse("2008-03-01"), sdf.parse("2008-10-30"), sdf.parse("2009-02-15"), sdf.parse("2009-04-01")};
for (int r = startRow; r < startRow+3; r++) {
row = (sheet.getRow(r)==null)?sheet.createRow(r):sheet.getRow(r);
cell = row.createCell(startCol);
cell.setCellValue(labels[r-startRow]);
}
for (int r = startRow+3; r < startRow+8; r++) {
row = (sheet.getRow(r)==null)?sheet.createRow(r):sheet.getRow(r);
cell = row.createCell(startCol);
cell.setCellValue(values[r-startRow-3]);
cell = row.createCell(startCol+1);
cell.setCellValue(dates[r-startRow-3]);
cell.setCellStyle(dateStyle);
}
for (int r = startRow; r < startRow+2; r++) {
cell = sheet.getRow(r).createCell(startCol+1);
cell.setCellFormula(formulas[r-startRow]);
cell.setCellStyle(percentStyle);
if (r == startRow+1) {
cell = evaluator.evaluateInCell(cell);
System.out.println(new DataFormatter().formatCellValue(cell));
}
}
cell = sheet.getRow(startRow+2).createCell(startCol+1);
cell.setCellFormula(formulas[2]);
sheet.autoSizeColumn(startCol);
sheet.autoSizeColumn(startCol+1);
} catch (Exception e) {
e.printStackTrace();
}
}
private void randomTestCases(int startCol, int startRow, int count) {
/*
This provides randon test cases
*/
try {
long day = 24L*60L*60L*1000L;
long startDate = sdf.parse("2010-01-01").getTime();
for (int test = startCol; test < startCol+3*count; test+=3) {
if (test > 24) return;
c1 = (char)(65+test);
c2 = (char)(65+test+1);
Random rnd = new Random();
int rows = 5+rnd.nextInt(5);
formulas = new String[]{"XIRR("+c1+(startRow+4)+":"+c1+(startRow+3+rows)+","+c2+(startRow+4)+":"+c2+(startRow+3+rows)+")",
"myXIRR("+c1+(startRow+4)+":"+c1+(startRow+3+rows)+", "+c2+(startRow+4)+":"+c2+(startRow+3+rows)+")",
""+c2+(startRow+1)+"-"+c2+(startRow+2)};
values = new Double[rows];
values[0] = -1d*(rows-1d)*(1000+rnd.nextInt(5000));
for (int i = 1; i < rows; i++) {
values[i] = 1d*(1000+rnd.nextInt(5000));
}
dates = new Date[rows];
for (int i = 0; i < rows; i++) {
dates[i] = sdf.parse(sdf.format(new Date(startDate+=day*(1L+rnd.nextInt(150)))));
}
for (int r = startRow; r < startRow+3; r++) {
row = (sheet.getRow(r)==null)?sheet.createRow(r):sheet.getRow(r);
cell = row.createCell(test);
cell.setCellValue(labels[r-startRow]);
}
for (int r = startRow+3; r < startRow+3+rows; r++) {
row = (sheet.getRow(r)==null)?sheet.createRow(r):sheet.getRow(r);
cell = row.createCell(test);
cell.setCellValue(values[r-startRow-3]);
cell = row.createCell(test+1);
cell.setCellValue(dates[r-startRow-3]);
cell.setCellStyle(dateStyle);
}
for (int r = startRow; r < startRow+2; r++) {
cell = sheet.getRow(r).createCell(test+1);
cell.setCellFormula(formulas[r-startRow]);
cell.setCellStyle(percentStyle);
if (r == startRow+1) {
evaluator.clearAllCachedResultValues();
cell = evaluator.evaluateInCell(cell);
System.out.println(new DataFormatter().formatCellValue(cell));
}
}
cell = sheet.getRow(startRow+2).createCell(test+1);
cell.setCellFormula(formulas[2]);
sheet.autoSizeColumn(test);
sheet.autoSizeColumn(test+1);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main( String[] args ) {
XIRREvaluator xirrEvaluator = new XIRREvaluator();
//test case as from the example in Excel's XIRR documentation
//starting on column 0, row 0
xirrEvaluator.testCaseFromExcelDocu(0,0);
//9 random test cases
//starting on column 0, row 10
xirrEvaluator.randomTestCases(0,10,9);
//9 random test cases
//starting on column 0, row 25
xirrEvaluator.randomTestCases(0,25,9);
xirrEvaluator.save();
}
}
/*
Class for user defined function myXIRR
*/
class CalculateXIRR implements FreeRefFunction {
#Override
public ValueEval evaluate( ValueEval[] args, OperationEvaluationContext ec ) {
if (args.length < 2 || args.length > 3) {
return ErrorEval.VALUE_INVALID;
}
double result;
try {
double[] values = ValueCollector.collectValues(args[0]);
double[] dates = ValueCollector.collectValues(args[1]);
double guess;
if(args.length == 3) {
ValueEval v = OperandResolver.getSingleValue(args[2], ec.getRowIndex(), ec.getColumnIndex()) ;
guess = OperandResolver.coerceValueToDouble(v);
} else {
guess = 0.1d;
}
result = calculateXIRR( values, dates, guess ) ;
checkValue(result);
} catch (EvaluationException e) {
//e.printStackTrace();
return e.getErrorEval();
}
return new NumberEval( result ) ;
}
public double calculateXIRR(double[] values, double[] dates, double guess ) {
double result;
/*
Either calculating XIRR using https://github.com/ept/jxirr (C) 2005 Gautam Satpathy
*/
XIRRData data = new XIRRData(values.length, guess, values, dates);
result = XIRR.xirr(data) - 1d;
/*
Or calculating XIRR Class Xirr
from https://stackoverflow.com/questions/36789967/java-program-to-calculate-xirr-without-using-excel-or-any-other-library
*/
//result = Xirr.Newtons_method(guess, values, dates);
return result;
}
static final void checkValue(double result) throws EvaluationException {
if (Double.isNaN(result) || Double.isInfinite(result)) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
}
static final class ValueCollector extends MultiOperandNumericFunction {
private static final ValueCollector instance = new ValueCollector();
public ValueCollector() {
super(false, false);
}
public static double[] collectValues(ValueEval...operands) throws EvaluationException {
return instance.getNumberArray(operands);
}
protected double evaluate(double[] values) {
throw new IllegalStateException("should not be called");
}
}
}
/*
Class Xirr from https://stackoverflow.com/questions/36789967/java-program-to-calculate-xirr-without-using-excel-or-any-other-library
*/
final class Xirr {
private static final double tol = 0.00000001;
private static double f_xirr(double p, double dt, double dt0, double x) {
double resf = p * Math.pow((x + 1d), (dt0-dt) / 365d);
return resf;
}
private static double df_xirr(double p, double dt, double dt0, double x) {
double resf = (1d / 365d) * (dt0-dt) * p * Math.pow((x + 1d), ((dt0-dt) / 365d) - 1d);
return resf;
}
private static double total_f_xirr(double[] payments, double[] days, double x) {
double resf = 0d;
for (int i = 0; i < payments.length; i++) {
resf = resf + f_xirr(payments[i], days[i], days[0], x);
}
return resf;
}
private static double total_df_xirr(double[] payments, double[] days, double x) {
double resf = 0d;
for (int i = 0; i < payments.length; i++) {
resf = resf + df_xirr(payments[i], days[i], days[0], x);
}
return resf;
}
public static double Newtons_method(double guess, double[] payments, double[] days) {
double x0 = guess;
double x1 = 0d;
double err = 1e+100;
while (err > tol) {
x1 = x0 - total_f_xirr(payments, days, x0) / total_df_xirr(payments, days, x0);
err = Math.abs(x1 - x0);
x0 = x1;
}
return x0;
}
}

Related

Octave dfdp.m for JAVA

I want to implement Levenberg Marquardt fitting in JAVA and found apache commons math suitable. Since I want to fit a function, where I dont have the derivative to calculate the gradient or Jacobian, I need somthing like dfdp.m from GNU octave to calculate numerical derivatives. Has someone done this already?
I did it myself, in case someone else needs it here is the approach
dfdp.m code
m=size(x,1); if (m==1), m=size(x,2); end %# PAK: in case #cols > #rows
n=length(p); %dimensions
ps=p; prt=zeros(m,n);del=zeros(n,1); % initialise Jacobian to Zero
for j=1:n
del(j)=dp(j) .*p(j); %cal delx=fract(dp)*param value(p)
if p(j)==0
del(j)=dp(j); %if param=0 delx=fraction
end
p(j)=ps(j) + del(j);
if del(j)~=0, f1=feval(func,x,p); %FJ ~= not equal (!=) ...> p is now (p + dp*p)
if dp(j) < 0, prt(:,j)=(f1-f)./del(j);
else
p(j)=ps(j) - del(j); %FJ ...> p is now (p - dp*p)
prt(:,j)=(f1-feval(func,x,p))./(2 .*del(j)); %FJ 2 steps from (ps + del) to (ps - del)
end
end
p(j)=ps(j); %restore p(j)
end
JAVA code
private static class GhoosProblem {
private double[][] data;
private double[] dp;
public GhoosProblem(double[][] datapoints, double[] delta_p) {
data = datapoints;
//dp= fractional increment of p for numerical derivatives
//dp(j)>0 central differences calculated
//dp(j)<0 one sided differences calculated
//dp(j)=0 sets corresponding partials to zero; i.e. holds p(j) fixed
dp = delta_p;
}
public MultivariateVectorFunction getModelFunction() {
return new MultivariateVectorFunction() {
public double[] value(double[] params) {
double[] values = new double[data.length];
for (int i = 0; i < values.length; ++i) {
final double t = data[i][0]; // get the double value
values[i] = params[0] *
Math.pow(t, params[2]) *
Math.exp(-params[1] * t); // Ghoos function
}
return values; // function values
}
};
}
public MultivariateMatrixFunction getModelFunctionJacobian2() {
return new MultivariateMatrixFunction() {
public double[][] value(double[] params) {
double[][] jacobian = new double[data.length][params.length];
final double a = params[0];
final double b = params[2];
final double c = params[1];
for (int i = 0; i < jacobian.length; ++i) {
final double t = data[i][0]; // get the double value
jacobian[i][0] = Math.pow(t, b) * Math.exp(-c*t);
jacobian[i][2] = a * Math.exp(-c*t) * Math.pow(t, b) * Math.log(t);
jacobian[i][1] = a * Math.pow(t, b) * (-t*Math.exp(-c*t));
}
//System.out.println("Jacobian= "+ Arrays.deepToString(jacobian));
return jacobian;
}
};
}
// compared to Ge2.m octave
public MultivariateMatrixFunction getModelFunctionJacobian() {
return new MultivariateMatrixFunction() {
public double[][] value(double[] params) {
int m = data.length; // cols
int n = params.length; // rows
double[] p = params;
double[] ps = params;
double[] del = new double[n];
double[] f = new double[n];
double[] f1 = new double[n];
BlockRealMatrix prt = new BlockRealMatrix(m, n); // initializes to zeros
f=feval(p);
for (int j=0; j<n; ++j) {
del[j]=dp[j] * p[j]; //delta_x=fractional(dp) * param value(p)
if (p[j]==0)
del[j]=dp[j]; //if param=0 delta_x=fractional(dp)
p[j]=ps[j] + del[j];
if (del[j]!=0) {
f1=feval(p); //p is now (p + dp*p)
if (dp[j]<0)
prt.setColumn(j,(new ArrayRealVector(f1)).subtract(new ArrayRealVector(f)).mapDivideToSelf(del[j]).toArray()); // one sided diff
else {
p[j]=ps[j] - del[j]; // p is now (p - dp*p)
prt.setColumn(j,(new ArrayRealVector(f1)).subtract(new ArrayRealVector(feval(p))).mapDivideToSelf(2*del[j]).toArray()); // central diff
}
}
p[j]=ps[j]; //restore p(j)
}//for
//System.out.println("Jacobian= "+ Arrays.deepToString(prt.getData()));
return prt.getData(); //jacobian, dimension is (m x n)
}
};
}
public double[] feval(double[] params) {
double[] values = new double[data.length];
for (int i = 0; i < values.length; ++i) {
final double t = data[i][0]; // get the double value
values[i] = params[0] *
Math.pow(t, params[2]) *
Math.exp(-params[1] * t); // Ghoos function
}
return values;
}
}//GhoosProblem
sorry if idention of code did not come out nice!
the relevant part is the getModelFunctionJacobian() -Function
I have renamed the analytical derivatives part as getModelFunctionJacobian2(), and posted here for comparison
to complete with here is the levenberg marquardt setup to use the GhoosFunction
public void fit() {
final double[][] dataPoints = { // x, y
//{0.0/60, 0.0}, // never use {0, 0} => org.apache.commons.math3.exception.ConvergenceException: illegal state: unable to perform Q.R decomposition on the 17x3 jacobian matrix
{15.0/60, 8.891104},
{30.0/60, 13.21852},
{45.0/60, 28.09051},
{60.0/60, 43.0011},
{75.0/60, 57.43561},
{90.0/60, 67.06862},
{105.0/60, 82.60239},
{120.0/60, 72.4649},
{135.0/60, 61.4},
{150.0/60, 43.97924},
{165.0/60, 30.6},
{180.0/60, 20.77112},
{195.0/60, 15.5},
{210.0/60, 10.85442},
{225.0/60, 9.33},
{240.0/60, 7.260234},
};
final double[] initialGuess = { 1.0, 1.0, 1.0 }; // p
final double[] fract_change = { 1E-4, 1E-4, 1E-4 }; // dp should be below 0.0001
final GhoosProblem problem = new GhoosProblem(dataPoints, fract_change);
final int len = dataPoints.length;
final double[] weights = new double[len];
final double[] target = new double[len];
for (int i = 0; i < len; i++){
weights[i] = 1.0;// / dataPoints[i][1];
target[i] = dataPoints[i][1];
}
final LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer()
.withCostRelativeTolerance(1E-4) // stol in octave
.withParameterRelativeTolerance(1E-4); // dp should be below 0.0001
final Optimum optimum = optimizer.optimize(
builder(problem)
.weight(new DiagonalMatrix(weights))
.target(target)
.start(initialGuess)
.maxIterations(100)
.build()
);
final RealVector solution = optimum.getPoint();
solution.setEntry(0, solution.getEntry(0) / 60.0); // go back to minutes
System.out.println("solution= " + solution);
System.out.println("CostRelativeTolerance= " + optimizer.getCostRelativeTolerance());
System.out.println("ParameterRelativeTolerance= " + optimizer.getParameterRelativeTolerance());
System.out.println("evaluations= " + optimum.getEvaluations());
System.out.println("iterations= " + optimum.getIterations());
//System.out.println("residuals= " + optimum.getResiduals());
System.out.println("RMS= " + optimum.getRMS());
System.out.println("sigma= " + optimum.getSigma(1E-10));
}//fit
public LeastSquaresBuilder builder(GhoosProblem problem){
return new LeastSquaresBuilder()
.checkerPair(new SimpleVectorValueChecker(1e-6, 1e-6)) // The SimpleVectorValueChecker Class (Simple implementation of the ConvergenceChecker) contains a method that uses the value of the function between two successive iterations of the optimisation algorithm to check if convergence has occured
.maxEvaluations(Integer.MAX_VALUE)
.maxIterations(Integer.MAX_VALUE)
//.lazyEvaluation(true)
.model(problem.getModelFunction(), problem.getModelFunctionJacobian());
}

Multithread increases calculation time - Java

I was asked to check calculation time depending on number of threads working on the problem. Therefore I had written a program that calculates integral using Monte Carlo method. I am dividing the range for number of threads. After that I stats threads, which calculate their part, and finally sum partial results to get general one.
The problem is that time of calculation increases with number of threads instead of decreasing (i7 processor, Windows 7)
A few people working on it, and we do not know why is that. I hope someone will give me an advice.
I attach code:
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Runner {
private static final int MAXT = 10; // maksymalna ilość wątków
static PrintWriter outM;
static PrintWriter outMTime;
public static void main(String[] args){
double xp = 2;
double xk = 3;
filesOp();
// Wypisywanie kolumn tabeli
for(int threadNumber=1; threadNumber<=MAXT; threadNumber++){
outM.print("\t"+ threadNumber);
outMTime.print("\t"+ threadNumber);
}
double time1;
double time2;
//double startTime=System.currentTimeMillis(); // Przed wystartowaniem programu
for(int n=10000; n<=10000000; n=n*10){
System.out.println("Licze dla: " + n + " punktow.");
outM.print("\n"+n);
outMTime.print("\n"+n);
for(int threadNumber=1; threadNumber<=MAXT; threadNumber++){
outM.print("\t");
outMTime.print("\t");
time1=System.nanoTime();
multiThread(xp, xk, n, threadNumber);
time2=System.nanoTime();
outMTime.print((time2-time1)/1000000);
// czas pracy dla danej liczby wątków
}
}
outM.close();
outMTime.close();
}
public static void multiThread(double xp, double xk, int n, int threadNumber){
// Funkcja licząca całkę wielowątkowo.
// Całka do policzenia jest dzielona pomiędzy wątki
ArrayList<Thread> threadList = new ArrayList<Thread>();
ConcurrentLinkedQueue<Double> results = new ConcurrentLinkedQueue<Double>();
for(int i=0; i<threadNumber; i++){
MonteCarlo mc = new MonteCarlo( xp+(i*((xk-xp)/threadNumber)), xp+((i+1)*((xk-xp)/threadNumber)), (int)(n/threadNumber), results);
Thread t = new Thread(mc);
threadList.add(t);
t.start();
}
//for(int j=0; j<threadNumber; j++){ // pętla czeka na zakończenie wątków
for(Thread t : threadList){
try {
//while(t.isAlive()){}
//threadList.get(j).join();
t.join();
} catch (Exception e) {
e.printStackTrace();
}
}
double wynik = 0;
//for(int k=0; k<results.size(); k++){
for(double r: results){
//wynik = wynik + results.remove();
wynik= wynik + r;
}
outM.print(wynik);
}
public static void filesOp(){
File fileTemp;
fileTemp = new File("wyniki.txt");
if (fileTemp.exists()) fileTemp.delete();
fileTemp = new File("pomiary.txt");
if (fileTemp.exists()) fileTemp.delete();
try {
outM = new PrintWriter(new FileWriter("wyniki.txt", true));
outMTime = new PrintWriter(new FileWriter("pomiary.txt", true));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class MonteCarlo implements Runnable{
double xp;
double xk;
long n;
ConcurrentLinkedQueue<Double> results;
MonteCarlo(double xp, double xk, long n, ConcurrentLinkedQueue<Double> results){
this.xp=xp;
this.xk=xk;
this.n=n;
this.results=results;
}
//funkcja dla ktorej obliczamy calke
private static double func(double x) {
return x*x+3;
}
private static double funcIn(double x, double y) {
if (( y > 0) && (y <= func(x)))
return 1;
else if (( y > 0) && (y <= func(x)))
return -1;
return 0;
}
//random number from a to b
private static double randomPoint(double a, double b) {
return a + Math.random() * (b-a);
}
public void run(){
double yp, yk, calka;
int pointsIn;
yp = 0;
yk = Math.ceil(Math.max(func(xp), func(xk)));
pointsIn = 0;
for (long i=0; i<n; i++) {
pointsIn += funcIn(randomPoint(xp, xk), randomPoint(yp, yk));
}
calka = (pointsIn / (double)n) * ((xk-xp) * (yk-yp));
results.add(calka);
}
}
And the example of results:
1 2 3 4 5 6 7 8 9 10
10000 6.185818 2.821405 3.721287 3.470309 4.068365 3.604195 4.323075 4.192455 6.159694 4.239105
100000 10.994522 15.874134 34.992323 40.851124 36.199631 49.54579 45.122417 61.427132 55.845435 60.7661
1000000 108.653008 274.443662 340.274574 407.054352 437.455361 469.853467 496.849012 584.519687 571.09329 594.152023
10000000 1066.059033 2877.947652 3600.551966 4175.707089 4488.434247 5081.572093 5501.217804 6374.335759 6128.274553 6339.043475
The problem most likely lies in
private static double randomPoint(double a, double b) {
return a + Math.random() * (b-a);
}
Math.random() performs poorly under heavy contention. If you are using java 7 or later, try this instead:
private static double randomPoint(double a, double b) {
return ThreadLocalRandom.current().nextDouble(a, b);
}
Using static funtions frequently is one of the pitfalls in Multithreading.
A more general answer can be found in this post already.

XYPlot doesn't draw all points correctly

I'm trying to display 550 data points with periodic peaks (the flat line is 61). The problem is, that androidplot isn't drawing all the points correctly! From my log:
ECG I values 61,61,62,63,62,61,61,61,61,67,71,68,61,53,61,61,61,61,61,61,61,61,62,63,64,64,64,63,62,61,61,61
I've got the rangeboundaries set to plot.setRangeBoundaries(0,100, BoundaryMode.AUTO);, but as you can see, the peaks never drop to the 53 data point. I can see this lower point sometimes, but it gets smoothed out a fraction of a second later (as you can see in the screenshot).
My line and point formatter is:
LineAndPointFormatter lapf = new LineAndPointFormatter(p.color, null, null, null);
lapf.getLinePaint().setStrokeJoin(Paint.Join.MITER);
lapf.getLinePaint().setStrokeWidth(1);
I've tried with the both Paint.Join.ROUND and Paint.Join.BEVEL and got the same effect. I've also used the debugger to check that 53 is being inserted into the series.
EDIT
After some debugging, it looks like my pulse loop thread is wrong:
while (keepRunning) {
for (PulseXYSeries j : series) {
for (int k = 0; k < j.plotStep; k++) {
int at = (j.position + k) % j.getSize();
if (j.pulsing) {
if (j.pulsePosition == j.pulseValues.size() - 1) {
j.pulsing = false;
j.pulsePosition = 0;
} else {
try {
int pulseVal = j.pulseValues.get(j.pulsePosition);
j.setY(pulseVal,at);
j.pulsePosition += 1;
} catch(IndexOutOfBoundsException e) {
j.pulsePosition = 0;
}
}
} else {
j.setY(j.pulseValues.get(0), at);
long currTime = SystemClock.elapsedRealtime();
if (currTime - j.getLastPulse() >= j.getPulseDelay()) {
j.pulsing = true;
j.setLastPulse(currTime);
}
}
j.remove(((at + j.eraserSize) % j.getSize()));
}
j.position = (j.position + 1) % j.getSize(); // fixed it by changing +1 to + j.plotStep
}
Thread.sleep(delay);
}
My custom series looks like:
private class PulseXYSeries implements XYSeries {
private List<Integer> pulseValues = new ArrayList<Integer>();
private int pulsePerMinute;
public int pulsePosition;
public int position;
private ArrayList<Integer> values;
private String title;
private long lastPulse;
public boolean pulsing = false;
public int eraserSize = 20;
public int plotStep = 3;
}

How to combine 2 images to form hybrid image?

I am implementing hybrid image with ImageJ and stuck at merging low filter image and high filter image to form a hybrid image.
This is what I already done.I have 2 images from Gaussian Blur and Laplician of Gaussian filer. I need to merge these 2 images by layer after that. Any idea how to achieve it?
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.filter.*;
import ij.plugin.*;
import ij.io.*;
import java.io.*;
public class HybridImage_Plugin implements PlugInFilter{
int cfsize=3;
String img_lowPass;
String img_highPass;
private double[][] filter;
private double sigma;
float w=2 ,delta=0 , thr=0;
int mode=0;
//dialogbox
private boolean GUI()
{
GenericDialog gd = new GenericDialog("Enter Values", IJ.getInstance());
gd.addNumericField("Sigma (3,5,9,17,35)", cfsize, 0);
gd.addStringField("Low-Pass", "/home/atrx/ImageJ/plugins/hybridimage/l1.tif");
gd.addStringField("High-Pass", "/home/atrx/ImageJ/plugins/hybridimage/l2.tif");
return getUserParams(gd);
}
//get parameters
private boolean getUserParams(GenericDialog gd)
{
gd.showDialog();
if (gd.wasCanceled())
{
return false;
}
cfsize = (int) gd.getNextNumber();
img_lowPass = gd.getNextString();
img_highPass= gd.getNextString();
return true;
}
public int setup(String arg, ImagePlus imp) {
return PlugInFilter.NO_IMAGE_REQUIRED;
}
public void run(ImageProcessor ip) {
int[][] result;
if(GUI() == false)
{
return;
}
else
{
Opener opener1 = new Opener();
Opener opener2 = new Opener();
ImagePlus imp1= opener1.openImage(img_lowPass);
ImagePlus imp2= opener2.openImage(img_highPass);
//imp1.show("Low Pass Image");
//imp2.show("HighPass Image");
ImageProcessor ip1 = imp1.getProcessor();
ImageProcessor ip2 = imp2.getProcessor();
//lowpass filter(Gaussian Blur)
ip1.blurGaussian(cfsize);
showProcessor(ip1,"Low Pass Filtered Image");
//highpass filter(LoG)
int csize = ip2.getHeight();
int rsize = ip2.getWidth();
Rectangle rect = ip2.getRoi();
int d0,a0,acr,dow,it;
int i,x,y;
double h12, h21, ft, h1h2, h2h1, fmu, dh, dv;
double r, dt, dmx, dmn;
float logaus[] = new float[(rect.width>rect.height)? rect.width : rect.height];
float gaus[] = new float[(rect.width>rect.height)? rect.width : rect.height];
float dgaus[] = new float[(rect.width>rect.height)? rect.width : rect.height];
long zcn =0;
byte pixels[] = (byte[])ip2.getPixels();
int img_in[] = new int[rect.width*rect.height];
if (cfsize<0) cfsize=3;
if (cfsize>35) cfsize=35;
if(w<0) w=0;
int fsize = (int)(cfsize*w);
if (fsize%2 == 0)
{
fsize += 1;
}
double dimg[] = new double[rect.height*rect.width];
double dr[] = new double[rect.height*rect.width];
i=0;
for(y=rect.y;y<(rect.y+rect.height);y++)
{
for(x=rect.x;x<(rect.x+rect.width);x++)
{
img_in[i] = (pixels[(y*rsize)+x]&0xff);
i++;
}
}
int size = rect.width + fsize -1;
int image[] = new int[(rect.width+fsize-1)*(rect.height+fsize-1)];
int extension= (fsize/2);
for( i=0; i<rect.height;i++)
{
System.arraycopy(img_in,(i*rect.width),image,( ((i+extension)*(rect.width+fsize-1))+ extension ),rect.width);
}
h1h2= h2h1 = h12 =0.0;
for(i=1; i<( (fsize+1) /2);i++)
{
w = (float)cfsize/(float)2.0/(float)1.414;
ft = i/w;
gaus[i] = (float)Math.exp(-ft*ft/2);
h1h2 += 2.0 *(gaus[i]);
logaus[i] =(float)(1-ft*ft)*(float)Math.exp(-ft*ft/2);
h2h1 += 2.0*(logaus[i]);
dgaus[i] =(float)ft*(float)Math.exp(-ft*ft/2);
}
fmu = (h2h1 + 1)* (h1h2+1);
int prel[] = new int[rect.width+1];
dmx = -99999.9;
dmn = 99999.9;
int limit = ((rect.width+fsize-1)*(rect.height+fsize-1));
for(d0=0;d0<rect.height;d0++)
{
for(a0=0;a0<rect.width;a0++)
{
acr = a0 + fsize/2;
dow = d0 + fsize/2;
dh = dv = 0.0;
h1h2 = h2h1 = 0.0;
for (int j=1; j<(fsize+1)/2; j++)
{
int a0d0, a0d1, a1d0, a1d1;
h12=h21=0.0;
for(i=1;i<(fsize+1)/2;i++)
{
a0d0 = acr-i+((dow-j)*size);
a0d1 = acr-i+((dow+j)*size);
a1d0 = acr+i+((dow-j)*size);
a1d1 = acr+i+((dow+j)*size);
h12 += logaus[i]*(image[a0d0] + image[a0d1]+
image[a1d0] + image[a1d1]);
h21 += gaus[i]* (image[a0d0] + image[a0d1] +
image[a1d0] + image[a1d1]);
}
a0d0 = acr-j+dow*size;
a0d1 = acr+(dow-j)*size;
a1d0 = acr+j+dow*size;
a1d1 = acr+(dow+j)*size;
h1h2 += gaus[j] * (h12+ image[a0d0]+image[a0d1]+
image[a1d0]+image[a1d1]);
h2h1 += logaus[j]*(h21+ image[a0d0]+ image[a0d1] +
image[a1d0] + image[a1d1] );
if(thr != 0.0)
{
dh += dgaus[j] * ( image[a1d0] - image[a0d0] );
dv += dgaus[j] * ( image[a1d1] - image[a0d1] );
}
}
dt = dimg[d0*rect.width+a0] = h1h2 + h2h1 + (2*image[dow*size+acr]) ;
if (dt > dmx) dmx = dt;
if (dt < dmn) dmn = dt;
if( thr!= 0.0)
{
dr[(d0*rect.width)+a0] = Math.abs(dh) + Math.abs(dv);
}
}
}
dmx = (dmx-dmn) / 2;
dmn += dmx;
int row=0, column=0;
for(d0=0;d0<rect.height;d0++)
{
for(a0=0;a0<rect.width;a0++)
{
int id = (d0*rect.width) +a0;
int index = rsize*(rect.y+d0) + (a0+rect.x);
int k = 15;
it = (int)(dt = (dimg[id] - (dmn-delta*dmx))*255 / (dmx*(1+Math.abs(delta))));
switch(mode){
case 0:
pixels[index] = (byte)((dt-dmn+dmx)/dmx*127);
break;
case 1:
pixels[index] = (byte)Math.abs(it);
break;
case 2:
pixels[index] = (byte)( ((dt!=0)?((dt>0) ? 1: -1) : 0) * 192);
break;
case 3:
default:
r = dr[id];
it = ( (dt!=0) ? ((dt>0) ? 1: -1) : 0);
if( it==0 && r>=thr)
{
k = 255;
zcn++;
}
else
{
if( (it*prel[a0]<0 || it*prel[a0+1]<0) && r>=thr)
{
k = 255;
zcn++;
}
}
prel[a0+1] = it;
if(k==255 || mode!=3)
pixels[index] = (byte)k;
break;
}
}
}
showProcessor(ip2,"High Pass Filtered Image");
}
}
static void showProcessor(ImageProcessor ip, String title){
ImagePlus win = new ImagePlus(title,ip);
win.show();
}
}
Have you tried performing a weighted sum?
OUT = w*LPF + (1 - w)*HPF
This kind of sum is used everywhere. In particular, image blending, alpha matting and even in some optimization schemes.
However because there are patches of varying spatial frequencies all around your image, you may have to make the weight adaptive. You also have to choose which one you want to emphasize more. Do you want the low pass or high pass information to stand out more? Depending on which you want, you might want to use information in either one of those images and run it through some distance or similarity measure to get the right weight.

How can I parse ASCII Art to HTML using Java or Javascript? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I saw that the Neo4j API uses ASCII Art very cleverly with its API:
http://jaxenter.com/getting-started-with-neo4j-the-java-graph-database-47955.html
I want to try something similar, but with ASCI Art to HTML. How can ASCII art be parsed, so for example, given an ASCII Art input something like:
--------------------------------
I I
I ------- ------- I
I I I I I I
I I A I I B I I
I I I I I I
I ------- ------- I
I I
I I
--------------------------------
: could result in HTML output something like:
<div>
<div style='display:inline;'>
A
</div>
<div style='display:inline;'>
B
</div>
</div>
Update
The question was closed citing that I need to "demonstrate a minimal understanding of the problem being solved.". I do have an understanding of the problem to be solved. The problem is that I want to solve is to make templated HTML easier to understand in source code for the following web framework:
https://github.com/zubairq/coils
: although the solution could be applied to any web framework. I have since seen someone attempt to make an initial version in C++ here:
https://github.com/h3nr1x/asciidivs2html/blob/master/asciidivs2html.cpp
: very impressive! If you can get it to work in Java or Clojure then if we can get the question reopened I will nominate a bounty so you can get more points for the solution :)
I ran the Java solution provided by #meewok and here is the result:
$ java AsciiToDIVs.RunConverter
Created a box(ID=0,X=0,Y=0,width=33,height=10)
Created a box(ID=1,X=2,Y=4,width=8,height=5,parent=0)
Created a char(Char=A,X=4,Y=7,parent=1)
Created a box(ID=2,X=2,Y=21,width=8,height=5,parent=0)
Created a char(Char=B,X=4,Y=24,parent=2)
<div><div><div>A</div></div><div><div>B</div></div></div>
Methodology
A solution to implement is the following:
create an in memory 2D array (array of arrays) which is similar to a chessboard.
Then i will create an algorith that when it detects "-" characters, i initialize acall to a method to detect the remaining corners ( top right, bottom left, bottom right) following the characters and where they end.
Example ( quick pseudocode ):
while(selectedCell==I)
selectedCell=selectedCell.goDown();
Using such a strategy you can map out your boxes and which boxes are contained within which.
Remaining would be to print this info as html..
Quick and Dirty Implementation
Since I was in the mood I spent an hour+ to quickly cook up a toy implementation.
The below is non-optimized in respect to that I do not make use of Iterators to go over Cells, and would need refactoring to become a serious framework.
Cell.java
package AsciiToDIVs;
public class Cell {
public char Character;
public CellGrid parentGrid;
private int rowIndex;
private int colIndex;
public Cell(char Character, CellGrid parent, int rowIndex, int colIndex)
{
this.Character = Character;
this.parentGrid = parent;
this.rowIndex = rowIndex;
this.colIndex = colIndex;
}
public int getRowIndex() {
return rowIndex;
}
public int getColIndex() {
return colIndex;
}
}
CellGrid.java
package AsciiToDIVs;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
public class CellGrid {
private ArrayList<ArrayList<Cell>> CellGridData;
public CellGrid(String asciiFile) throws IOException {
readDataFile(asciiFile);
}
public ArrayList<FoundObject> findBoxes(FoundBoxObject parent)
{
int startRowIndex = 0, startColIndex = 0,
parentRowLimit = Integer.MAX_VALUE,
parentColLimit = Integer.MAX_VALUE,
startingColIndex = 0;
if(parent != null)
{
startRowIndex = parent.getRowIndex()+1;
startColIndex = startingColIndex = parent.getColIndex()+1;
parentRowLimit = parent.getRowIndex() + parent.getHeight();
parentColLimit = parent.getColIndex() + parent.getWidth();
}
ArrayList<FoundObject> results = new ArrayList<FoundObject>();
Cell currentCell;
if(startRowIndex>=CellGridData.size())
return null;
for(; startRowIndex<CellGridData.size() && startRowIndex<parentRowLimit; startRowIndex++ )
{
startColIndex = startingColIndex;
for(; startColIndex< CellGridData.get(startRowIndex).size() && startColIndex<parentColLimit; startColIndex++)
{
FoundBoxObject withinBox = checkWithinFoundBoxObject(results, startRowIndex, startColIndex);
if(withinBox !=null)
startColIndex+=withinBox.getWidth();
currentCell = getCell(startRowIndex, startColIndex);
if(currentCell!=null)
{
if(currentCell.Character == '-') // Found a TOP-CORNER
{
int boxHeight = getConsecutiveIs(startRowIndex+1, startColIndex) + 1;
if(boxHeight>1)
{
int boxWidth = getConsecutiveDashes(startRowIndex, startColIndex);
FoundBoxObject box = new FoundBoxObject(startRowIndex, startColIndex, boxWidth, boxHeight, parent);
results.add(box);
findBoxes(box);
startColIndex+=boxWidth;
}
}
//This is a character
else if(currentCell.Character != '-' && currentCell.Character != 'I' && currentCell.Character != ' '
&& currentCell.Character != '\n' && currentCell.Character != '\n' && currentCell.Character != '\t')
{
FoundCharObject Char = new FoundCharObject(startRowIndex, startColIndex, parent, currentCell.Character);
results.add(Char);
}
}
}
}
if(parent!=null)
parent.containedObjects = results;
return results;
}
public static String printDIV(ArrayList<FoundObject> objects)
{
String result = "";
Iterator<FoundObject> it = objects.iterator();
FoundObject fo;
while(it.hasNext())
{
result+="<div>";
fo = it.next();
if(fo instanceof FoundCharObject)
{
FoundCharObject fc = (FoundCharObject)fo;
result+=fc.getChar();
}
if(fo instanceof FoundBoxObject)
{
FoundBoxObject fb = (FoundBoxObject)fo;
result+=printDIV(fb.containedObjects);
}
result+="</div>";
}
return result;
}
private FoundBoxObject checkWithinFoundBoxObject(ArrayList<FoundObject> results, int rowIndex, int colIndex)
{
Iterator<FoundObject> it = results.iterator();
FoundObject f;
FoundBoxObject fbox = null;
while(it.hasNext())
{
f = it.next();
if(f instanceof FoundBoxObject)
{
fbox = (FoundBoxObject) f;
if(rowIndex >= fbox.getRowIndex() && rowIndex <= fbox.getRowIndex() + fbox.getHeight())
{
if(colIndex >= fbox.getColIndex() && colIndex <= fbox.getColIndex() + fbox.getWidth())
{
return fbox;
}
}
}
}
return null;
}
private int getConsecutiveDashes(int startRowIndex, int startColIndex)
{
int counter = 0;
Cell cell = getCell(startRowIndex, startColIndex);
while( cell!=null && cell.Character =='-')
{
counter++;
cell = getCell(startRowIndex, startColIndex++);
}
return counter;
}
private int getConsecutiveIs(int startRowIndex, int startColIndex)
{
int counter = 0;
Cell cell = getCell(startRowIndex, startColIndex);
while( cell!=null && cell.Character =='I')
{
counter++;
cell = getCell(startRowIndex++, startColIndex);
}
return counter;
}
public Cell getCell(int rowIndex, int columnIndex)
{
ArrayList<Cell> row;
if(rowIndex<CellGridData.size())
row = CellGridData.get(rowIndex);
else return null;
Cell cell = null;
if(row!=null){
if(columnIndex<row.size())
cell = row.get(columnIndex);
}
return cell;
}
public Iterator<ArrayList<Cell>> getRowGridIterator(int StartRow) {
Iterator<ArrayList<Cell>> itRow = CellGridData.iterator();
int CurrentRow = 0;
while (itRow.hasNext()) {
// Itrate to Row
if (CurrentRow++ < StartRow)
itRow.next();
}
return itRow;
}
private void readDataFile(String asciiFile) throws IOException {
CellGridData = new ArrayList<ArrayList<Cell>>();
ArrayList<Cell> row;
FileInputStream fstream = new FileInputStream(asciiFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
String strLine;
// Read File Line By Line
int rowIndex = 0;
while ((strLine = br.readLine()) != null) {
CellGridData.add(row = new ArrayList<Cell>());
// System.out.println (strLine);
for (int colIndex = 0; colIndex < strLine.length(); colIndex++) {
row.add(new Cell(strLine.charAt(colIndex), this, rowIndex,colIndex));
// System.out.print(strLine.charAt(i));
}
rowIndex++;
// System.out.println();
}
// Close the input stream
br.close();
}
public String printGrid() {
String result = "";
Iterator<ArrayList<Cell>> itRow = CellGridData.iterator();
Iterator<Cell> itCol;
Cell cell;
while (itRow.hasNext()) {
itCol = itRow.next().iterator();
while (itCol.hasNext()) {
cell = itCol.next();
result += cell.Character;
}
result += "\n";
}
return result;
}
}
FoundBoxObject.java
package AsciiToDIVs;
import java.util.ArrayList;
public class FoundBoxObject extends FoundObject {
public ArrayList<FoundObject> containedObjects = new ArrayList<FoundObject>();
public static int boxCounter = 0;
public final int ID = boxCounter++;
public FoundBoxObject(int rowIndex, int colIndex, int width, int height, FoundBoxObject parent) {
super(rowIndex, colIndex, width, height);
if(parent!=null)
System.out.println("Created a box(" +
"ID="+ID+
",X="+rowIndex+
",Y="+colIndex+
",width="+width+
",height="+height+
",parent="+parent.ID+")");
else
System.out.println("Created a box(" +
"ID="+ID+
",X="+rowIndex+
",Y="+colIndex+
",width="+width+
",height="+height+
")");
}
}
FoundCharObject.java
package AsciiToDIVs;
public class FoundCharObject extends FoundObject {
private Character Char;
public FoundCharObject(int rowIndex, int colIndex,FoundBoxObject parent, char Char) {
super(rowIndex, colIndex, 1, 1);
if(parent!=null)
System.out.println("Created a char(" +
"Char="+Char+
",X="+rowIndex+
",Y="+colIndex+
",parent="+parent.ID+")");
else
System.out.println("Created a char(" +
",X="+rowIndex+
",Y="+colIndex+")");
this.Char = Char;
}
public Character getChar() {
return Char;
}
}
FoundObject.java
package AsciiToDIVs;
public class FoundObject {
private int rowIndex;
private int colIndex;
private int width = 0;
private int height = 0;
public FoundObject(int rowIndex, int colIndex, int width, int height )
{
this.rowIndex = rowIndex;
this.colIndex = colIndex;
this.width = width;
this.height = height;
}
public int getRowIndex() {
return rowIndex;
}
public int getColIndex() {
return colIndex;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
Main Method
public static void main(String args[])
{
try {
CellGrid grid = new CellGrid("ascii.txt");
System.out.println(CellGrid.printDIV(grid.findBoxes(null)));
//System.out.println(grid.printGrid());
} catch (IOException e) {
e.printStackTrace();
}
}
Update
The 'printDIV' should be like this (more '' were being printed than needed).
public static String printDIV(ArrayList<FoundObject> objects)
{
String result = "";
Iterator<FoundObject> it = objects.iterator();
FoundObject fo;
while(it.hasNext())
{
fo = it.next();
if(fo instanceof FoundCharObject)
{
FoundCharObject fc = (FoundCharObject)fo;
result+=fc.getChar();
}
if(fo instanceof FoundBoxObject)
{
result+="<div>";
FoundBoxObject fb = (FoundBoxObject)fo;
result+=printDIV(fb.containedObjects);
result+="</div>";
}
}
return result;
}
Here's a fairly simple solution in JavaScript, tested via Node. Of course, you'll need to adjust the input and output methods.
var s = "\n\
--------------------------------\n\
I I\n\
I ------- ------- I\n\
I I I I I I\n\
I I A I I B I I\n\
I I I I I I\n\
I ------- ------- I\n\
I I\n\
I I\n\
--------------------------------\n\
";
var lines = s.split('\n');
var outer_box_top_re = /--+/g;
var i;
for (i=0; i<lines.length; i++) {
while ((res = outer_box_top_re.exec(lines[i])) != null) {
L = res.index
R = outer_box_top_re.lastIndex
process_box(i, L, R)
}
}
function process_box(T, L, R) {
console.log('<div top="' + T + '" left="' + L + '" right="' + R + '">')
blank_out(T, L, R)
var i = T;
while (1) {
i += 1;
if (i >= lines.length) {
console.log('Fell off bottom of ascii-art without finding bottom of box');
process.exit(1);
}
var line = lines[i];
if (line[L] == 'I' && line[R-1] == 'I') {
// interior
// Look for (the tops of) sub-boxes.
// (between L+1 and R-2)
var inner_box_top_re = /--+/g;
// Inner and outer need to be separate so that
// inner doesn't stomp on outer's lastIndex.
inner_box_top_re.lastIndex = L+1;
while ((res = inner_box_top_re.exec(lines[i])) != null) {
sub_L = res.index;
sub_R = inner_box_top_re.lastIndex;
if (sub_L > R-1) { break; }
process_box(i, sub_L, sub_R);
}
// Look for any other content (i.e., a box label)
content = lines[i].substring(L+1, R-1);
if (content.search(/[^ ]/) != -1) {
console.log(content);
}
blank_out(i, L, R);
}
else if (line.substring(L,R).match(/^-+$/)) {
// bottom
blank_out(i, L, R);
break;
}
else {
console.log("line " + i + " doesn't contain a valid continuation of the box");
process.exit(1)
}
}
console.log('</div>')
}
function blank_out(i, L, R) {
lines[i] = (
lines[i].substring(0,L)
+ lines[i].substring(L,R).replace(/./g, ' ')
+ lines[i].substring(R)
);
}
What you want is the idea of 2-dimensional parsing, which detects 2D entities and verifies they have legitimate relationships.
See http://mmi.tudelft.nl/pub/siska/TSD%202DVisLangGrammar.pdf‎
What will be difficult is defining the sets of possible "ASCII Art" constraints.
Do only want to to recognize letters? Made only of the same-letter characters?
"cursive" letters? boxes? (Your example has boxes whose sides aren't made of the same
ASCII character). Boxes with arbitrary thick walls? Nested boxes? Diagrams with (thin/fat) arrows? Kilroy-was-here-nose-over-the-wall?
Pictures of Mona Lisa in which character pixels provide density relations?
What exactly do you mean by "ASCII art"?
The real problem is defining the range of things you intend to recognize. If
you limit that range, your odds of success go way up (see the referenced paper).
The problem here has little to to do specifically with Java or Javascript. This is far more related
to algorithms. Pick a limited class of art, choose the right algorithms, and then what you have is a coding problem which should be relatively easy to solve. No limits, no algorithms --> no amount of Javascript will save you.

Categories

Resources