<commit_before>package ar.rules;

import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import ar.Aggregates;
import ar.Glyph;
import ar.Renderer;
import ar.Transfer;
import ar.glyphsets.SimpleGlyph;
import ar.renderers.ParallelRenderer;
import ar.util.Util;
import ar.aggregates.Iterator2D;

//Base algorithm: http://en.wikipedia.org/wiki/Marching_squares 
//Another implementation that does sub-cell interpolation for smoother contours: http://udel.edu/~mm/code/marchingSquares

//TODO: have 'at' return the iso-contour-value at the location.  Need to resolve inside/outsideness
//TODO: Better ISO contour picking (round numbers?)
public interface ISOContours<N> {
	static final Renderer RENDERER = new ParallelRenderer();

	/**List of contours from smallest value to largest value.**/
	public List<? extends Glyph<Shape, N>> contours();

	/**Produce a set of ISO contours that are spaced at the given interval.**/
	public static class SpacedContours<N extends Number> implements Transfer<N,N> {
		final N empty;
		final double spacing;
		final N floor;
		
		/**@param spacing How far apart to place contours
		 * @param floor Lowest contour value (if omitted, will be the min value in the input)
		 */
		public SpacedContours(N empty, double spacing, N floor) {
			this.empty = empty;
			this.spacing = spacing;
			this.floor = floor;
		}
		
		public N emptyValue() {return empty;}

		@Override
		public ar.Transfer.Specialized<N, N> specialize(Aggregates<? extends N> aggregates) {
			return new Specialized<>(empty, spacing, floor, aggregates);
		}
		
		public static final class Specialized<N extends Number> extends SpacedContours<N> implements ISOContours<N>, Transfer.Specialized<N, N> {
			List<Glyph<Shape, N>> contours;
			
			public Specialized(N empty, double spacing, N floor, Aggregates<? extends N> aggregates) {
				super(empty, spacing, floor);
				Util.Stats<N> stats = Util.stats(aggregates, false);
				contours = new ArrayList<>();
				
				int i=0;
				N threshold;
				N bottom = floor == null ? stats.min : floor;
				do {
					threshold = LocalUtils.addTo(bottom, i*spacing);
					ISOContours<N> t = new Single.Specialized<>(empty, threshold, aggregates);
					this.contours.addAll(t.contours());
					i++;
				} while (threshold.doubleValue() < stats.max.doubleValue());		
			}
			public List<? extends Glyph<Shape, N>> contours() {return contours;}

			public N at(int x, int y, Aggregates<? extends N> aggregates) {return LocalUtils.search(contours, x,y, empty);}
		}
		
	}

	/**Produce N contours, evenly spaced between max and min.**/
	public static class NContours<N extends Number> implements Transfer<N,N> {
		final N empty;
		final int n;
		public NContours(N empty, int n) {
			this.empty = empty;
			this.n = n;
		}
		public N emptyValue() {return empty;}

		@Override
		public ar.Transfer.Specialized<N, N> specialize(Aggregates<? extends N> aggregates) {return new NContours.Specialized<>(empty, n, aggregates);}
		
		public static final class Specialized<N extends Number> extends NContours<N> implements ISOContours<N>, Transfer.Specialized<N, N> {
			List<Glyph<Shape, N>> contours;
			
			public Specialized(N empty, int n, Aggregates<? extends N> aggregates) {
				super(empty, n);
				Util.Stats<N> stats = Util.stats(aggregates, false);
				contours = new ArrayList<>();
				
				double step = (stats.max.doubleValue()-stats.min.doubleValue())/n;
				for (int i=0;i<n;i++) {
					N threshold = LocalUtils.addTo(stats.min, (step*i));
					ISOContours<N> t = new Single.Specialized<>(empty, threshold, aggregates);
					this.contours.addAll(t.contours());
				}				
			}

			public N at(int x, int y, Aggregates<? extends N> aggregates) {return LocalUtils.search(contours, x,y, empty);}
			public List<? extends Glyph<Shape, N>> contours() {return contours;}
		}
	}
	
	/**Produce a single ISO contour at the given division point.**/
	public static class Single<N extends Number> implements Transfer<N, N> {
		protected final N threshold;
		protected final N empty;

		public Single(N empty, N threshold) {
			this.threshold = threshold;
			this.empty = empty;
		}

		public N emptyValue() {return empty;}
		public Transfer.Specialized<N, N> specialize(Aggregates<? extends N> aggregates) {
			return new Specialized<>(empty, threshold, aggregates);
		}


		public static final class Specialized<N extends Number> extends Single<N> implements Transfer.Specialized<N,N>, ISOContours<N> { 
			private final List<? extends Glyph<Shape, N>> contours;

			public Specialized(N empty, N threshold, Aggregates<? extends N> aggregates) {
				super(empty, threshold);
				Aggregates<? extends N> padAggs = new PadAggregates<>(aggregates, empty);  

				Aggregates<Boolean> isoDivided = RENDERER.transfer(padAggs, new ISOBelow<>(threshold));
				Aggregates<MC_TYPE> classified = RENDERER.transfer(isoDivided, new MCClassifier());
				Shape s = Assembler.assembleContours(classified, isoDivided);
				contours = Arrays.asList(new SimpleGlyph<>(s, LocalUtils.minIncr(threshold)));
			}
			public List<? extends Glyph<Shape, N>> contours() {return contours;}

			public N at(int x, int y, Aggregates<? extends N> aggregates) {return LocalUtils.search(contours, x,y, empty);}
		}
	}
	
	public static class LocalUtils {
		@SuppressWarnings("unchecked")
		public static <N extends Number> N minIncr(N val) {
			if (val instanceof Double) {return (N) new Double(((Double) val).doubleValue()+Double.MIN_VALUE);}
			if (val instanceof Float) {return (N) new Float(((Float) val).floatValue()+Float.MIN_VALUE);}
			if (val instanceof Integer) {return (N) new Integer((int) (((Integer) val).intValue()+1));}
			if (val instanceof Long) {return (N) new Long((long) (((Long) val).longValue()+1));}
			if (val instanceof Short) {return (N) new Short((short) (((Short) val).shortValue()+1));}
			throw new IllegalArgumentException("Cannot increment " + val.getClass().getName());
		}
		
		@SuppressWarnings("unchecked")
		public static <N extends Number> N addTo(N val, double more) {
			if (val instanceof Double) {return (N) new Double(((Double) val).doubleValue()+more);}
			if (val instanceof Float) {return (N) new Float(((Float) val).floatValue()+more);}
			if (val instanceof Integer) {return (N) new Integer((int) (((Integer) val).intValue()+more));}
			if (val instanceof Long) {return (N) new Long((long) (((Long) val).longValue()+more));}
			if (val instanceof Short) {return (N) new Short((short) (((Short) val).shortValue()+more));}
			throw new IllegalArgumentException("Cannot add to " + val.getClass().getName());
		}
		
		
		private static ThreadLocal<Point> point = new ThreadLocal<Point>() {
			protected Point initialValue() {return new Point();}
		};
		
		/**Search a list of contours, return the highest-indexed contour that contains the given point.
		 * If no match, return empty.
		 * 
		 * TODO: This runs SUPER slow (2min at 800x800).  Should fix that. 
		 */
		public static <N> N search(List<? extends Glyph<Shape,N>> contours, int x, int y, N empty) {
			Point p = point.get();
			p.setLocation(x,y);
			for (int i=contours.size()-1; i>=0;i--) {
				Glyph<Shape, N> g = contours.get(i); 
				if (g.shape().contains(p)) {return g.info();}
			}
			return empty;
		}
	}

	
	public static final class Assembler {
		/** Build a single path from all of the contour parts.  
		 * 
		 * May be disjoint and have holes (thus GeneralPath).
		 * 
		 * @param classified The line-segment classifiers
		 * @param isoDivided Original classification, used to disambiguate saddle conditions
		 * @return
		 */
		public static final GeneralPath assembleContours(Aggregates<MC_TYPE> classified, Aggregates<Boolean> isoDivided) {
			GeneralPath isoPath = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
	
			//Find an unambiguous case of an actual line, follow it around and build the line.  
			//Stitching sets the line segments that have been "consumed" to MC_TYPE.empty, so segments are only processed once.
			for (int x = classified.lowX(); x < classified.highX(); x++) {
				for (int y = classified.lowY(); y < classified.highY(); y++) {<commit_msg>Simplified search...still not fast enough.<commit_after>package ar.rules;

import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import ar.Aggregates;
import ar.Glyph;
import ar.Renderer;
import ar.Transfer;
import ar.glyphsets.SimpleGlyph;
import ar.renderers.ParallelRenderer;
import ar.util.Util;
import ar.aggregates.Iterator2D;

//Base algorithm: http://en.wikipedia.org/wiki/Marching_squares 
//Another implementation that does sub-cell interpolation for smoother contours: http://udel.edu/~mm/code/marchingSquares

//TODO: have 'at' return the iso-contour-value at the location.  Need to resolve inside/outsideness
//TODO: Better ISO contour picking (round numbers?)
public interface ISOContours<N> {
	static final Renderer RENDERER = new ParallelRenderer();

	/**List of contours from smallest value to largest value.**/
	public List<? extends Glyph<Shape, N>> contours();

	/**Produce a set of ISO contours that are spaced at the given interval.**/
	public static class SpacedContours<N extends Number> implements Transfer<N,N> {
		final N empty;
		final double spacing;
		final N floor;
		
		/**@param spacing How far apart to place contours
		 * @param floor Lowest contour value (if omitted, will be the min value in the input)
		 */
		public SpacedContours(N empty, double spacing, N floor) {
			this.empty = empty;
			this.spacing = spacing;
			this.floor = floor;
		}
		
		public N emptyValue() {return empty;}

		@Override
		public ar.Transfer.Specialized<N, N> specialize(Aggregates<? extends N> aggregates) {
			return new Specialized<>(empty, spacing, floor, aggregates);
		}
		
		public static final class Specialized<N extends Number> extends SpacedContours<N> implements ISOContours<N>, Transfer.Specialized<N, N> {
			List<Glyph<Shape, N>> contours;
			
			public Specialized(N empty, double spacing, N floor, Aggregates<? extends N> aggregates) {
				super(empty, spacing, floor);
				Util.Stats<N> stats = Util.stats(aggregates, false);
				contours = new ArrayList<>();
				
				int i=0;
				N threshold;
				N bottom = floor == null ? stats.min : floor;
				do {
					threshold = LocalUtils.addTo(bottom, i*spacing);
					ISOContours<N> t = new Single.Specialized<>(empty, threshold, aggregates);
					this.contours.addAll(t.contours());
					i++;
				} while (threshold.doubleValue() < stats.max.doubleValue());		
			}
			public List<? extends Glyph<Shape, N>> contours() {return contours;}

			public N at(int x, int y, Aggregates<? extends N> aggregates) {return LocalUtils.search(contours, x,y, empty);}
		}
		
	}

	/**Produce N contours, evenly spaced between max and min.**/
	public static class NContours<N extends Number> implements Transfer<N,N> {
		final N empty;
		final int n;
		public NContours(N empty, int n) {
			this.empty = empty;
			this.n = n;
		}
		public N emptyValue() {return empty;}

		@Override
		public ar.Transfer.Specialized<N, N> specialize(Aggregates<? extends N> aggregates) {return new NContours.Specialized<>(empty, n, aggregates);}
		
		public static final class Specialized<N extends Number> extends NContours<N> implements ISOContours<N>, Transfer.Specialized<N, N> {
			List<Glyph<Shape, N>> contours;
			
			public Specialized(N empty, int n, Aggregates<? extends N> aggregates) {
				super(empty, n);
				Util.Stats<N> stats = Util.stats(aggregates, false);
				contours = new ArrayList<>();
				
				double step = (stats.max.doubleValue()-stats.min.doubleValue())/n;
				for (int i=0;i<n;i++) {
					N threshold = LocalUtils.addTo(stats.min, (step*i));
					ISOContours<N> t = new Single.Specialized<>(empty, threshold, aggregates);
					this.contours.addAll(t.contours());
				}				
			}

			public N at(int x, int y, Aggregates<? extends N> aggregates) {return LocalUtils.search(contours, x,y, empty);}
			public List<? extends Glyph<Shape, N>> contours() {return contours;}
		}
	}
	
	/**Produce a single ISO contour at the given division point.**/
	public static class Single<N extends Number> implements Transfer<N, N> {
		protected final N threshold;
		protected final N empty;

		public Single(N empty, N threshold) {
			this.threshold = threshold;
			this.empty = empty;
		}

		public N emptyValue() {return empty;}
		public Transfer.Specialized<N, N> specialize(Aggregates<? extends N> aggregates) {
			return new Specialized<>(empty, threshold, aggregates);
		}


		public static final class Specialized<N extends Number> extends Single<N> implements Transfer.Specialized<N,N>, ISOContours<N> { 
			private final List<? extends Glyph<Shape, N>> contours;

			public Specialized(N empty, N threshold, Aggregates<? extends N> aggregates) {
				super(empty, threshold);
				Aggregates<? extends N> padAggs = new PadAggregates<>(aggregates, empty);  

				Aggregates<Boolean> isoDivided = RENDERER.transfer(padAggs, new ISOBelow<>(threshold));
				Aggregates<MC_TYPE> classified = RENDERER.transfer(isoDivided, new MCClassifier());
				Shape s = Assembler.assembleContours(classified, isoDivided);
				contours = Arrays.asList(new SimpleGlyph<>(s, LocalUtils.minIncr(threshold)));
			}
			public List<? extends Glyph<Shape, N>> contours() {return contours;}

			public N at(int x, int y, Aggregates<? extends N> aggregates) {return LocalUtils.search(contours, x,y, empty);}
		}
	}
	
	public static class LocalUtils {
		@SuppressWarnings("unchecked")
		public static <N extends Number> N minIncr(N val) {
			if (val instanceof Double) {return (N) new Double(((Double) val).doubleValue()+Double.MIN_VALUE);}
			if (val instanceof Float) {return (N) new Float(((Float) val).floatValue()+Float.MIN_VALUE);}
			if (val instanceof Integer) {return (N) new Integer((int) (((Integer) val).intValue()+1));}
			if (val instanceof Long) {return (N) new Long((long) (((Long) val).longValue()+1));}
			if (val instanceof Short) {return (N) new Short((short) (((Short) val).shortValue()+1));}
			throw new IllegalArgumentException("Cannot increment " + val.getClass().getName());
		}
		
		@SuppressWarnings("unchecked")
		public static <N extends Number> N addTo(N val, double more) {
			if (val instanceof Double) {return (N) new Double(((Double) val).doubleValue()+more);}
			if (val instanceof Float) {return (N) new Float(((Float) val).floatValue()+more);}
			if (val instanceof Integer) {return (N) new Integer((int) (((Integer) val).intValue()+more));}
			if (val instanceof Long) {return (N) new Long((long) (((Long) val).longValue()+more));}
			if (val instanceof Short) {return (N) new Short((short) (((Short) val).shortValue()+more));}
			throw new IllegalArgumentException("Cannot add to " + val.getClass().getName());
		}
		
		/**Search a list of contours, return the highest-indexed contour that contains the given point.
		 * If no match, return empty.
		 * 
		 * TODO: This runs SUPER slow (2min at 800x800).  Should fix that...probably by doing an aggregation into a cached aggregate set on first access 
		 */
		public static <N> N search(List<? extends Glyph<Shape,N>> contours, int x, int y, N empty) {
			for (int i=contours.size()-1; i>=0;i--) {
				Glyph<Shape, N> g = contours.get(i);
				if (g.shape().contains(x,y)) {return g.info();}
			}
			return empty;
		}
	}

	
	public static final class Assembler {
		/** Build a single path from all of the contour parts.  
		 * 
		 * May be disjoint and have holes (thus GeneralPath).
		 * 
		 * @param classified The line-segment classifiers
		 * @param isoDivided Original classification, used to disambiguate saddle conditions
		 * @return
		 */
		public static final GeneralPath assembleContours(Aggregates<MC_TYPE> classified, Aggregates<Boolean> isoDivided) {
			GeneralPath isoPath = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
	
			//Find an unambiguous case of an actual line, follow it around and build the line.  
			//Stitching sets the line segments that have been "consumed" to MC_TYPE.empty, so segments are only processed once.
			for (int x = classified.lowX(); x < classified.highX(); x++) {
				for (int y = classified.lowY(); y < classified.highY(); y++) {
{'fraction_non_alphanumeric': 0.12893272023233301, 'fraction_numerical': 0.001936108422071636, 'mean_word_length': 4.222432859399684, 'pattern_counts': {'":': 0, '<': 153, '<?xml version=': 0, '>': 147, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7131664', 'n_tokens_mistral': 5738, 'n_tokens_neox': 4993, 'n_words': 1638}