<commit_before>import itertools
from functools import wraps
from typing import Generic, TypeVar, Callable, Union, Any, Iterable, Generator

from toolz import concatv

from amino.list import List
from amino.maybe import Just, Empty, Maybe
from amino.tc.base import Implicits
from amino.func import I
from amino.util.string import safe_string

A = TypeVar('A')
B = TypeVar('B')


class LazyList(Generic[A], Implicits, implicits=True):
    _default_chunk_size = 20

    def fetch(f: Callable) -> Callable:
        @wraps(f)
        def wrapper(self: 'LazyList', index: int) -> Any:
            self._fetch(index)
            return f(self, index)
        return wrapper

    def __init__(self, source: Iterable, init: List[A]=List(), chunk_size: Union[int, None]=None) -> None:
        self.source = iter(source)
        self.strict = init
        self._chunk_size = chunk_size or self._default_chunk_size

    @fetch
    def __getitem__(self, index: int) -> Maybe[A]:
        return self.strict.__getitem__(index)

    def __len__(self) -> int:
        return self.drain.length

    @property
    def _one(self) -> Generator:
        try:
            yield next(self.source)
        except StopIteration:
            pass

    def _fetch(self, index: Union[slice, int, float]) -> None:
        count = index.stop if isinstance(index, slice) else index + 1
        def chunk() -> List[A]:
            for i in range(self._chunk_size):<commit_msg>allow negative indexes in `LazyList`<commit_after>import itertools
from functools import wraps
from typing import Generic, TypeVar, Callable, Union, Any, Iterable, Generator

from toolz import concatv

from amino.list import List
from amino.maybe import Just, Empty, Maybe
from amino.tc.base import Implicits
from amino.func import I
from amino.util.string import safe_string

A = TypeVar('A')
B = TypeVar('B')


class LazyList(Generic[A], Implicits, implicits=True):
    _default_chunk_size = 20

    def fetch(f: Callable) -> Callable:
        @wraps(f)
        def wrapper(self: 'LazyList', index: int) -> Any:
            if index >= 0:
                self._fetch(index)
            else:
                self._drain()
            return f(self, index)
        return wrapper

    def __init__(self, source: Iterable, init: List[A]=List(), chunk_size: Union[int, None]=None) -> None:
        self.source = iter(source)
        self.strict = init
        self._chunk_size = chunk_size or self._default_chunk_size

    @fetch
    def __getitem__(self, index: int) -> Maybe[A]:
        return self.strict.__getitem__(index)

    def __len__(self) -> int:
        return self.drain.length

    @property
    def _one(self) -> Generator:
        try:
            yield next(self.source)
        except StopIteration:
            pass

    def _fetch(self, index: Union[slice, int, float]) -> None:
        count = index.stop if isinstance(index, slice) else index + 1
        def chunk() -> List[A]:
            for i in range(self._chunk_size):
{'fraction_non_alphanumeric': 0.10408299866131192, 'fraction_numerical': 0.0023427041499330657, 'mean_word_length': 2.6901234567901233, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 20, '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': '7275287', 'n_tokens_mistral': 931, 'n_tokens_neox': 894, 'n_words': 272}