package dev.dump;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Collections;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by IntelliJ IDEA. User: User Date: 28.09.2007 Time: 9:46:47 To change this template use
* File | Settings | File Templates.
*/
class NumberDiapasone2RegExp {
private static final String invalidArgumentEmpty="Invalid argument \"{0}\" was found! {1}";
private static final Pattern pattern=Pattern.compile("^(\\d+)-(\\d+)?$");
private String src;
private String result="";
private Long left;
private Long right;
private boolean transform09ToD;
public NumberDiapasone2RegExp(final String src) {
this(src, false);
}
public NumberDiapasone2RegExp(final String src, final boolean transform09ToD) {
this.transform09ToD=transform09ToD;
if (src==null || src.trim().length()==0)
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It cannot be empty."));
if (src.indexOf("-")<0)
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It is supposed to have \"-\"."));
if (src.indexOf("-")!=src.lastIndexOf("-"))
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It is supposed to have only one \"-\"."));
Matcher syntaxChecker=pattern.matcher(src);
if (!syntaxChecker.find()){
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It is supposed to be in format \"##-##\"."));
}
this.src=src;
parseAndCheck();
String theSameDigits="";
//the same digit goes towards result
if (left.toString().length()==right.toString().length()){
for (int i=0; i<left.toString().length(); i++){
if (i<right.toString().length() &&
left.toString().charAt(i)==right.toString().charAt(i)){
theSameDigits+=left.toString().charAt(i);
}
}
if (theSameDigits.length()>0){
this.src=this.src.replaceFirst(Pattern.quote(theSameDigits),"");
this.src=this.src.replaceFirst(Pattern.quote("-"+theSameDigits),"-");
parseAndCheck();
}
}
result=glueParts(compact(transform09ToD, toParts()));
Matcher m=secondCompact.matcher(result);
while (m.find()){
result=m.group(1).replace("(","").replace(")","")+"["+m.group(2).replaceAll("[\\[\\]]","")+m.group(3).replaceAll("[\\[\\]]","")+"][0-9]";
m.reset(result);
}
//compact squares again
StringBuffer sb=new StringBuffer();
Pattern squresP=Pattern.compile("(\\[(\\d|-)+\\])");
m=squresP.matcher(result);
while (m.find()) {
m.appendReplacement(sb, Matcher.quoteReplacement(compactSquares(m.group(1))));
}
m.appendTail(sb);
result=sb.toString();
result=result.replaceAll("\\[(\\d)-\\1\\]","$1");
result=result.replaceAll("\\[(\\d)\\]","$1");
result=result.replace("{1}","").replace("{0,1}","?");
if (result.indexOf("|")>=0) result=theSameDigits+"("+result+")";
else result=theSameDigits+result;
if (result.startsWith("(") && result.endsWith(")")) result=result.substring(1, result.length()-1);
}
private static Pattern secondCompact=Pattern.compile("(.*)(\\[\\d-?\\d\\]|\\d)\\[0-9\\]\\|(\\[\\d-?\\d\\]|\\d)\\[0-9\\]");
static List<String> compact(boolean transform09ToD, String... parts) {
Set<String> unique=new HashSet<String>();
List<String> result=new ArrayList<String>();
for (String part : parts){
if (part==null || part.length()==0) continue;
part=compactSquares(part);
part=part.replaceAll("\\[(\\d)\\]","$1");
if (part.indexOf("[0-9]")>=0){
if (transform09ToD) part=part.replace("[0-9]","\\d");
}
//[0-3][0-9]|4[0-9]=>[0-34][0-9]
//[023][0-9]|4[0-9]=>[0234][0-9]
//[02345789]=>[02-57-9]
Matcher m=secondCompact.matcher(part);
if (m.find()){
part=m.group(1).replace("(","").replace(")","")+"["+m.group(2).replaceAll("[\\[\\]]","")+m.group(3).replaceAll("[\\[\\]]","")+"][0-9]";
}
part=part.replaceAll("\\[(\\d)-\\1\\]","$1");
if (unique.add(part)) result.add(part);
}
return result;
}
static String compactSquares(String src){
boolean inSquares=false;
if (src.startsWith("[") && src.endsWith("]")){
inSquares=true;
src=src.substring(1,src.length()-1);
}
StringBuffer sb=new StringBuffer();
if (!src.contains("-")) {
List<Integer> digits=new ArrayList<Integer>();
for (int i=0; i<src.length();i++){
digits.add(Integer.parseInt(""+src.charAt(i)));
}
Collections.sort(digits);
for (Integer s : digits){
sb.append(s);
}
src=sb.toString();
sb.setLength(0);
}
int firstChar = -2;
int lastChar = -2;
int currentChar;
for (int i=0; i<src.length(); i++) {
currentChar=src.charAt(i);
if (currentChar == lastChar + 1) {
lastChar = currentChar;
continue;
}
if (currentChar == '-' && i+1 < src.length()) {
lastChar = src.charAt(i + 1) - 1;
continue;
}
flush(sb, firstChar, lastChar);
firstChar = currentChar;
lastChar = currentChar;
}
flush(sb, firstChar, lastChar);
return (inSquares?"[":"")+sb.toString()+(inSquares?"]":"");
}
private static void flush(StringBuffer sb, int firstChar, int lastChar) {
if (lastChar<=0) return;
if (firstChar==lastChar) {
sb.append((char)firstChar);
return;
}
if (firstChar+1==lastChar){
sb.append((char)firstChar);
sb.append((char)lastChar);
return;
}
sb.append((char)firstChar);
sb.append('-');
sb.append((char)lastChar);
}
static String glueParts(List<String> parts) {
if (parts==null || parts.isEmpty()) return "";
if (parts.size()==1) return parts.get(0);
StringBuilder result=new StringBuilder(128);
for (String part : parts){
result.append(part);
result.append("|");
}
result.deleteCharAt(result.length()-1);
return result.toString();
}
private String[] toParts() {
List<String> result=new ArrayList<String>();
if (getNumberOfDigits(left)>2 || getNumberOfDigits(right)>2) {
result.add(startPart(left));
}
long leftPart=left;
long rightPart=right;
if (!String.valueOf(left).matches("10*")) leftPart=toPower(left);
if (!String.valueOf(right).matches("10*")) rightPart=toPower(right)/10;
if (rightPart/leftPart>=10) {
result.add(speedUpPart(left, right));
}
//for 1-2 digit process
if (getNumberOfDigits(left)==1 && getNumberOfDigits(right)==1){
result.add("["+left+"-"+right+"]");
}
else if (getNumberOfDigits(left)==1 && getNumberOfDigits(right)==2){
if (0==Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (1==Integer.parseInt(getMajorDigit(right))) {
result.add("["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (2<=Integer.parseInt(getMajorDigit(right))) {
result.add("["+
getMajorDigit(left)+
"-9]");
result.add("[1-"+
(Integer.parseInt(getMajorDigit(right))-1)+
"][0-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else throw new IllegalStateException();
}
else if (getNumberOfDigits(left)==2 && getNumberOfDigits(right)==2){
if (Integer.parseInt(getMajorDigit(left))==Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (Integer.parseInt(getMajorDigit(left))+1==Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (Integer.parseInt(getMajorDigit(left))+2<=Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-9]");
result.add("["+(Integer.parseInt(getMajorDigit(left))+1)+
"-"+(Integer.parseInt(getMajorDigit(right))-1)+
"][0-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else throw new IllegalStateException();
}
else result.add(staticPart(right));
result.add(breakPart(right));
return result.toArray(new String[0]);
}
static String breakPart(final Long number) {
if (getNumberOfDigits(number)<=2) {
return "";
}
StringBuilder result=new StringBuilder(256);
StringBuilder staticSection=new StringBuilder(32);
staticSection.append(getMajorDigit(number));
for (int i=1; i<getNumberOfDigits(number)-1; i++){
if (i!=1) result.append("|");
result.append(staticSection.toString());
staticSection.append(String.valueOf(number).charAt(i));
final long nextDigit=Long.parseLong(""+String.valueOf(number).charAt(i))-1;
if (nextDigit<0) {
result.setLength(0);
result.append("|");
continue;
}
if (nextDigit==0) result.append("0");
else if (nextDigit==1) result.append("[01]");
else result.append("[0-"+(nextDigit)+"]");
final int numberOfRepeats=(getNumberOfDigits(number)-i-1);
if (numberOfRepeats==1) result.append("[0-9]");
else result.append("[0-9]{"+numberOfRepeats+"}");
}
//остаток - 2 последние цифры числа
if (result.length()>0) {
result.append("|");
result.append(staticSection.toString());
//последнюю цифру от 0 до нее
result.append("[0-"+Long.parseLong(number.toString().replaceFirst("\\d+(\\d)","$1"))+"]");
}
if (result.length()>0) return result.toString().replace("||","|").replaceAll("^\\|","");
return "";
}
static String staticPart(final Long number) {
final long majorDigitMinus1=(Long.parseLong(getMajorDigit(number))-1);
if (majorDigitMinus1<=0) return "";
if (majorDigitMinus1==2) return "[1"+majorDigitMinus1+"][0-9]{"+(getNumberOfDigits(number)-1)+"}";
else if (majorDigitMinus1==1) return "1[0-9]{"+(getNumberOfDigits(number)-1)+"}";
return "[1-"+majorDigitMinus1+"][0-9]{"+(getNumberOfDigits(number)-1)+"}";
}
/**
* [1-9][0-9]{<X-1>,<Y-1>}, where X-number of digits of less number, Y-number of digits of greater number
*/
static String speedUpPart(Long left, Long right) {
//найти ближайшее до 0 то есть для 23 найти 100 для 777 найти 1000
//округленные до ближайшего 0
if (!String.valueOf(left).matches("10*")) left=toPower(left);
if (!String.valueOf(right).matches("10*")) right=toPower(right)/10;
final int leftPartRepeat=getNumberOfDigits(left)+(String.valueOf(left).matches("10*")?0:1)-1;
final int rightPartRepeat=getNumberOfDigits(right)+(String.valueOf(right).matches("10*")?0:1)-2;
if (getNumberOfDigits(left)==1 && getNumberOfDigits(right)==2)
return "[1-9]";
else if (leftPartRepeat>=rightPartRepeat)
return "[1-9][0-9]{"+rightPartRepeat+"}";
else
return "[1-9][0-9]{"+leftPartRepeat+","+rightPartRepeat+"}";
}
private static long toPower(final Long number) {
final double dValue=Math.pow(10, getNumberOfDigits(number));
final String value=String.format(Locale.US,"%24.0f",dValue);
return Long.parseLong(value.replaceFirst("\\s*(\\d+)(\\D\\d+)?","$1"));
}
private static int getNumberOfDigits(long number){
return (String.valueOf(number).length());
}
private static String getMajorDigit(long number){
return (String.valueOf(number).substring(0,1));
}
private static long getNumberWithoutMajorDigit(long number){
return Long.parseLong(String.valueOf(number).replaceFirst("\\d(\\d+)","$1"));
}
/**
* f(<n>>2)=<major digit>(f(<n-1>)|[<major digit+1>-9][0-9]{<n-1>})
*/
static String startPart(long number) {
int i=getNumberOfDigits(number);
if (i==1) {
if (number==9) return "9";
else if (number==8) return "[89]";
return "["+number+"-9]";
}
final long majorPlusOne=Long.parseLong(getMajorDigit(number))+1;
final int numberOfDigitsMinusOne=getNumberOfDigits(number)-1;
String result = (majorPlusOne < 10 ? "(" : "");
result+=getMajorDigit(number);
result+=startPart(getNumberWithoutMajorDigit(number));
result+=result.indexOf("|")<0 && majorPlusOne<10 && majorPlusOne!=numberOfDigitsMinusOne && numberOfDigitsMinusOne>1?"{"+numberOfDigitsMinusOne+"}":"";
result+=(majorPlusOne < 10
? "|[" + majorPlusOne + "-9][0-9]"+(numberOfDigitsMinusOne > 1 ? "{" + numberOfDigitsMinusOne + "}" : "")
: "");
result+=(majorPlusOne < 10 ? ")" : "");
return result;
}
private void parseAndCheck() {
Matcher matcher=pattern.matcher(src);
matcher.find();
try{
left=Long.parseLong(matcher.group(1));
right=Long.parseLong(matcher.group(2));
}
catch(Exception ex){
left=right+1;
}
if (left>right){
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"Left part must be less than right one."));
}
}
public String getPattern() {
return result;
}
public static void main(String[] args) {
System.err.println(new NumberDiapasone2RegExp(args[0]).getPattern());
}
}