package it.unibz.inf.ontop.si.dag;

/*
 * #%L
 * ontop-quest-owlapi
 * %%
 * Copyright (C) 2009 - 2014 Free University of Bozen-Bolzano
 * %%
 * 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.
 * #L%
 */


import com.google.common.collect.ImmutableList;
import it.unibz.inf.ontop.spec.ontology.ClassifiedTBox;
import it.unibz.inf.ontop.spec.ontology.owlapi.OWLAPITranslatorOWL2QL;
import junit.framework.TestCase;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Random;

import static it.unibz.inf.ontop.utils.SITestingTools.OWLAPI_TRANSLATOR;

public class DAGPerformanceTest extends TestCase {

	private final Logger log = LoggerFactory.getLogger(DAGPerformanceTest.class);

	private static final int size = 1000;
	private static final int maxdepth = 10;

	private static class LevelRange {
		final int min, max, width;

		public LevelRange(int min, int max) {
			this.min = min;
			this.max = max;
			this.width = max - min;
		}

		public String toString() {
			return "[" + min + "," + max + "]=" + width;
		}
	}

	/***
	 * Test the performance of classifying an ontology with 500 classes, 1000
	 * subclassAxioms and 2 roles
	 */
	public void testOnto15() throws Exception {
		OWLOntologyManager man = OWLManager.createOWLOntologyManager();
		OWLDataFactory fac = man.getOWLDataFactory();
		OWLOntology ont = man.createOntology(IRI.create("http://www.obda.org/inf/obda/quest/core/dag/test.owl"));

		log.debug("Generating classes");
		String base = "http://www.obda.org/inf/obda/quest/core/dag/test.owl#";
		OWLClass[] classes = new OWLClass[size];
		for (int i = 0; i < size; i++) {
			OWLClass c = fac.getOWLClass(IRI.create(base + "class" + i));
			classes[i] = c;
			man.addAxiom(ont, fac.getOWLDeclarationAxiom(c));
		}
		log.debug("Generating axioms");
		LevelRange[] ranges = new LevelRange[10];
		for (int depth = 0; depth < maxdepth; depth++) {
			int width = (int) ((Math.log10(depth + 1)) * size);
			LevelRange r = (depth == 0)
				? new LevelRange(0, width)
				: new LevelRange(ranges[depth - 1].max, width);
			ranges[depth] = r;
		}
		LevelRange[] corrected = new LevelRange[9];
		for (int depth = 0; depth < 9; depth++) {
			LevelRange r2 = (depth == 0)
				? new LevelRange(0, ranges[9 - depth].width)
				: new LevelRange(corrected[depth - 1].max, (corrected[depth - 1].max) + ranges[9 - depth].width);
			corrected[depth] = r2;
		}
		log.info("Creating axioms");
		Random r = new Random(10);
		for (int i = 1; i < 9; i++) {
			LevelRange parentRange = corrected[i - 1];
			LevelRange currentRange = corrected[i];
			for (int axiomindex = 0; axiomindex < (currentRange.width*1.5); axiomindex++) {
				int rand1 = r.nextInt(parentRange.width);
				int rand2 = r.nextInt(currentRange.width);
				int parent = parentRange.min + rand1;
				int child = currentRange.min + rand2;
				
				OWLClass c1 = classes[parent];
				OWLClass c2 = classes[child];
				OWLAxiom subc = fac.getOWLSubClassOfAxiom(c1, c2);
				man.addAxiom(ont, subc);
			}
		}
		
    	Random r2 = new Random(10);
    	for (int i =0; i<((ont.getAxiomCount()-size) *0.05);i++){
    		int rand1 = r2.nextInt(size);
			int rand2 = r2.nextInt(size);
			OWLClass c1 = classes[rand1];
			OWLClass c2 = classes[rand2];
			OWLAxiom subc = fac.getOWLSubClassOfAxiom(c2, c1);
			man.addAxiom(ont, subc);
    	}


		log.debug("Translating into quest API and creating a DAG out of it");
		long start = System.nanoTime();
		ClassifiedTBox reasoner = OWLAPI_TRANSLATOR.translateAndClassify(ont).tbox();
		log.debug("{}s", ((System.nanoTime() - start)/1000000));

//		long start = System.nanoTime();
//		log.debug("Optimizing Equivalences");
//		EquivalenceTBoxOptimizer equiOptimizer = new EquivalenceTBoxOptimizer(o);
//		equiOptimizer.optimize();
//		
//		Map<Predicate, Description> equi = equiOptimizer.getEquivalenceMap();
//		log.debug("Equivalences: {}", equi.size());
//		log.debug("{}s", ((System.nanoTime() - start)/1000000));
//		log.debug("Done.");
//		System.out.println(ont);
//		System.out.println(equiOptimizer.getOptimalTBox());
	}
	
	/**
	 * Test the performance of classifying an ontology with 500 classes, 1000
	 * subclassAxioms and 2 roles
	 */
	public void testOnto17() throws Exception {
		OWLOntologyManager man = OWLManager.createOWLOntologyManager();
		OWLDataFactory fac = man.getOWLDataFactory();
		OWLOntology ont = man.createOntology(IRI.create("http://www.obda.org/inf/obda/quest/core/dag/test.owl"));

		log.debug("Generating classes");
		String base = "http://www.obda.org/inf/obda/quest/core/dag/test.owl#";
		OWLClass[] classes = new OWLClass[size];
		for (int i = 0; i < size; i++) {
			OWLClass c = fac.getOWLClass(IRI.create(base + "class" + i));
			classes[i] = c;
			man.addAxiom(ont, fac.getOWLDeclarationAxiom(c));
		}

		log.debug("Generating axioms");
		LevelRange[] ranges = new LevelRange[10];
		for (int depth = 0; depth < maxdepth; depth++) {
			int width = (int) ((Math.log10(depth + 1)) * size);
			LevelRange r = (depth == 0)
				? new LevelRange(0, width)
				: new LevelRange(ranges[depth - 1].max, width);
			ranges[depth] = r;
		}
		LevelRange[] corrected = new LevelRange[9];
		for (int depth = 0; depth < 9; depth++) {
			LevelRange r2 = (depth == 0)
				? new LevelRange(0, ranges[9 - depth].width)
				: new LevelRange(corrected[depth - 1].max, (corrected[depth - 1].max) + ranges[9 - depth].width);
			corrected[depth] = r2;
		}
		log.info("Creating axioms");
		Random r = new Random(10);
		for (int i = 1; i < 9; i++) {
			LevelRange parentRange = corrected[i - 1];
			LevelRange currentRange = corrected[i];
			for (int axiomindex = 0; axiomindex < (currentRange.width*1.7); axiomindex++) {
				int rand1 = r.nextInt(parentRange.width);
				int rand2 = r.nextInt(currentRange.width);
				int parent = parentRange.min + rand1;
				int child = currentRange.min + rand2;
				
				OWLClass c1 = classes[parent];
				OWLClass c2 = classes[child];
				OWLAxiom subc = fac.getOWLSubClassOfAxiom(c1, c2);
				man.addAxiom(ont, subc);
			}
		}
		
    	Random r2 = new Random(10);
    	for (int i =0; i<((ont.getAxiomCount()-size) *0.05);i++){
    		int rand1 = r2.nextInt(size);
			int rand2 = r2.nextInt(size);
			OWLClass c1 = classes[rand1];
			OWLClass c2 = classes[rand2];
			OWLAxiom subc = fac.getOWLSubClassOfAxiom(c2, c1);
			man.addAxiom(ont, subc);
    		
    	}

		log.debug("Translating into quest API and creating a DAG out of it");
		long start = System.nanoTime();
		ClassifiedTBox reasoner = OWLAPI_TRANSLATOR.translateAndClassify(ont).tbox();
		log.debug("{}s", ((System.nanoTime() - start)/1000000));

//		long start = System.nanoTime();
//		log.debug("Optimizing Equivalences");
//		EquivalenceTBoxOptimizer equiOptimizer = new EquivalenceTBoxOptimizer(o);
//		equiOptimizer.optimize();
//
//		Map<Predicate, Description> equi = equiOptimizer.getEquivalenceMap();
//		log.debug("Equivalences: {}", equi.size());
//		log.debug("{}s", ((System.nanoTime() - start)/1000000));
//		log.debug("Done.");
//		System.out.println(ont);
//		System.out.println(equiOptimizer.getOptimalTBox());
	}
	
	/**
	 * Test the performance of classifying an ontology with 500 classes, 1000
	 * subclassAxioms and 2 roles
	 */
	public void testOnto19() throws Exception {
		OWLOntologyManager man = OWLManager.createOWLOntologyManager();
		OWLDataFactory fac = man.getOWLDataFactory();
		OWLOntology ont = man.createOntology(IRI.create("http://www.obda.org/inf/obda/quest/core/dag/test.owl"));

		log.debug("Generating classes");
		String base = "http://www.obda.org/inf/obda/quest/core/dag/test.owl#";
		OWLClass[] classes = new OWLClass[size];
		for (int i = 0; i < size; i++) {
			OWLClass c = fac.getOWLClass(IRI.create(base + "class" + i));
			classes[i] = c;
			man.addAxiom(ont, fac.getOWLDeclarationAxiom(c));
		}

		log.debug("Generating axioms");
		LevelRange[] ranges = new LevelRange[10];
		for (int depth = 0; depth < maxdepth; depth++) {
			int width = (int) ((Math.log10(depth + 1)) * size);
			LevelRange r = (depth == 0)
				? new LevelRange(0, width)
				: new LevelRange(ranges[depth - 1].max, width);
			ranges[depth] = r;
		}
		LevelRange[] corrected = new LevelRange[9];
		for (int depth = 0; depth < 9; depth++) {
			LevelRange r2 = (depth == 0)
				? new LevelRange(0, ranges[9 - depth].width)
				: new LevelRange(corrected[depth - 1].max, (corrected[depth - 1].max) + ranges[9 - depth].width);
			corrected[depth] = r2;
		}

		log.info("Creating axioms");
		Random r = new Random(10);
		for (int i = 1; i < 9; i++) {
			LevelRange parentRange = corrected[i - 1];
			LevelRange currentRange = corrected[i];
			for (int axiomindex = 0; axiomindex < (currentRange.width*1.9); axiomindex++) {
				int rand1 = r.nextInt(parentRange.width);
				int rand2 = r.nextInt(currentRange.width);
				int parent = parentRange.min + rand1;
				int child = currentRange.min + rand2;
				
				OWLClass c1 = classes[parent];
				OWLClass c2 = classes[child];
				OWLAxiom subc = fac.getOWLSubClassOfAxiom(c1, c2);
				man.addAxiom(ont, subc);
			}
		}
		
    	Random r2 = new Random(10);
    	for (int i =0; i<((ont.getAxiomCount()-size) *0.05);i++){
    		int rand1 = r2.nextInt(size);
			int rand2 = r2.nextInt(size);
			OWLClass c1 = classes[rand1];
			OWLClass c2 = classes[rand2];
			OWLAxiom subc = fac.getOWLSubClassOfAxiom(c2, c1);
			man.addAxiom(ont, subc);
    	}
		

		log.debug("Translating into quest API and creating a DAG out of it");
		long start = System.nanoTime();
		ClassifiedTBox reasoner = OWLAPI_TRANSLATOR.translateAndClassify(ont).tbox();
		log.debug("{}s", ((System.nanoTime() - start)/1000000));

//		long start = System.nanoTime();
//		log.debug("Optimizing Equivalences");
//		EquivalenceTBoxOptimizer equiOptimizer = new EquivalenceTBoxOptimizer(o);
//		equiOptimizer.optimize();
//		
//		Map<Predicate, Description> equi = equiOptimizer.getEquivalenceMap();
//		log.debug("Equivalences: {}", equi.size());
//		log.debug("{}s", ((System.nanoTime() - start)/1000000));
//		log.debug("Done.");
//		System.out.println(ont);
//		System.out.println(equiOptimizer.getOptimalTBox());
	}	
}
