<commit_before>use v6;

class WorkdayCalendar {
    has Str  $.file     is rw;
    has Date @.holidays is rw;
    has Str  @.workdays is rw = <Mon Tue Wed Thu Fri>;
    
    method clear { 
        @.workdays = <Mon Tue Wed Thu Fri>;
        @.holidays = (); 
    }

    multi method new(Str :$filename!) {
        my $c = WorkdayCalendar.new;
        $c.read($filename);
        return $c;
    }

    multi method new(Str $filename) {
        return WorkdayCalendar.new(filename=>$filename);
    }

    method perl {
        return "WorkdayCalendar.new(" ~ @.workdays.perl ~ ", " ~ @.holidays.perl ~ ", " ~ $.file.perl ~ ")";         
    }

    method read(Str $calendar_filename) {
        my $CAL = open $calendar_filename or die "Can't open $calendar_filename : $!";
        $.file = $calendar_filename;
        self.clear;
        for $CAL.lines -> $line {
            next if $line ~~ /^'#'/; #--- Comments are skipped
            my ($type, $data) = split /':'/, $line;
            given $type {
                when 'H' {
                    my ($year, $month, $day) = split /'/'|'-'/, $data; #--- Only Holidays '
                    try {
                        push @.holidays, Date.new("$year-$month-$day");;
                        CATCH {
                            note "ERROR: Specifying holiday date. Date $year-$month-$day skipped";
                            next;
                        }
                    }    
                }
                when 'W' {
                    try {
                        my @workweek_spec = split /','/, $data; 
                        my $workweek_spec_failed = False;
                        for @workweek_spec -> $weekday {
                            if ($weekday ne any(<Mon Tue Wed Thu Fri Sat Sun>)) {
                                warn "Workday '$weekday' not recognized";
                                $workweek_spec_failed = True;
                                last;
                            }
                        }
                        if ($workweek_spec_failed) {
                            note "ERROR: Workweek specification not valid. Assuming Mon,Tue,Wed,Thu,Fri";
                            @.workdays = <Mon Tue Wed Thu Fri>;
                            next;
                        } else {
                            @.workdays = @workweek_spec;    
                        }
                    }
                }                 
            }
        }
        @.holidays .= sort;
        close $CAL;
    }

    method is-workday(Date $day){
        return !(self.is-weekend($day) || self.is-holiday($day));
    }

    method is-weekend(Date $day) {
        my $weekday_name = <Mon Tue Wed Thu Fri Sat Sun>[$day.day-of-week - 1]; 
        return ($weekday_name ne any(@.workdays));
    }

    method is-holiday(Date $day) {
        my @daycounts;
        push @daycounts, $_.get-daycount for @.holidays;
        return ?($day.get-daycount == any(@daycounts));
    }

    method workdays-away(Date $start, Int $days) {
        return $start if $days == 0;
        my Date $current_day = $start;
        for (1 .. $days.abs) {
            repeat {
                $current_day = ($days > 0) ?? $current_day.Date::succ 
                                           !! $current_day.Date::pred;
            } until self.is-workday($current_day);
        }   
        return $current_day;                              
    }

    method workdays-to(Date $start is copy, Date $target is copy) {
        return 0 if $start.get-daycount == $target.get-daycount;
        my $sign = ($start.get-daycount < $target.get-daycount) ?? +1
                                                                !! -1;
        my Date $current_day = $start;
        my Int $count = 0;
        repeat {
            $current_day = ($sign == +1) ?? $current_day.Date::succ
                                         !! $current_day.Date::pred;
            $count++ if self.is-workday($current_day);
        } until ($current_day.get-daycount == $target.get-daycount);
        return ($count * $sign);
    }
    
    method networkdays(Date $start is copy, Date $target is copy) {
        if $start.get-daycount == $target.get-daycount {
             return (self.is-workday($start)) ?? 1
                                              !! 0;
        }
        my $sign = +1;
        if $start.get-daycount > $target.get-daycount {
            $sign = -1;
            my Date $aux_day = $start;
            $start = $target;
            $target = $aux_day;
        }
        my Date $current_day = $start;
        my Int $count = 0;
        while ($current_day.get-daycount <= $target.get-daycount) {
            $count++ if self.is-workday($current_day);
            $current_day = $current_day.Date::succ;    
        } 
        return $count * $sign;
    }
    
    method range(Date $begin, Date $end) {      
        my Date @slice;
        my Int $from = ($begin.get-daycount, $end.get-daycount).min;
        my Int $to   = ($begin.get-daycount, $end.get-daycount).max;
        for (@.holidays) -> $date { #--- The holidays are already sorted
            push @slice, $date if ($from <= $date.get-daycount <= $to);
        }
        my $result_calendar = self.clone; #-- Requires a customized version of clone
        $result_calendar.holidays = @slice;
        return $result_calendar;
    }
    
    method clone {
        my WorkdayCalendar $new = WorkdayCalendar.new;
        given self {
            $new.workdays = .workdays;
            $new.holidays = .holidays;
            $new.file     = .file;
        }
        return $new;
    }
}  

#------------------------------------------------------------------------------#

class Workdate is Date {
    has WorkdayCalendar $.calendar is rw;
        
    # We try to provide the same constructors as the Date class 
    multi method new(:$year!, :$month, :$day, :$calendar = WorkdayCalendar.new) {
        #my $D = callwith(year=>$year, month=>$month, day=>$day); 
        my $D = self.Date::new(year=>$year, month=>$month, day=>$day);
        $D.calendar = $calendar; 
        return $D; <commit_msg>get-daycount went away<commit_after>use v6;

class WorkdayCalendar {
    has Str  $.file     is rw;
    has Date @.holidays is rw;
    has Str  @.workdays is rw = <Mon Tue Wed Thu Fri>;
    
    method clear { 
        @.workdays = <Mon Tue Wed Thu Fri>;
        @.holidays = (); 
    }

    multi method new(Str :$filename!) {
        my $c = WorkdayCalendar.new;
        $c.read($filename);
        return $c;
    }

    multi method new(Str $filename) {
        return WorkdayCalendar.new(filename=>$filename);
    }

    method perl {
        return "WorkdayCalendar.new(" ~ @.workdays.perl ~ ", " ~ @.holidays.perl ~ ", " ~ $.file.perl ~ ")";         
    }

    method read(Str $calendar_filename) {
        my $CAL = open $calendar_filename or die "Can't open $calendar_filename : $!";
        $.file = $calendar_filename;
        self.clear;
        for $CAL.lines -> $line {
            next if $line ~~ /^'#'/; #--- Comments are skipped
            my ($type, $data) = split /':'/, $line;
            given $type {
                when 'H' {
                    my ($year, $month, $day) = split /'/'|'-'/, $data; #--- Only Holidays '
                    try {
                        push @.holidays, Date.new("$year-$month-$day");;
                        CATCH {
                            note "ERROR: Specifying holiday date. Date $year-$month-$day skipped";
                            next;
                        }
                    }    
                }
                when 'W' {
                    try {
                        my @workweek_spec = split /','/, $data; 
                        my $workweek_spec_failed = False;
                        for @workweek_spec -> $weekday {
                            if ($weekday ne any(<Mon Tue Wed Thu Fri Sat Sun>)) {
                                warn "Workday '$weekday' not recognized";
                                $workweek_spec_failed = True;
                                last;
                            }
                        }
                        if ($workweek_spec_failed) {
                            note "ERROR: Workweek specification not valid. Assuming Mon,Tue,Wed,Thu,Fri";
                            @.workdays = <Mon Tue Wed Thu Fri>;
                            next;
                        } else {
                            @.workdays = @workweek_spec;    
                        }
                    }
                }                 
            }
        }
        @.holidays .= sort;
        close $CAL;
    }

    method is-workday(Date $day){
        return !(self.is-weekend($day) || self.is-holiday($day));
    }

    method is-weekend(Date $day) {
        my $weekday_name = <Mon Tue Wed Thu Fri Sat Sun>[$day.day-of-week - 1]; 
        return ($weekday_name ne any(@.workdays));
    }

    method is-holiday(Date $day) {
        my @daycounts;
        push @daycounts, $_.daycount for @.holidays;
        return ?($day.daycount == any(@daycounts));
    }

    method workdays-away(Date $start, Int $days) {
        return $start if $days == 0;
        my Date $current_day = $start;
        for (1 .. $days.abs) {
            repeat {
                $current_day = ($days > 0) ?? $current_day.Date::succ 
                                           !! $current_day.Date::pred;
            } until self.is-workday($current_day);
        }   
        return $current_day;                              
    }

    method workdays-to(Date $start is copy, Date $target is copy) {
        return 0 if $start.daycount == $target.daycount;
        my $sign = ($start.daycount < $target.daycount) ?? +1
                                                                !! -1;
        my Date $current_day = $start;
        my Int $count = 0;
        repeat {
            $current_day = ($sign == +1) ?? $current_day.Date::succ
                                         !! $current_day.Date::pred;
            $count++ if self.is-workday($current_day);
        } until ($current_day.daycount == $target.daycount);
        return ($count * $sign);
    }
    
    method networkdays(Date $start is copy, Date $target is copy) {
        if $start.daycount == $target.daycount {
             return (self.is-workday($start)) ?? 1
                                              !! 0;
        }
        my $sign = +1;
        if $start.daycount > $target.daycount {
            $sign = -1;
            my Date $aux_day = $start;
            $start = $target;
            $target = $aux_day;
        }
        my Date $current_day = $start;
        my Int $count = 0;
        while ($current_day.daycount <= $target.daycount) {
            $count++ if self.is-workday($current_day);
            $current_day = $current_day.Date::succ;    
        } 
        return $count * $sign;
    }
    
    method range(Date $begin, Date $end) {      
        my Date @slice;
        my Int $from = ($begin.daycount, $end.daycount).min;
        my Int $to   = ($begin.daycount, $end.daycount).max;
        for (@.holidays) -> $date { #--- The holidays are already sorted
            push @slice, $date if ($from <= $date.daycount <= $to);
        }
        my $result_calendar = self.clone; #-- Requires a customized version of clone
        $result_calendar.holidays = @slice;
        return $result_calendar;
    }
    
    method clone {
        my WorkdayCalendar $new = WorkdayCalendar.new;
        given self {
            $new.workdays = .workdays;
            $new.holidays = .holidays;
            $new.file     = .file;
        }
        return $new;
    }
}  

#------------------------------------------------------------------------------#

class Workdate is Date {
    has WorkdayCalendar $.calendar is rw;
        
    # We try to provide the same constructors as the Date class 
    multi method new(:$year!, :$month, :$day, :$calendar = WorkdayCalendar.new) {
        #my $D = callwith(year=>$year, month=>$month, day=>$day); 
        my $D = self.Date::new(year=>$year, month=>$month, day=>$day);
        $D.calendar = $calendar; 
        return $D; 
{'fraction_non_alphanumeric': 0.1391689205802803, 'fraction_numerical': 0.0024588148512417014, 'mean_word_length': 1.2634019662400298, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5080232', 'n_tokens_mistral': 3553, 'n_tokens_neox': 3406, 'n_words': 854}