#! /usr/bin/perl.exe ############################################################################### ############################################################################### # # Patterned dates finder. # # by Andrew Hardwick # ############################################################################### ############################################################################### # Version 1, 2006/4/4 # Outputs date representations for visual checking. # Supports ISO, USA & UK orders, 4 & 2 digit years, leading 0 keep or discard. # Version 2, 2006/4/5 # Added leap year support. # Added removal of duplicates (where different representation look equal). # Version 3, 2006/4/6 # Added searching for palindromes. # Added searching for 180 rotational symmetry ambigrams = diad symmetry. # Debugged removal of duplicates (was removing some non-duplicates too). # Added searching for mirror symmetry. # Added searching for cyclic sysmmetry. # Version 4, 2006/4/7 # Added searching for arithmetic series. # Added searching for geometric series. # Version 5, 2006/4/7 # Added searching for string patterns common across all same size substrings. # Version 6, 2007/4/11 # Source code cosmetic tidying (reduced all lines to <=80 characters). # Changed to take year range as command line parameters instead of hardcoded. ############################################################################### # How to use: # Set the start & end years in the settings in this file the run the program. # It will output dates in which it finds patterns. ############################################################################### # This file is formated for 80 character (+linebreak) rows & 4 character tabs. ############################################################################### ############################################################################### # Turn off automatic variables use strict; ############################################################################### # Main routine ############################################################################### { # Get parameters unless(@ARGV) { die("Give a year or start and end years as parameters.\n");} my $YearStart=$ARGV[0]; my $YearEnd=$ARGV[1]||$YearStart; # Iterate over years for(my $Year=$YearStart;$Year<=$YearEnd;$Year++) { # Iterate over months for(my $Month=1;$Month<=12;$Month++) { # Iterate over days my $MonthLength= ($Month==9||$Month==4||$Month==6||$Month==11)?30:31; if($Month==2) { # Leap year processing $MonthLength=($Year%400==0||$Year%100!=0&&$Year%4==0)?28:27;} for(my $Day=1;$Day<=$MonthLength;$Day++) { # Iterate over alternative date representations my @Dates=DateFormats($Year,$Month,$Day); foreach my $Date (@Dates) { # Search for whole string patterns my @Patterns=FindPatternsInString($Date); # Search for patterns in regular size chunks for(my $Size=1;$Size<=length($Date)/2;$Size++) { if(length($Date)%$Size==0) { my @Chunks=$Date=~/.{$Size}/g; # Search for number series my @ChunkPatterns=FindSeriesInNumberList(@Chunks); # Search for repeated patterns (not single digit) if($Size>1) { push(@ChunkPatterns, FindPatternsInStringList(@Chunks));} # Add the chunk patterns to the pattern list foreach my $ChunkPattern (@ChunkPatterns) { push(@Patterns,join(':',@Chunks). " = ".$ChunkPattern);}}} # Output patterns found (if any) foreach my $Pattern (@Patterns) { print "$Year/$Month/$Day: $Date = $Pattern\n";}}}}}} ############################################################################### # Create dates of different formats ############################################################################### # Formats a dates as ISO, UK & USA formats with years in 2 & 4 digit formats # and leading 0s kept & removed in <=2 digit fields. Field separators # are not included. It does not check for invalid dates or cope with # with years BC. ############################################################################### # Parameter 0: # Year as number. # Parameter 1: # Month as number. # Parameter 2: # Day as number. # Returns: # Array of the different formats as strings. ############################################################################### sub DateFormats { my($Year,$Month,$Day)=@_[0...2]; my @Dates; # 4 digit year my @Year; $Year="000$Year"; push(@Year,substr($Year,-4)); # 2 digit year push(@Year,substr($Year,-2)); # 1 digit year if(substr($Year,-2,1)eq'0') { push(@Year,substr($Year,-1));} # 2 digit month my @Month; push(@Month,substr("0$Month",-2)); # 1 digit month if($Month<10) { push(@Month,$Month);} # 2 digit day my @Day; push(@Day,substr("0$Day",-2)); # 1 digit day if($Day<10) { push(@Day,$Day);} # Assemble all formats foreach my $Year (@Year) { foreach my $Month (@Month) { foreach my $Day (@Day) { # ISO push(@Dates,"$Year$Month$Day"); # UK push(@Dates,"$Day$Month$Year"); # USA push(@Dates,"$Month$Day$Year");}}} # Remove duplicates @Dates=sort(@Dates); for(my $c=1;$c<@Dates;$c++) { if($Dates[$c]eq$Dates[$c-1]) { splice(@Dates,$c--,1);}} return @Dates;} ############################################################################### # Find text patterns in a string of digits ############################################################################### # Searches for text based patterns strings of digits and returns # names/descriptions of any it finds. ############################################################################### # Parameter 0: # A string of digits. # Returns: # Array of names/descriptions of the patterns found. ############################################################################### sub FindPatternsInString { my $String=$_[0]; my(@Found,$Temp); # Check for single digit if($String=~/^(.)\1*$/) { push(@Found,'single digit');} # Check for palindrome my $Reversed=reverse($String); if($String eq $Reversed) { push(@Found,'palindrome');} # Check for left-right mirror symmetric if($String eq $Reversed && $String=~/^[180]*$/) { push(@Found,'left-right mirror symmetric');} # Check for up-down mirror symmetric if($String=~/^[180]*$/) { push(@Found,'up-down mirror symmetric');} # Check for diad ambigram if($String=~/^[1256890]*$/) { ($Temp=$Reversed)=~tr/2569/5296/; if($String eq $Temp) { push(@Found,'180 deg rotationally symmetric');}} # Check for cyclic permuation symmetric if(substr($String.$String,1,-1)=~/$String/) { push(@Found,'cyclic permutation symmetric');} return @Found;} ############################################################################### # Find mathematical series in a list of numbers ############################################################################### # Searches for simple mathematical series in an array of digits and # returns names/descriptions of any it finds. It is sensitive to the order # of the numbers (e.g. (1,2,3) is an arithmetic series but (1,3,2) is not). ############################################################################### # Parameters: # The array/list of numbers. # Returns: # Array of names/descriptions of the patterns found. ############################################################################### sub FindSeriesInNumberList { my @List=@_; my(@Found,$c); # Check for arithmetic series (but not simply equal) if(@List>=3&&$List[0]!=$List[1]) { my $Increment=$List[1]-$List[0]; for($c=2;$c<@List;$c++) { if($List[$c-1]+$Increment!=$List[$c]) { last;}} if($c==@List) { push(@Found,"arthimetic series of increment $Increment");}} # Check for geometric series (but not simply equal) if(@List>=3&&$List[0]!=$List[1]&&$List[0]!=0) { my $Ratio=$List[1]/$List[0]; for($c=2;$c<@List;$c++) { if($List[$c-1]*$Ratio!=$List[$c]) { last;}} if($c==@List) { push(@Found,"geometric series of ratio $Ratio");}} # Check for simple ratio (but not just equal or already a geometric series) if(@List==2&&$List[0]!=$List[1]&&$List[0]!=0&&$List[1]!=0) { if($List[1]>$List[0]) { if($List[1]%$List[0]==0) { push(@Found,"integer ratio of 1 to ".$List[1]/$List[0]);}} else { if($List[0]%$List[1]==0) { push(@Found,"integer ratio of ".$List[0]/$List[1]." to 1");}}} return @Found;} ############################################################################### # Find text patterns common to a list of digits ############################################################################### # Searches for text based patterns that are common to all members of # list of strings of digits and returns names/descriptions of any it finds. ############################################################################### # Parameters: # The array/list of strings of digits. # Returns: # Array of names/descriptions of the patterns found. ############################################################################### sub FindPatternsInStringList { my @List=@_; # Find the patterns which apply each string my($Found,%Found,@Found); foreach my $String (@List) { foreach $Found (FindPatternsInString($String)) { $Found{$Found}++;}} # Find the subset of those patterns that apply to to every string foreach $Found (keys %Found) { if($Found{$Found}==@List) { push(@Found,"each is $Found");}} return @Found;} ############################################################################### ###############################################################################