<commit_before>package sudoku;

import common.BackedSet;
import common.Sets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import sudoku.time.TechniqueEvent;

/**
 * <p>The color-chain technique exploits the fact that a Rule with only two connected Claims is
 * analogous to a {@code xor} operation. A collection of interconnected two-Claim Rules, regardless
 * of the size and shape of such a network, has only two possible solution-states.</p>
 * @author fiveham
 */
public class ConsequenceIntersection{
	
  private final Sudoku target;
  
  /**
   * <p>Constructs a ColorChain that works to solve the specified {@code target}.</p>
   * @param target the Puzzle that this Technique works to solve.
   */
	public ConsequenceIntersection(Sudoku puzzle){
	  this.target = puzzle;
	}
	
	public ConsequenceIntersection apply(Sudoku sudoku){
		return new ConsequenceIntersection(sudoku);
	}
	
	protected TechniqueEvent process(){
		return implications();
	}
	
  /**
   * <p>Tries to find an overlap among the consequences of each of the Claims of a given Fact in
   * the puzzle hypothetically being true, starting from the smallest Facts in the puzzle and
   * increasing in Fact size from there.</p>
   * @return a TechniqueEvent describing the Fact whose Claims' consequences led to progress in
   * solving the puzzle and the Claims that were falsified in that step of progress, or
   * {@code null} if no progress was made
   */
	public TechniqueEvent implications(){
		return target.factStream()
				.sorted(Comparator.comparingInt(Fact::size))
				.map(this::implications)
				.filter(Objects::nonNull)
				.findFirst()
				.orElse(null);
	}
	
  /**
   * <p>Tries to find an overlap among the consequences of each of the Claims of {@code f} in the
   * puzzle hypothetically being true.</p>
   * @return a TechniqueEvent describing the Fact whose Claims' consequences led to progress in
   * solving the puzzle and the Claims that were falsified in that step of progress, or
   * {@code null} if no progress was made
   */
	private TechniqueEvent implications(Fact f){
		Set<Claim> con = new Logic(f).findConsequenceIntersection();
		return con.isEmpty() 
				? null
				: new SolveEventImplications(f, con).falsifyClaims();
	}
	
	public static class SolveEventImplications extends TechniqueEvent{
		
		private final Fact initFact;
		
		SolveEventImplications(Fact f, Set<Claim> falsifiedClaims){
			super(falsifiedClaims);
			this.initFact = f;
		}
		
		@Override
		protected String toStringStart() {
			return "Exploration of the consequences of the possible solutions of " + initFact;
		}
	}
	
	private class Logic {
		
		private final Puzzle puzzle;
		private Collection<WhatIf> whatIfs;
		
    /**
     * @param claims
     * @throws IllegalArgumentException if {@code claims} is empty.
     */
		public Logic(Set<? extends Claim> claims){
			try{
				this.puzzle = claims.iterator().next().getPuzzle();
			} catch(NoSuchElementException e){
				throw new IllegalArgumentException("Could not get any Claims from the specified set.");
			}
			
			whatIfs = claims.stream()
					.map(WhatIf::new)
					.collect(Collectors.toList());
			
			popularity = new HashMap<>();
		}
		
		public Set<Claim> findConsequenceIntersection(){
			Set<Claim> result;
			while((result = consequenceIntersection()).isEmpty() && isDepthAvailable()){
				exploreDepth();
			}
			return result;
		}
		
		public Set<Claim> consequenceIntersection(){
			return whatIfs.stream()
					.map(WhatIf::consequences)
					.collect(Sets.massIntersectionCollector());
		}
		
		public boolean isDepthAvailable(){
			return whatIfs.stream().anyMatch(WhatIf::isDepthAvailable);
		}
		
		public void exploreDepth(){
			populatePopularity();
			int sizeForExploration = sizeForExploration();
			whatIfs = whatIfs.stream()
					.map((wi) -> wi.hasExplorableReducedFact(sizeForExploration)
							? wi.exploreDepth() 
							: new HashSet<>(Arrays.asList(wi))) 
					.reduce((c1, c2) -> {
					  c1.addAll(c2);
					  return c1;
					})
					.get();
		}
		
		private int sizeForExploration(){
			return whatIfs.stream()
					.filter(WhatIf::isDepthAvailable)
					.mapToInt(WhatIf::minReducedFactSize)
					.reduce(Integer.MAX_VALUE, Integer::min);
		}
		
		/**
		 * <p>Populates this Logic's popularity Map.</p>
		 */
		private void populatePopularity(){
			popularity = Sets.countingUnion(whatIfs.stream()
					.map((wi) -> wi.reducedFacts()
							.collect(Collectors.toList())));
		}
		
		private Map<Fact,Integer> popularity;
		
		/**
		 * <p>Returns a comparator that compares ReducedFacts by the popularity of their Facts in this 
		 * Logic, putting the most popular ReducedFact first. Equivalent to 
		 * {@code Comparator.comparingInt(this::popularity).reversed()}.</p>
		 * @return a comparator that compares ReducedFacts by the popularity of their Facts in this 
     * Logic, putting the most popular ReducedFact first
		 */<commit_msg>document sizeForExploration<commit_after>package sudoku;

import common.BackedSet;
import common.Sets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import sudoku.time.TechniqueEvent;

/**
 * <p>The color-chain technique exploits the fact that a Rule with only two connected Claims is
 * analogous to a {@code xor} operation. A collection of interconnected two-Claim Rules, regardless
 * of the size and shape of such a network, has only two possible solution-states.</p>
 * @author fiveham
 */
public class ConsequenceIntersection{
	
  private final Sudoku target;
  
  /**
   * <p>Constructs a ColorChain that works to solve the specified {@code target}.</p>
   * @param target the Puzzle that this Technique works to solve.
   */
	public ConsequenceIntersection(Sudoku puzzle){
	  this.target = puzzle;
	}
	
	public ConsequenceIntersection apply(Sudoku sudoku){
		return new ConsequenceIntersection(sudoku);
	}
	
	protected TechniqueEvent process(){
		return implications();
	}
	
  /**
   * <p>Tries to find an overlap among the consequences of each of the Claims of a given Fact in
   * the puzzle hypothetically being true, starting from the smallest Facts in the puzzle and
   * increasing in Fact size from there.</p>
   * @return a TechniqueEvent describing the Fact whose Claims' consequences led to progress in
   * solving the puzzle and the Claims that were falsified in that step of progress, or
   * {@code null} if no progress was made
   */
	public TechniqueEvent implications(){
		return target.factStream()
				.sorted(Comparator.comparingInt(Fact::size))
				.map(this::implications)
				.filter(Objects::nonNull)
				.findFirst()
				.orElse(null);
	}
	
  /**
   * <p>Tries to find an overlap among the consequences of each of the Claims of {@code f} in the
   * puzzle hypothetically being true.</p>
   * @return a TechniqueEvent describing the Fact whose Claims' consequences led to progress in
   * solving the puzzle and the Claims that were falsified in that step of progress, or
   * {@code null} if no progress was made
   */
	private TechniqueEvent implications(Fact f){
		Set<Claim> con = new Logic(f).findConsequenceIntersection();
		return con.isEmpty() 
				? null
				: new SolveEventImplications(f, con).falsifyClaims();
	}
	
	public static class SolveEventImplications extends TechniqueEvent{
		
		private final Fact initFact;
		
		SolveEventImplications(Fact f, Set<Claim> falsifiedClaims){
			super(falsifiedClaims);
			this.initFact = f;
		}
		
		@Override
		protected String toStringStart() {
			return "Exploration of the consequences of the possible solutions of " + initFact;
		}
	}
	
	private class Logic {
		
		private final Puzzle puzzle;
		private Collection<WhatIf> whatIfs;
		
    /**
     * @param claims
     * @throws IllegalArgumentException if {@code claims} is empty.
     */
		public Logic(Set<? extends Claim> claims){
			try{
				this.puzzle = claims.iterator().next().getPuzzle();
			} catch(NoSuchElementException e){
				throw new IllegalArgumentException("Could not get any Claims from the specified set.");
			}
			
			whatIfs = claims.stream()
					.map(WhatIf::new)
					.collect(Collectors.toList());
			
			popularity = new HashMap<>();
		}
		
		public Set<Claim> findConsequenceIntersection(){
			Set<Claim> result;
			while((result = consequenceIntersection()).isEmpty() && isDepthAvailable()){
				exploreDepth();
			}
			return result;
		}
		
		public Set<Claim> consequenceIntersection(){
			return whatIfs.stream()
					.map(WhatIf::consequences)
					.collect(Sets.massIntersectionCollector());
		}
		
		public boolean isDepthAvailable(){
			return whatIfs.stream().anyMatch(WhatIf::isDepthAvailable);
		}
		
		public void exploreDepth(){
			populatePopularity();
			int sizeForExploration = sizeForExploration();
			whatIfs = whatIfs.stream()
					.map((wi) -> wi.hasExplorableReducedFact(sizeForExploration)
							? wi.exploreDepth() 
							: new HashSet<>(Arrays.asList(wi))) 
					.reduce((c1, c2) -> {
					  c1.addAll(c2);
					  return c1;
					})
					.get();
		}
		
		/**
		 * <p>Returns the maximum allowable size that a Fact's reduced counterpart can have in a WhatIf 
		 * if that reduced fact can be explored.</p>
		 * @return the maximum allowable size for an explorable reduced counterpart of a Fact
		 */
		private int sizeForExploration(){
			return whatIfs.stream()
					.filter(WhatIf::isDepthAvailable)
					.mapToInt(WhatIf::minReducedFactSize)
					.reduce(Integer.MAX_VALUE, Integer::min);
		}
		
		/**
		 * <p>Populates this Logic's popularity Map.</p>
		 */
		private void populatePopularity(){
			popularity = Sets.countingUnion(whatIfs.stream()
					.map((wi) -> wi.reducedFacts()
							.collect(Collectors.toList())));
		}
		
		private Map<Fact,Integer> popularity;
		
		/**
		 * <p>Returns a comparator that compares ReducedFacts by the popularity of their Facts in this 
		 * Logic, putting the most popular ReducedFact first. Equivalent to 
		 * {@code Comparator.comparingInt(this::popularity).reversed()}.</p>
		 * @return a comparator that compares ReducedFacts by the popularity of their Facts in this 
     * Logic, putting the most popular ReducedFact first
		 */
{'fraction_non_alphanumeric': 0.10202188833240586, 'fraction_numerical': 0.0009274717121127806, 'mean_word_length': 3.706678306416412, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 55, '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': '398108', 'n_tokens_mistral': 3687, 'n_tokens_neox': 3059, 'n_words': 976}