Skip to content

Instantly share code, notes, and snippets.

@kamermans
Last active February 8, 2020 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kamermans/0f91f2a0fc955845b3d2aa28a4253949 to your computer and use it in GitHub Desktop.
Save kamermans/0f91f2a0fc955845b3d2aa28a4253949 to your computer and use it in GitHub Desktop.
Compute date palindromes efficiently
package main
import (
"fmt"
"os"
"sort"
"sync"
"time"
)
const (
start = 2020
end = 2030
show = 50
)
func init() {
os.Setenv("TZ", "UTC")
}
func main() {
var wg sync.WaitGroup
palindromes := make(chan Palindrome)
timing := time.Now()
initSearch(start, end)
searches := []SearchFunc{
searchByDay,
searchBySecond,
searchTimestamp,
}
// Run all the searches in parallel
for _, search := range searches {
search := search
wg.Add(1)
go func() {
defer wg.Done()
search(start, end, palindromes)
}()
}
// Close channel after all searches are finished
go func() {
wg.Wait()
close(palindromes)
}()
// Sort results
results := []Palindrome{}
for palindrome := range palindromes {
results = append(results, palindrome)
}
sort.Slice(results, func(i, j int) bool {
return results[i].ToTime().Before(results[j].ToTime())
})
// Print results
for i, palindrome := range results {
if i < show {
fmt.Printf("%v\n", palindrome)
}
}
fmt.Printf("Found %v palindromes in %v\n", len(results), time.Since(timing))
}
$ go build . && ./Palindrome.exe
2020-01-01 09:09:11 (Timestamp: 1577887751)
2020-01-02 15:42:31 (Timestamp: 1577997751)
2020-01-02 18:45:51 (Timestamp: 1578008751)
2020-01-04 01:19:11 (Timestamp: 1578118751)
2020-01-05 07:52:31 (Timestamp: 1578228751)
2020-01-06 14:25:51 (Timestamp: 1578338751)
2020-01-07 20:59:11 (Timestamp: 1578448751)
2020-01-09 03:32:31 (Timestamp: 1578558751)
2020-01-10 10:05:51 (Timestamp: 1578668751)
2020-01-11 10:02:02 (Date/Time: 20200111100202)
2020-01-11 16:39:11 (Timestamp: 1578778751)
2020-01-12 23:12:31 (Timestamp: 1578888751)
2020-01-14 05:45:51 (Timestamp: 1578998751)
2020-01-14 08:49:11 (Timestamp: 1579009751)
2020-01-15 15:22:31 (Timestamp: 1579119751)
2020-01-16 21:55:51 (Timestamp: 1579229751)
2020-01-18 04:29:11 (Timestamp: 1579339751)
2020-01-19 11:02:31 (Timestamp: 1579449751)
2020-01-20 17:35:51 (Timestamp: 1579559751)
2020-01-22 00:09:11 (Timestamp: 1579669751)
2020-01-22 10:02:02 (Date/Time: 20200122100202)
2020-01-23 06:42:31 (Timestamp: 1579779751)
2020-01-24 13:15:51 (Timestamp: 1579889751)
2020-01-25 19:49:11 (Timestamp: 1579999751)
2020-01-25 20:07:31 (Timestamp: 1580000851)
2020-01-27 02:40:51 (Timestamp: 1580110851)
2020-01-28 09:14:11 (Timestamp: 1580220851)
2020-01-29 15:47:31 (Timestamp: 1580330851)
2020-01-30 22:20:51 (Timestamp: 1580440851)
2020-02-01 04:54:11 (Timestamp: 1580550851)
2020-02-02 (Date: 20200202)
2020-02-02 11:27:31 (Timestamp: 1580660851)
2020-02-03 18:00:51 (Timestamp: 1580770851)
2020-02-05 00:34:11 (Timestamp: 1580880851)
2020-02-06 07:07:31 (Timestamp: 1580990851)
2020-02-06 10:10:51 (Timestamp: 1581001851)
2020-02-07 16:44:11 (Timestamp: 1581111851)
2020-02-08 23:17:31 (Timestamp: 1581221851)
2020-02-10 05:50:51 (Timestamp: 1581331851)
2020-02-11 12:24:11 (Timestamp: 1581441851)
2020-02-11 20:02:02 (Date/Time: 20200211200202)
2020-02-12 18:57:31 (Timestamp: 1581551851)
2020-02-14 01:30:51 (Timestamp: 1581661851)
2020-02-15 08:04:11 (Timestamp: 1581771851)
2020-02-16 14:37:31 (Timestamp: 1581881851)
2020-02-17 21:10:51 (Timestamp: 1581991851)
2020-02-18 00:14:11 (Timestamp: 1582002851)
2020-02-19 06:47:31 (Timestamp: 1582112851)
2020-02-20 13:20:51 (Timestamp: 1582222851)
2020-02-21 19:54:11 (Timestamp: 1582332851)
Found 3544 palindromes in 2.9637ms
package main
import (
"strconv"
"time"
)
var (
monthDays = []int{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
bySecondMonthHourCombos = map[int]int{}
bySecondYearTimeCombos = map[int][2]int{}
bySecondDays = []int{11, 22}
bySecondLength = 14
)
type Palindrome interface {
ToInt() int
ToTime() time.Time
String() string
}
type SearchFunc = func(int, int, chan<- Palindrome)
type SecondPalindrome struct {
Year, Month, Day, Hour, Minute, Second int
intVal int
}
func (p *SecondPalindrome) ToInt() int {
if p.intVal == 0 {
p.intVal = makeDateBySecond(p.Year, p.Month, p.Day, p.Hour, p.Minute, p.Second)
}
return p.intVal
}
func (p SecondPalindrome) ToTime() time.Time {
return time.Date(p.Year, time.Month(p.Month), p.Day, p.Hour, p.Minute, p.Second, 0, time.UTC)
}
func (p SecondPalindrome) String() string {
return p.ToTime().Format("2006-01-02 15:04:05") + " (Date/Time: " + strconv.Itoa(p.ToInt()) + ")"
}
type DayPalindrome struct {
Year, Month, Day int
intVal int
}
func (p *DayPalindrome) ToInt() int {
if p.intVal == 0 {
p.intVal = makeDateByDay(p.Year, p.Month, p.Day)
}
return p.intVal
}
func (p DayPalindrome) ToTime() time.Time {
return time.Date(p.Year, time.Month(p.Month), p.Day, 0, 0, 0, 0, time.UTC)
}
func (p DayPalindrome) String() string {
return p.ToTime().Format("2006-01-02") + " (Date: " + strconv.Itoa(p.ToInt()) + ")"
}
type TimestampPalindrome struct {
Val int
}
func (p *TimestampPalindrome) ToInt() int {
return p.Val
}
func (p TimestampPalindrome) ToTime() time.Time {
return time.Unix(int64(p.Val), 0)
}
func (p TimestampPalindrome) String() string {
return p.ToTime().Format("2006-01-02 15:04:05") + " (Timestamp: " + strconv.Itoa(p.Val) + ")"
}
func initSearch(start, end int) {
// YY YY MM D D HH MM SS
// MM == rev(HH)
// YYYY = rev(MM SS)
for month := 1; month < 13; month++ {
hour := reverseInt(month, 2)
if hour > 23 {
continue
}
bySecondMonthHourCombos[month] = reverseInt(month, 2)
}
}
func makeDateBySecond(year, month, day, hour, minute, second int) int {
return year*10000000000 + month*100000000 + day*1000000 + hour*10000 + minute*100 + second
}
func makeDateByDay(year, month, day int) int {
return year*10000 + month*100 + day
}
func searchBySecond(start, end int, palindromes chan<- Palindrome) {
for year := start; year <= end; year++ {
minuteSecond := reverseInt(year, 4)
minute := minuteSecond / 100
second := minuteSecond % 100
if minute > 59 || second > 59 || second == 0 || second%10 == 0 {
continue
}
for month, hour := range bySecondMonthHourCombos {
for _, day := range bySecondDays {
palindromes <- &SecondPalindrome{year, month, day, hour, minute, second, 0}
}
}
}
}
func searchByDay(start, end int, palindromes chan<- Palindrome) {
for year := start; year <= end; year++ {
monthDay := reverseInt(year, 4)
month := monthDay / 100
day := monthDay % 100
if month > 12 || day > daysIn(month, year) || day%10 == 0 {
continue
}
palindromes <- &DayPalindrome{year, month, day, 0}
}
}
func searchTimestamp(start, end int, palindromes chan<- Palindrome) {
startTS := int(time.Date(start, time.January, 1, 0, 0, 0, 0, time.UTC).Unix())
endTS := int(time.Date(end, time.December, 31, 23, 59, 59, 0, time.UTC).Unix())
// 1581190424
startTSPrefix := startTS / 100000
endTSPrefix := endTS / 100000
for prefix := startTSPrefix; prefix <= endTSPrefix; prefix++ {
ts := prefix*100000 + reverseInt(prefix, 5)
if ts < startTS || ts > endTS {
continue
}
palindromes <- &TimestampPalindrome{ts}
}
}
func isPalindromeRune(d []rune) bool {
for i := 0; i < len(d)/2; i++ {
if d[i] != d[len(d)-1-i] {
return false
}
}
return true
}
func isPalindrome(num, length int) bool {
return isPalindromeRune([]rune(strconv.Itoa(reverseInt(num, length))))
}
func daysIn(m int, year int) int {
if m == 2 && isLeap(year) {
return 29
}
return monthDays[m]
}
func isLeap(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}
func reverseInt(n, length int) int {
r := 0
// for n != 0 {
for i := 0; i < length; i++ {
r = r * 10
r = r + n%10
n = n / 10
}
return r
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment