<commit_before>package schyntax

import (
	"github.com/schyntax/go-schyntax/internals"
	"math"
	"time"
)

type Schedule struct {
	OriginalText string
	ir           *internals.IrProgram
}

func New(schedule string) (sch *Schedule, err error) {
	defer func() {
		if e := recover(); e != nil {
			sch = nil
			switch e.(type) {
			case string:
				err = newInternalError(e.(string), sch.OriginalText)
			default:
				if er, ok := e.(error); ok {
					err = er
				} else {
					panic(e) // no idea what was passed to panic, just pass it along
				}
			}
		}
	}()

	sch = &Schedule{}
	sch.OriginalText = schedule

	parser := internals.NewParser(schedule)
	ast := parser.Parse()

	validator := internals.Validator{schedule, ast}
	validator.AssertValid()

	sch.ir = internals.CompileAst(ast)

	return
}

func (s *Schedule) Next() (time.Time, error) {
	return s.getEvent(time.Now(), searchModeAfter)
}

func (s *Schedule) NextAfter(after time.Time) (time.Time, error) {
	return s.getEvent(after, searchModeAfter)
}

func (s *Schedule) Previous() (time.Time, error) {
	return s.getEvent(time.Now(), searchModeAtOrBefore)
}

func (s *Schedule) PreviousAtOrBefore(atOrBefore time.Time) (time.Time, error) {
	return s.getEvent(atOrBefore, searchModeAtOrBefore)
}

type searchMode int8

const (
	searchModeAtOrBefore searchMode = iota
	searchModeAfter
)

func (s *Schedule) getEvent(start time.Time, mode searchMode) (result time.Time, err error) {
	start = start.UTC()
	found := false

	for _, group := range s.ir.Groups {
		if e, good := tryGetGroupEvent(group, start, mode); good {<commit_msg>Add alternate next/previous methods which panic.<commit_after>package schyntax

import (
	"github.com/schyntax/go-schyntax/internals"
	"math"
	"time"
)

type Schedule struct {
	OriginalText string
	ir           *internals.IrProgram
}

func New(schedule string) (sch *Schedule, err error) {
	defer func() {
		if e := recover(); e != nil {
			sch = nil
			switch e.(type) {
			case string:
				err = newInternalError(e.(string), sch.OriginalText)
			default:
				if er, ok := e.(error); ok {
					err = er
				} else {
					panic(e) // no idea what was passed to panic, just pass it along
				}
			}
		}
	}()

	sch = &Schedule{}
	sch.OriginalText = schedule

	parser := internals.NewParser(schedule)
	ast := parser.Parse()

	validator := internals.Validator{schedule, ast}
	validator.AssertValid()

	sch.ir = internals.CompileAst(ast)

	return
}

func (s *Schedule) Next() time.Time {
	t, err := s.TryNext()
	if err != nil {
		panic(err)
	}

	return t
}

func (s *Schedule) TryNext() (time.Time, error) {
	return s.getEvent(time.Now(), searchModeAfter)
}

func (s *Schedule) NextAfter(after time.Time) time.Time {
	t, err := s.TryNextAfter(after)
	if err != nil {
		panic(err)
	}

	return t
}

func (s *Schedule) TryNextAfter(after time.Time) (time.Time, error) {
	return s.getEvent(after, searchModeAfter)
}

func (s *Schedule) Previous() time.Time {
	t, err := s.TryPrevious()
	if err != nil {
		panic(err)
	}

	return t
}

func (s *Schedule) TryPrevious() (time.Time, error) {
	return s.getEvent(time.Now(), searchModeAtOrBefore)
}

func (s *Schedule) PreviousAtOrBefore(atOrBefore time.Time) time.Time {
	t, err := s.TryPreviousAtOrBefore(atOrBefore)
	if err != nil {
		panic(err)
	}

	return t
}

func (s *Schedule) TryPreviousAtOrBefore(atOrBefore time.Time) (time.Time, error) {
	return s.getEvent(atOrBefore, searchModeAtOrBefore)
}

type searchMode int8

const (
	searchModeAtOrBefore searchMode = iota
	searchModeAfter
)

func (s *Schedule) getEvent(start time.Time, mode searchMode) (result time.Time, err error) {
	start = start.UTC()
	found := false

	for _, group := range s.ir.Groups {
		if e, good := tryGetGroupEvent(group, start, mode); good {
{'fraction_non_alphanumeric': 0.1346513491851456, 'fraction_numerical': 0.0005343307507347048, 'mean_word_length': 3.8, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '5231965', 'n_tokens_mistral': 1402, 'n_tokens_neox': 1311, 'n_words': 351}