<commit_before>use either::Either;


pub trait CoSuspend<Output> {
    type Yield;
    type Continuation: Coroutine<Output>;
    type Suspension: CoSuspend<Output>;

    fn get(self) -> Either<(Self::Yield, Self::Continuation), Self::Suspension>;
}

impl<Output> CoSuspend<Output> for ! {
    type Yield = !;
    type Continuation = !;
    type Suspension = !;

    fn get(self) -> Either<(Self::Yield, Self::Continuation), Self::Suspension> {
        unreachable!()
    }
}

pub enum CoResult<Suspend, Output>
    where Suspend: CoSuspend<Output>
{
    Suspend(Suspend),
    Return(Output),
}

pub trait Coroutine<Output>: Sized {
    type Input;
    type Suspend: CoSuspend<Output>;

    fn send(self, i: Self::Input) -> CoResult<Self::Suspend, Output>;
}

impl<Output> Coroutine<Output> for ! {
    type Input = !;
    type Suspend = !;

    fn send(self, _: Self::Input) -> CoResult<Self::Suspend, Output> {
        unreachable!()
    }
}


#[cfg(test)]
mod tests {

    use super::{Coroutine, CoSuspend, CoResult};
    use either::Either;

    struct Guess(i64);

    struct Retry {
        too_small: bool,
        guess: Guess,
    }

    impl Retry {
        fn too_small(guess: Guess) -> Self {
            Retry { too_small: true, guess }
        }

        fn too_big(guess: Guess) -> Self {
            Retry { too_small: false, guess }
        }
    }

    struct TooBig(Guess);

    impl CoSuspend<&'static str> for TooBig {
        type Yield = &'static str;
        type Continuation = Guess;
        type Suspension = !;

        fn get(self) -> Either<(Self::Yield, Self::Continuation), Self::Suspension> {
            Either::Former(("Too big!", self.0))
        }
    }

    impl CoSuspend<&'static str> for Retry {
        type Yield = &'static str;
        type Continuation = Guess;
        type Suspension = TooBig;

        fn get(self) -> Either<(Self::Yield, Self::Continuation), Self::Suspension> {
            if self.too_small {
                Either::Former(("Too small!", self.guess))
            } else {
                Either::Latter(TooBig(self.guess))
            }
        }
    }

    impl Coroutine<&'static str> for Guess {
        type Input = i64;
        type Suspend = Retry;

        fn send(self, i: Self::Input) -> CoResult<Self::Suspend, &'static str> {
            if self.0 == i {
                CoResult::Return("You guessed it!")
            } else if i < self.0 {
                CoResult::Suspend(Retry::too_small(self))
            } else {
                CoResult::Suspend(Retry::too_big(self))
            }
        }
    }

    fn play_guess(secret: i64) -> Guess {
        Guess(secret)
    }

    /*
    fn play_guess(secret: i64) {
        return move |guess: i64| {
            let mut guess = guess;
            loop {
                guess = if guess == secret {
                    return "You guessed it!";
                } else if guess < secret {
                    yield "Too small!";
                } else {
                    yield "Too big!";
                }
            }
        }
    */

    #[test]
    fn guess() {
        let game = play_guess(16);

        let suspend = match game.send(10) {
            CoResult::Suspend(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let game = match suspend.get() {
            Either::Former((msg, guess_again)) => {
                assert_eq!(msg, "Too small!");
                guess_again
            },
            _ => panic!("The number was clearly too small! Why do you not see this?")
        };

        let suspend = match game.send(19) {
            CoResult::Suspend(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let suspend = match suspend.get() {
            Either::Latter(suspend) => suspend,
            _ => panic!("The number was clearly too big! Why do you not see this?")
        };

        let game = match suspend.get() {
            Either::Former((msg, guess_again)) => {
                assert_eq!(msg, "Too big!");
                guess_again
            }
        };
            
        match game.send(16) {
            CoResult::Return(msg) => assert_eq!(msg, "You guessed it!"),
            _ => panic!("The answer was correct! The coroutine should return.")
        }
    }
}<commit_msg>Use generalized enums as suspension points<commit_after>use enums::Enum;


pub trait CoSuspend<Output> {
    type Yield;
    type Continuation: Coroutine<Output>;
    type Suspension: CoSuspend<Output>;
}

impl<Output> CoSuspend<Output> for ! {
    type Yield = !;
    type Continuation = !;
    type Suspension = !;
}

impl<Output, Enumeration, Y, C> CoSuspend<Output> for Enumeration
    where C: Coroutine<Output>,
          Enumeration: Enum<Variant = (Y, C)>,
          Enumeration::Next: CoSuspend<Output>
{
    type Yield = Y;
    type Continuation = C;
    type Suspension = Enumeration::Next;
}

pub enum CoResult<Suspend, Output>
    where Suspend: CoSuspend<Output>
{
    Suspend(Suspend),
    Return(Output),
}

pub trait Coroutine<Output>: Sized {
    type Input;
    type Suspend: CoSuspend<Output>;

    fn send(self, i: Self::Input) -> CoResult<Self::Suspend, Output>;
}

impl<Output> Coroutine<Output> for ! {
    type Input = !;
    type Suspend = !;

    fn send(self, _: Self::Input) -> CoResult<Self::Suspend, Output> {
        unreachable!()
    }
}


#[cfg(test)]
mod tests {

    use super::{Coroutine, CoResult};
    use enums::Match::*;
    use enums::Match;

    struct Guess(i64);

    impl Coroutine<&'static str> for Guess {
        type Input = i64;
        type Suspend = Match<(&'static str, Guess),
                       Match<(&'static str, Guess),
                       !>>;

        fn send(self, i: Self::Input) -> CoResult<Self::Suspend, &'static str> {
            if self.0 == i {
                CoResult::Return("You guessed it!")
            } else if i < self.0 {
                CoResult::Suspend(Variant(("Too small!", self)))
            } else {
                CoResult::Suspend(Next(Variant(("Too big!", self))))
            }
        }
    }

    fn play_guess(secret: i64) -> Guess {
        Guess(secret)
    }

    /*
    fn play_guess(secret: i64) {
        return move |guess: i64| {
            let mut guess = guess;
            loop {
                guess = if guess == secret {
                    return "You guessed it!";
                } else if guess < secret {
                    yield "Too small!";
                } else {
                    yield "Too big!";
                }
            }
        }
    */

    #[test]
    fn guess() {
        let game = play_guess(16);

        let suspend = match game.send(10) {
            CoResult::Suspend(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let game = match suspend {
            Variant((msg, guess_again)) => {
                assert_eq!(msg, "Too small!");
                guess_again
            }
            _ => panic!("The number was clearly too small! Why do you not see this?"),
        };

        let suspend = match game.send(19) {
            CoResult::Suspend(suspend) => suspend,
            _ => panic!("We didn't guess the number, so the coroutine should not return yet!"),
        };

        let game = match suspend {
            Next(Variant((msg, guess_again))) => {
                assert_eq!(msg, "Too big!");
                guess_again
            }
            _ => panic!("The number was clearly too big! Why do you not see this?"),
        };

        match game.send(16) {
            CoResult::Return(msg) => assert_eq!(msg, "You guessed it!"),
            _ => panic!("The answer was correct! The coroutine should return."),
        }
    }
}
{'fraction_non_alphanumeric': 0.1300012621481762, 'fraction_numerical': 0.00517480752240313, 'mean_word_length': 1.801980198019802, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 81, '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': '1289082', 'n_tokens_mistral': 2443, 'n_tokens_neox': 2410, 'n_words': 608}