From 73204923dd121546f89010e974f6e7e5e9009373 Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Thu, 8 Dec 2016 11:35:58 -0500 Subject: [PATCH 1/4] core: Add a stub for basic functionality. --- kenjutsu/core.py | 4 ++++ tests/test_core.py | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 kenjutsu/core.py create mode 100644 tests/test_core.py diff --git a/kenjutsu/core.py b/kenjutsu/core.py new file mode 100644 index 0000000..f695748 --- /dev/null +++ b/kenjutsu/core.py @@ -0,0 +1,4 @@ +__author__ = "John Kirkham " +__date__ = "$Dec 08, 2016 11:35:58 GMT-0500$" + + diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 0000000..f695748 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,4 @@ +__author__ = "John Kirkham " +__date__ = "$Dec 08, 2016 11:35:58 GMT-0500$" + + From 0a16d5e9d6979b5427b83de32f50e26a9bb03b96 Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Thu, 8 Dec 2016 11:38:44 -0500 Subject: [PATCH 2/4] kenjutsu -> core: Move/renamed. --- kenjutsu/core.py | 550 +++++++++++++++++++++++++++++++ kenjutsu/kenjutsu.py | 554 ------------------------------- tests/test_core.py | 728 ++++++++++++++++++++++++++++++++++++++++ tests/test_kenjutsu.py | 729 ----------------------------------------- 4 files changed, 1278 insertions(+), 1283 deletions(-) delete mode 100644 kenjutsu/kenjutsu.py delete mode 100644 tests/test_kenjutsu.py diff --git a/kenjutsu/core.py b/kenjutsu/core.py index f695748..f292a3b 100644 --- a/kenjutsu/core.py +++ b/kenjutsu/core.py @@ -1,4 +1,554 @@ +# -*- coding: utf-8 -*- + +""" +The module ``core`` provides support for working with ``slice``\ s. + +=============================================================================== +Overview +=============================================================================== +The module ``core`` provides several functions that are useful for working +with a Python ``slice`` or ``tuple`` of ``slice``\ s. This is of particular +value when working with NumPy_. + +.. _NumPy: http://www.numpy.org/ + +=============================================================================== +API +=============================================================================== +""" + + __author__ = "John Kirkham " __date__ = "$Dec 08, 2016 11:35:58 GMT-0500$" +import collections +import itertools +import numbers +import operator +import math +import warnings + + +def reformat_slice(a_slice, a_length=None): + """ + Takes a slice and reformats it to fill in as many undefined values as + possible. + + Args: + a_slice(slice): a slice to reformat. + + a_length(int): a length to fill for stopping if not + provided. + + Returns: + (slice): a new slice with as many values filled in as + possible. + + Examples: + + >>> reformat_slice(slice(2, -1, None)) + slice(2, -1, 1) + + >>> reformat_slice(slice(2, -1, None), 10) + slice(2, 9, 1) + """ + + new_slice = a_slice + if (new_slice is Ellipsis) or (new_slice == tuple()): + new_slice = slice(None) + elif isinstance(a_slice, numbers.Integral): + if a_slice < 0: + new_slice = slice(a_slice, a_slice-1, -1) + else: + new_slice = slice(a_slice, a_slice+1, 1) + elif isinstance(a_slice, collections.Sequence): + if not all(map(lambda i: isinstance(i, numbers.Integral), a_slice)): + raise ValueError( + "Arbitrary sequences not permitted." + " All elements must be of integral type." + ) + + # Normalize each integer in the range. + new_slice = [] + for i in a_slice: + new_slice.append(reformat_slice(i, a_length)) + return new_slice + elif not isinstance(a_slice, slice): + raise ValueError( + "Expected a `slice` type. Instead got `%s`." % str(a_slice) + ) + + if new_slice.step == 0: + raise ValueError("Slice cannot have a step size of `0`.") + + start = new_slice.start + stop = new_slice.stop + step = new_slice.step + + # Fill unknown values. + if step is None: + step = 1 + if start is None: + if step > 0: + start = 0 + elif step < 0: + start = -1 + if (stop is None) and (step > 0): + stop = a_length + + stop_i = stop is not None + + # Make adjustments for length + if a_length is not None: + # Normalize out-of-bound step sizes. + if step < -a_length: + step = -a_length + elif step > a_length: + step = a_length + + # Normalize bounded negative values. + if -a_length <= start < 0: + start += a_length + if stop_i and (-a_length <= stop < 0): + stop += a_length + + # Handle out-of-bound limits. + if step > 0: + if (start > a_length) or (stop < -a_length): + start = stop = 0 + step = 1 + else: + if start < -a_length: + start = 0 + if stop > a_length: + stop = a_length + elif step < 0: + if (start < -a_length) or (stop_i and stop >= (a_length - 1)): + start = stop = 0 + step = 1 + else: + if start >= a_length: + start = a_length - 1 + if stop_i and stop < -a_length: + stop = None + stop_i = False + + # Catch some known empty slices. + if stop_i and (start == stop): + start = stop = 0 + step = 1 + elif (step > 0) and (stop == 0): + start = stop = 0 + step = 1 + elif (step < 0) and (stop == -1): + start = stop = 0 + step = 1 + elif stop_i and (start >= 0) and (stop >= 0): + if (step > 0) and (start > stop): + start = stop = 0 + step = 1 + elif (step < 0) and (start < stop): + start = stop = 0 + step = 1 + + new_slice = slice(start, stop, step) + if isinstance(a_slice, numbers.Integral): + if new_slice.start == new_slice.stop == 0: + raise IndexError("Index out of range.") + new_slice = new_slice.start + + return(new_slice) + + +def reformat_slices(slices, lengths=None): + """ + Takes a tuple of slices and reformats them to fill in as many undefined + values as possible. + + Args: + slices(tuple(slice)): a tuple of slices to reformat. + lengths(tuple(int)): a tuple of lengths to fill. + + Returns: + (slice): a tuple of slices with all default + values filled if possible. + + Examples: + + >>> reformat_slices( + ... ( + ... slice(None), + ... slice(3, None), + ... slice(None, 5), + ... slice(None, None, 2) + ... ), + ... (10, 13, 15, 20) + ... ) + (slice(0, 10, 1), slice(3, 13, 1), slice(0, 5, 1), slice(0, 20, 2)) + """ + + new_slices = slices + if new_slices == tuple(): + new_slices = Ellipsis + + try: + len(new_slices) + except TypeError: + new_slices = (new_slices,) + + new_lengths = lengths + try: + if new_lengths is not None: + len(new_lengths) + except TypeError: + new_lengths = (new_lengths,) + + el_idx = None + try: + el_idx = new_slices.index(Ellipsis) + except ValueError: + pass + + if new_lengths is not None and el_idx is None: + if len(new_slices) != len(new_lengths): + raise ValueError("Shape must be the same as the number of slices.") + elif new_lengths is not None: + if (len(new_slices) - 1) > len(new_lengths): + raise ValueError( + "Shape must be as large or larger than the number of slices" + " without the Ellipsis." + ) + + if el_idx is not None: + # Break into three cases. + # + # 1. Before the Ellipsis + # 2. The Ellipsis + # 3. After the Ellipsis + # + # Cases 1 and 3 are trivially solved as before. + # Case 2 is either a no-op or a bunch of `slice(None)`s. + # + # The result is a combination of all of these. + + slices_before = new_slices[:el_idx] + slices_after = new_slices[el_idx+1:] + + if Ellipsis in slices_before or Ellipsis in slices_after: + raise ValueError("Only one Ellipsis is permitted. Found multiple.") + + new_lengths_before = None + new_lengths_after = None + slice_el = (Ellipsis,) + if new_lengths is not None: + pos_before = len(slices_before) + pos_after = len(new_lengths) - len(slices_after) + + new_lengths_before = new_lengths[:pos_before] + new_lengths_after = new_lengths[pos_after:] + + new_lengths_el = new_lengths[pos_before:pos_after] + slice_el = len(new_lengths_el) * (slice(None),) + if slice_el: + slice_el = reformat_slices( + slice_el, + new_lengths_el + ) + + if slices_before: + slices_before = reformat_slices(slices_before, new_lengths_before) + if slices_after: + slices_after = reformat_slices(slices_after, new_lengths_after) + + new_slices = slices_before + slice_el + slices_after + else: + if new_lengths is None: + new_lengths = [None] * len(new_slices) + + new_slices = list(new_slices) + for i, each_length in enumerate(new_lengths): + new_slices[i] = reformat_slice(new_slices[i], each_length) + + new_slices = tuple(new_slices) + + return(new_slices) + + +class UnknownSliceLengthException(Exception): + """ + Raised if a slice does not have a known length. + """ + + pass + + +def len_slice(a_slice, a_length=None): + """ + Determines how many elements a slice will contain. + + Raises: + UnknownSliceLengthException: Will raise an exception if + a_slice.stop and a_length is None. + + Args: + a_slice(slice): a slice to reformat. + + a_length(int): a length to fill for stopping if not + provided. + + Returns: + (slice): a new slice with as many values filled in as + possible. + + Examples: + + >>> len_slice(slice(2, None), 10) + 8 + + >>> len_slice(slice(2, 6)) + 4 + """ + + if isinstance(a_slice, numbers.Integral): + raise TypeError( + "An integral index does not provide an object with a length." + ) + + new_slice = reformat_slice(a_slice, a_length) + + new_slice_size = 0 + if isinstance(new_slice, slice): + if new_slice.stop is None: + if new_slice.step > 0: + raise UnknownSliceLengthException( + "Cannot determine slice length without a defined end" + " point. The reformatted slice was %s." % repr(new_slice) + ) + else: + new_slice = slice(new_slice.start, -1, new_slice.step) + + new_slice_diff = float(new_slice.stop - new_slice.start) + + new_slice_size = int(math.ceil(new_slice_diff / new_slice.step)) + else: + new_slice_size = len(new_slice) + + return(new_slice_size) + + +def len_slices(slices, lengths=None): + """ + Takes a tuple of slices and reformats them to fill in as many undefined + values as possible. + + Args: + slices(tuple(slice)): a tuple of slices to reformat. + lengths(tuple(int)): a tuple of lengths to fill. + + Returns: + (slice): a tuple of slices with all default + values filled if possible. + + Examples: + + >>> len_slices( + ... ( + ... slice(None), + ... slice(3, None), + ... slice(None, 5), + ... slice(None, None, 2) + ... ), + ... (10, 13, 15, 20) + ... ) + (10, 10, 5, 10) + """ + + new_slices = reformat_slices(slices, lengths) + + lens = [] + + for each_slice in new_slices: + if not isinstance(each_slice, numbers.Integral): + lens.append(len_slice(each_slice)) + + lens = tuple(lens) + + return(lens) + + +def split_blocks(space_shape, block_shape, block_halo=None): + """ + Return a list of slicings to cut each block out of an array or other. + + Takes an array with ``space_shape`` and ``block_shape`` for every + dimension and a ``block_halo`` to extend each block on each side. From + this, it can compute slicings to use for cutting each block out from + the original array, HDF5 dataset or other. + + Note: + Blocks on the boundary that cannot extend the full range will + be truncated to the largest block that will fit. This will raise + a warning, which can be converted to an exception, if needed. + + Args: + space_shape(tuple): Shape of array to slice + block_shape(tuple): Size of each block to take + block_halo(tuple): Halo to tack on to each block + + Returns: + collections.Sequence of \ + tuples of slices: Provides tuples of slices for \ + retrieving blocks. + + Examples: + + >>> split_blocks( + ... (2, 3,), (1, 1,), (1, 1,) + ... ) #doctest: +NORMALIZE_WHITESPACE + ([(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + + [(slice(0, 2, 1), slice(0, 2, 1)), + (slice(0, 2, 1), slice(0, 3, 1)), + (slice(0, 2, 1), slice(1, 3, 1)), + (slice(0, 2, 1), slice(0, 2, 1)), + (slice(0, 2, 1), slice(0, 3, 1)), + (slice(0, 2, 1), slice(1, 3, 1))], + + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(1, 2, 1))]) + + """ + + try: + irange = xrange + except NameError: + irange = range + + try: + from itertools import ifilter, imap + except ImportError: + ifilter, imap = filter, map + + if block_halo is not None: + if not (len(space_shape) == len(block_shape) == len(block_halo)): + raise ValueError( + "The dimensions of `space_shape`, `block_shape`, and" + " `block_halo` should be the same." + ) + else: + if not (len(space_shape) == len(block_shape)): + raise ValueError( + "The dimensions of `space_shape` and `block_shape` should be" + " the same." + ) + + block_halo = tuple() + for i in irange(len(space_shape)): + block_halo += (0,) + + vec_add = lambda a, b: imap(operator.add, a, b) + vec_sub = lambda a, b: imap(operator.sub, a, b) + + vec_mul = lambda a, b: imap(operator.mul, a, b) + vec_mod = lambda a, b: imap(operator.mod, a, b) + + vec_nonzero = lambda a: \ + imap(lambda _: _[0], ifilter(lambda _: _[1], enumerate(a))) + vec_str = lambda a: imap(str, a) + + vec_clip_floor = lambda a, a_min: \ + imap(lambda _: _ if _ >= a_min else a_min, a) + vec_clip_ceil = lambda a, a_max: \ + imap(lambda _: _ if _ <= a_max else a_max, a) + vec_clip = lambda a, a_min, a_max: \ + vec_clip_ceil(vec_clip_floor(a, a_min), a_max) + + uneven_block_division = tuple(vec_mod(space_shape, block_shape)) + + if any(uneven_block_division): + uneven_block_division_str = vec_nonzero(uneven_block_division) + uneven_block_division_str = vec_str(uneven_block_division_str) + uneven_block_division_str = ", ".join(uneven_block_division_str) + + warnings.warn( + "Blocks will not evenly divide the array." + + " The following dimensions will be unevenly divided: %s." % + uneven_block_division_str, + RuntimeWarning + ) + + ranges_per_dim = [] + haloed_ranges_per_dim = [] + trimmed_halos_per_dim = [] + + for each_dim in irange(len(space_shape)): + # Construct each block using the block size given. Allow to spill over. + if block_shape[each_dim] == -1: + block_shape = (block_shape[:each_dim] + + space_shape[each_dim:each_dim+1] + + block_shape[each_dim+1:]) + + # Generate block ranges. + a_range = [] + for i in irange(2): + offset = i * block_shape[each_dim] + this_range = irange( + offset, + offset + space_shape[each_dim], + block_shape[each_dim] + ) + a_range.append(list(this_range)) + + # Add the halo to each block on both sides. + a_range_haloed = [] + for i in irange(2): + sign = 2 * i - 1 + + haloed = vec_mul( + itertools.repeat(sign, len(a_range[i])), + itertools.repeat(block_halo[each_dim], len(a_range[i])), + ) + haloed = vec_add(a_range[i], haloed) + haloed = vec_clip(haloed, 0, space_shape[each_dim]) + + a_range_haloed.append(list(haloed)) + + # Compute how to trim the halo off of each block. + # Clip each block to the boundaries. + a_trimmed_halo = [] + for i in irange(2): + trimmed = vec_sub(a_range[i], a_range_haloed[0]) + a_trimmed_halo.append(list(trimmed)) + a_range[i] = list(vec_clip(a_range[i], 0, space_shape[each_dim])) + + # Convert all ranges to slices for easier use. + a_range = tuple(imap(slice, *a_range)) + a_range_haloed = tuple(imap(slice, *a_range_haloed)) + a_trimmed_halo = tuple(imap(slice, *a_trimmed_halo)) + + # Format all slices. + a_range = reformat_slices(a_range) + a_range_haloed = reformat_slices(a_range_haloed) + a_trimmed_halo = reformat_slices(a_trimmed_halo) + + # Collect all blocks + ranges_per_dim.append(a_range) + haloed_ranges_per_dim.append(a_range_haloed) + trimmed_halos_per_dim.append(a_trimmed_halo) + + # Take all combinations of all ranges to get blocks. + blocks = list(itertools.product(*ranges_per_dim)) + haloed_blocks = list(itertools.product(*haloed_ranges_per_dim)) + trimmed_halos = list(itertools.product(*trimmed_halos_per_dim)) + + return(blocks, haloed_blocks, trimmed_halos) diff --git a/kenjutsu/kenjutsu.py b/kenjutsu/kenjutsu.py deleted file mode 100644 index 3dc57e1..0000000 --- a/kenjutsu/kenjutsu.py +++ /dev/null @@ -1,554 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -The module ``kenjutsu`` provides support for working with ``slice``\ s. - -=============================================================================== -Overview -=============================================================================== -The module ``kenjutsu`` provides several functions that are useful for working -with a Python ``slice`` or ``tuple`` of ``slice``\ s. This is of particular -value when working with NumPy_. - -.. _NumPy: http://www.numpy.org/ - -=============================================================================== -API -=============================================================================== -""" - - -__author__ = "John Kirkham " -__date__ = "$Sep 08, 2016 15:46:46 EDT$" - - -import collections -import itertools -import numbers -import operator -import math -import warnings - - -def reformat_slice(a_slice, a_length=None): - """ - Takes a slice and reformats it to fill in as many undefined values as - possible. - - Args: - a_slice(slice): a slice to reformat. - - a_length(int): a length to fill for stopping if not - provided. - - Returns: - (slice): a new slice with as many values filled in as - possible. - - Examples: - - >>> reformat_slice(slice(2, -1, None)) - slice(2, -1, 1) - - >>> reformat_slice(slice(2, -1, None), 10) - slice(2, 9, 1) - """ - - new_slice = a_slice - if (new_slice is Ellipsis) or (new_slice == tuple()): - new_slice = slice(None) - elif isinstance(a_slice, numbers.Integral): - if a_slice < 0: - new_slice = slice(a_slice, a_slice-1, -1) - else: - new_slice = slice(a_slice, a_slice+1, 1) - elif isinstance(a_slice, collections.Sequence): - if not all(map(lambda i: isinstance(i, numbers.Integral), a_slice)): - raise ValueError( - "Arbitrary sequences not permitted." - " All elements must be of integral type." - ) - - # Normalize each integer in the range. - new_slice = [] - for i in a_slice: - new_slice.append(reformat_slice(i, a_length)) - return new_slice - elif not isinstance(a_slice, slice): - raise ValueError( - "Expected a `slice` type. Instead got `%s`." % str(a_slice) - ) - - if new_slice.step == 0: - raise ValueError("Slice cannot have a step size of `0`.") - - start = new_slice.start - stop = new_slice.stop - step = new_slice.step - - # Fill unknown values. - if step is None: - step = 1 - if start is None: - if step > 0: - start = 0 - elif step < 0: - start = -1 - if (stop is None) and (step > 0): - stop = a_length - - stop_i = stop is not None - - # Make adjustments for length - if a_length is not None: - # Normalize out-of-bound step sizes. - if step < -a_length: - step = -a_length - elif step > a_length: - step = a_length - - # Normalize bounded negative values. - if -a_length <= start < 0: - start += a_length - if stop_i and (-a_length <= stop < 0): - stop += a_length - - # Handle out-of-bound limits. - if step > 0: - if (start > a_length) or (stop < -a_length): - start = stop = 0 - step = 1 - else: - if start < -a_length: - start = 0 - if stop > a_length: - stop = a_length - elif step < 0: - if (start < -a_length) or (stop_i and stop >= (a_length - 1)): - start = stop = 0 - step = 1 - else: - if start >= a_length: - start = a_length - 1 - if stop_i and stop < -a_length: - stop = None - stop_i = False - - # Catch some known empty slices. - if stop_i and (start == stop): - start = stop = 0 - step = 1 - elif (step > 0) and (stop == 0): - start = stop = 0 - step = 1 - elif (step < 0) and (stop == -1): - start = stop = 0 - step = 1 - elif stop_i and (start >= 0) and (stop >= 0): - if (step > 0) and (start > stop): - start = stop = 0 - step = 1 - elif (step < 0) and (start < stop): - start = stop = 0 - step = 1 - - new_slice = slice(start, stop, step) - if isinstance(a_slice, numbers.Integral): - if new_slice.start == new_slice.stop == 0: - raise IndexError("Index out of range.") - new_slice = new_slice.start - - return(new_slice) - - -def reformat_slices(slices, lengths=None): - """ - Takes a tuple of slices and reformats them to fill in as many undefined - values as possible. - - Args: - slices(tuple(slice)): a tuple of slices to reformat. - lengths(tuple(int)): a tuple of lengths to fill. - - Returns: - (slice): a tuple of slices with all default - values filled if possible. - - Examples: - - >>> reformat_slices( - ... ( - ... slice(None), - ... slice(3, None), - ... slice(None, 5), - ... slice(None, None, 2) - ... ), - ... (10, 13, 15, 20) - ... ) - (slice(0, 10, 1), slice(3, 13, 1), slice(0, 5, 1), slice(0, 20, 2)) - """ - - new_slices = slices - if new_slices == tuple(): - new_slices = Ellipsis - - try: - len(new_slices) - except TypeError: - new_slices = (new_slices,) - - new_lengths = lengths - try: - if new_lengths is not None: - len(new_lengths) - except TypeError: - new_lengths = (new_lengths,) - - el_idx = None - try: - el_idx = new_slices.index(Ellipsis) - except ValueError: - pass - - if new_lengths is not None and el_idx is None: - if len(new_slices) != len(new_lengths): - raise ValueError("Shape must be the same as the number of slices.") - elif new_lengths is not None: - if (len(new_slices) - 1) > len(new_lengths): - raise ValueError( - "Shape must be as large or larger than the number of slices" - " without the Ellipsis." - ) - - if el_idx is not None: - # Break into three cases. - # - # 1. Before the Ellipsis - # 2. The Ellipsis - # 3. After the Ellipsis - # - # Cases 1 and 3 are trivially solved as before. - # Case 2 is either a no-op or a bunch of `slice(None)`s. - # - # The result is a combination of all of these. - - slices_before = new_slices[:el_idx] - slices_after = new_slices[el_idx+1:] - - if Ellipsis in slices_before or Ellipsis in slices_after: - raise ValueError("Only one Ellipsis is permitted. Found multiple.") - - new_lengths_before = None - new_lengths_after = None - slice_el = (Ellipsis,) - if new_lengths is not None: - pos_before = len(slices_before) - pos_after = len(new_lengths) - len(slices_after) - - new_lengths_before = new_lengths[:pos_before] - new_lengths_after = new_lengths[pos_after:] - - new_lengths_el = new_lengths[pos_before:pos_after] - slice_el = len(new_lengths_el) * (slice(None),) - if slice_el: - slice_el = reformat_slices( - slice_el, - new_lengths_el - ) - - if slices_before: - slices_before = reformat_slices(slices_before, new_lengths_before) - if slices_after: - slices_after = reformat_slices(slices_after, new_lengths_after) - - new_slices = slices_before + slice_el + slices_after - else: - if new_lengths is None: - new_lengths = [None] * len(new_slices) - - new_slices = list(new_slices) - for i, each_length in enumerate(new_lengths): - new_slices[i] = reformat_slice(new_slices[i], each_length) - - new_slices = tuple(new_slices) - - return(new_slices) - - -class UnknownSliceLengthException(Exception): - """ - Raised if a slice does not have a known length. - """ - - pass - - -def len_slice(a_slice, a_length=None): - """ - Determines how many elements a slice will contain. - - Raises: - UnknownSliceLengthException: Will raise an exception if - a_slice.stop and a_length is None. - - Args: - a_slice(slice): a slice to reformat. - - a_length(int): a length to fill for stopping if not - provided. - - Returns: - (slice): a new slice with as many values filled in as - possible. - - Examples: - - >>> len_slice(slice(2, None), 10) - 8 - - >>> len_slice(slice(2, 6)) - 4 - """ - - if isinstance(a_slice, numbers.Integral): - raise TypeError( - "An integral index does not provide an object with a length." - ) - - new_slice = reformat_slice(a_slice, a_length) - - new_slice_size = 0 - if isinstance(new_slice, slice): - if new_slice.stop is None: - if new_slice.step > 0: - raise UnknownSliceLengthException( - "Cannot determine slice length without a defined end" - " point. The reformatted slice was %s." % repr(new_slice) - ) - else: - new_slice = slice(new_slice.start, -1, new_slice.step) - - new_slice_diff = float(new_slice.stop - new_slice.start) - - new_slice_size = int(math.ceil(new_slice_diff / new_slice.step)) - else: - new_slice_size = len(new_slice) - - return(new_slice_size) - - -def len_slices(slices, lengths=None): - """ - Takes a tuple of slices and reformats them to fill in as many undefined - values as possible. - - Args: - slices(tuple(slice)): a tuple of slices to reformat. - lengths(tuple(int)): a tuple of lengths to fill. - - Returns: - (slice): a tuple of slices with all default - values filled if possible. - - Examples: - - >>> len_slices( - ... ( - ... slice(None), - ... slice(3, None), - ... slice(None, 5), - ... slice(None, None, 2) - ... ), - ... (10, 13, 15, 20) - ... ) - (10, 10, 5, 10) - """ - - new_slices = reformat_slices(slices, lengths) - - lens = [] - - for each_slice in new_slices: - if not isinstance(each_slice, numbers.Integral): - lens.append(len_slice(each_slice)) - - lens = tuple(lens) - - return(lens) - - -def split_blocks(space_shape, block_shape, block_halo=None): - """ - Return a list of slicings to cut each block out of an array or other. - - Takes an array with ``space_shape`` and ``block_shape`` for every - dimension and a ``block_halo`` to extend each block on each side. From - this, it can compute slicings to use for cutting each block out from - the original array, HDF5 dataset or other. - - Note: - Blocks on the boundary that cannot extend the full range will - be truncated to the largest block that will fit. This will raise - a warning, which can be converted to an exception, if needed. - - Args: - space_shape(tuple): Shape of array to slice - block_shape(tuple): Size of each block to take - block_halo(tuple): Halo to tack on to each block - - Returns: - collections.Sequence of \ - tuples of slices: Provides tuples of slices for \ - retrieving blocks. - - Examples: - - >>> split_blocks( - ... (2, 3,), (1, 1,), (1, 1,) - ... ) #doctest: +NORMALIZE_WHITESPACE - ([(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(1, 2, 1)), - (slice(0, 1, 1), slice(2, 3, 1)), - (slice(1, 2, 1), slice(0, 1, 1)), - (slice(1, 2, 1), slice(1, 2, 1)), - (slice(1, 2, 1), slice(2, 3, 1))], - - [(slice(0, 2, 1), slice(0, 2, 1)), - (slice(0, 2, 1), slice(0, 3, 1)), - (slice(0, 2, 1), slice(1, 3, 1)), - (slice(0, 2, 1), slice(0, 2, 1)), - (slice(0, 2, 1), slice(0, 3, 1)), - (slice(0, 2, 1), slice(1, 3, 1))], - - [(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(1, 2, 1)), - (slice(0, 1, 1), slice(1, 2, 1)), - (slice(1, 2, 1), slice(0, 1, 1)), - (slice(1, 2, 1), slice(1, 2, 1)), - (slice(1, 2, 1), slice(1, 2, 1))]) - - """ - - try: - irange = xrange - except NameError: - irange = range - - try: - from itertools import ifilter, imap - except ImportError: - ifilter, imap = filter, map - - if block_halo is not None: - if not (len(space_shape) == len(block_shape) == len(block_halo)): - raise ValueError( - "The dimensions of `space_shape`, `block_shape`, and" - " `block_halo` should be the same." - ) - else: - if not (len(space_shape) == len(block_shape)): - raise ValueError( - "The dimensions of `space_shape` and `block_shape` should be" - " the same." - ) - - block_halo = tuple() - for i in irange(len(space_shape)): - block_halo += (0,) - - vec_add = lambda a, b: imap(operator.add, a, b) - vec_sub = lambda a, b: imap(operator.sub, a, b) - - vec_mul = lambda a, b: imap(operator.mul, a, b) - vec_mod = lambda a, b: imap(operator.mod, a, b) - - vec_nonzero = lambda a: \ - imap(lambda _: _[0], ifilter(lambda _: _[1], enumerate(a))) - vec_str = lambda a: imap(str, a) - - vec_clip_floor = lambda a, a_min: \ - imap(lambda _: _ if _ >= a_min else a_min, a) - vec_clip_ceil = lambda a, a_max: \ - imap(lambda _: _ if _ <= a_max else a_max, a) - vec_clip = lambda a, a_min, a_max: \ - vec_clip_ceil(vec_clip_floor(a, a_min), a_max) - - uneven_block_division = tuple(vec_mod(space_shape, block_shape)) - - if any(uneven_block_division): - uneven_block_division_str = vec_nonzero(uneven_block_division) - uneven_block_division_str = vec_str(uneven_block_division_str) - uneven_block_division_str = ", ".join(uneven_block_division_str) - - warnings.warn( - "Blocks will not evenly divide the array." + - " The following dimensions will be unevenly divided: %s." % - uneven_block_division_str, - RuntimeWarning - ) - - ranges_per_dim = [] - haloed_ranges_per_dim = [] - trimmed_halos_per_dim = [] - - for each_dim in irange(len(space_shape)): - # Construct each block using the block size given. Allow to spill over. - if block_shape[each_dim] == -1: - block_shape = (block_shape[:each_dim] + - space_shape[each_dim:each_dim+1] + - block_shape[each_dim+1:]) - - # Generate block ranges. - a_range = [] - for i in irange(2): - offset = i * block_shape[each_dim] - this_range = irange( - offset, - offset + space_shape[each_dim], - block_shape[each_dim] - ) - a_range.append(list(this_range)) - - # Add the halo to each block on both sides. - a_range_haloed = [] - for i in irange(2): - sign = 2 * i - 1 - - haloed = vec_mul( - itertools.repeat(sign, len(a_range[i])), - itertools.repeat(block_halo[each_dim], len(a_range[i])), - ) - haloed = vec_add(a_range[i], haloed) - haloed = vec_clip(haloed, 0, space_shape[each_dim]) - - a_range_haloed.append(list(haloed)) - - # Compute how to trim the halo off of each block. - # Clip each block to the boundaries. - a_trimmed_halo = [] - for i in irange(2): - trimmed = vec_sub(a_range[i], a_range_haloed[0]) - a_trimmed_halo.append(list(trimmed)) - a_range[i] = list(vec_clip(a_range[i], 0, space_shape[each_dim])) - - # Convert all ranges to slices for easier use. - a_range = tuple(imap(slice, *a_range)) - a_range_haloed = tuple(imap(slice, *a_range_haloed)) - a_trimmed_halo = tuple(imap(slice, *a_trimmed_halo)) - - # Format all slices. - a_range = reformat_slices(a_range) - a_range_haloed = reformat_slices(a_range_haloed) - a_trimmed_halo = reformat_slices(a_trimmed_halo) - - # Collect all blocks - ranges_per_dim.append(a_range) - haloed_ranges_per_dim.append(a_range_haloed) - trimmed_halos_per_dim.append(a_trimmed_halo) - - # Take all combinations of all ranges to get blocks. - blocks = list(itertools.product(*ranges_per_dim)) - haloed_blocks = list(itertools.product(*haloed_ranges_per_dim)) - trimmed_halos = list(itertools.product(*trimmed_halos_per_dim)) - - return(blocks, haloed_blocks, trimmed_halos) diff --git a/tests/test_core.py b/tests/test_core.py index f695748..baf98e2 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,4 +1,732 @@ +#!/usr/bin/env python + +# -*- coding: utf-8 -*- + __author__ = "John Kirkham " __date__ = "$Dec 08, 2016 11:35:58 GMT-0500$" +import doctest +import itertools +import math +import operator +import sys +import unittest + +from kenjutsu import core + + +try: + irange = xrange +except NameError: + irange = range + + +# Load doctests from `core`. +def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(core)) + return tests + + +class TestCore(unittest.TestCase): + def setUp(self): + pass + + + def test_reformat_slice(self): + with self.assertRaises(ValueError) as e: + core.reformat_slice(None) + + self.assertEqual( + str(e.exception), + "Expected a `slice` type. Instead got `None`." + ) + + with self.assertRaises(ValueError) as e: + core.reformat_slice(slice(None, None, 0)) + + self.assertEqual( + str(e.exception), + "Slice cannot have a step size of `0`." + ) + + with self.assertRaises(ValueError) as e: + core.reformat_slice([None]) + + self.assertEqual( + str(e.exception), + "Arbitrary sequences not permitted." + " All elements must be of integral type." + ) + + for size in [10, 11, 12]: + excess = size + 3 + each_range = range(size) + + for start in itertools.chain([None], irange(-excess, excess)): + for stop in itertools.chain([None], irange(-excess, excess)): + for step in itertools.chain(irange(-excess, excess)): + step = None if step == 0 else step + + a_slice = slice(start, stop, step) + + rf_slice = core.reformat_slice(a_slice) + self.assertEqual( + each_range[a_slice], + each_range[rf_slice] + ) + + rf_slice = core.reformat_slice(a_slice, size) + self.assertEqual( + each_range[a_slice], + each_range[rf_slice] + ) + + new_start = rf_slice.start + new_stop = rf_slice.stop + new_step = rf_slice.step + + if (new_step is not None and + new_step < 0 and + new_stop is None): + new_stop = -1 + + l = float(new_stop - new_start)/float(new_step) + self.assertEqual( + int(math.ceil(l)), + len(each_range[a_slice]) + ) + + a_slice = list() + a_slice.append(0 if start is None else start) + a_slice.append(0 if stop is None else stop) + a_slice.append(0 if step is None else step) + + a_op = operator.itemgetter(*a_slice) + + expected_result = None + try: + expected_result = a_op(each_range) + except IndexError: + pass + + if expected_result is not None: + rf_slice = core.reformat_slice(a_slice) + rf_op = operator.itemgetter(*rf_slice) + self.assertEqual( + expected_result, + rf_op(each_range) + ) + + rf_slice = core.reformat_slice(a_slice, size) + rf_op = operator.itemgetter(*rf_slice) + self.assertEqual( + expected_result, + rf_op(each_range) + ) + else: + core.reformat_slice(a_slice) + with self.assertRaises(IndexError): + core.reformat_slice(a_slice, size) + + if start is not None: + a_slice = start + + expected_result = None + try: + expected_result = each_range[a_slice] + except IndexError: + pass + + if expected_result is not None: + rf_slice = core.reformat_slice(a_slice) + self.assertEqual( + expected_result, + each_range[rf_slice] + ) + + rf_slice = core.reformat_slice(a_slice, size) + self.assertEqual( + expected_result, + each_range[rf_slice] + ) + else: + core.reformat_slice(a_slice) + with self.assertRaises(IndexError): + core.reformat_slice(a_slice, size) + + rf_slice = core.reformat_slice(Ellipsis) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + rf_slice = core.reformat_slice(Ellipsis, size) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + rf_slice = core.reformat_slice(tuple()) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + rf_slice = core.reformat_slice(tuple(), size) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + start = rf_slice.start + stop = rf_slice.stop + step = rf_slice.step + + if step is not None and step < 0 and stop is None: + stop = -1 + + l = float(stop - start)/float(step) + self.assertEqual( + int(math.ceil(l)), + len(each_range[:]) + ) + + + def test_reformat_slices(self): + with self.assertRaises(ValueError) as e: + core.reformat_slices((slice(None),), (1, 2)) + + self.assertEqual( + str(e.exception), + "Shape must be the same as the number of slices." + ) + + with self.assertRaises(ValueError) as e: + core.reformat_slices( + (slice(None), slice(None), Ellipsis), (1,) + ) + + self.assertEqual( + str(e.exception), + "Shape must be as large or larger than the number of slices" + " without the Ellipsis." + ) + + with self.assertRaises(ValueError) as e: + core.reformat_slices( + (Ellipsis, Ellipsis), (1,) + ) + + self.assertEqual( + str(e.exception), + "Only one Ellipsis is permitted. Found multiple." + ) + + rf_slice = core.reformat_slices(slice(None)) + self.assertEqual( + rf_slice, + (slice(0, None, 1),) + ) + + rf_slice = core.reformat_slices((slice(None),)) + self.assertEqual( + rf_slice, + (slice(0, None, 1),) + ) + + rf_slice = core.reformat_slices(Ellipsis) + self.assertEqual( + rf_slice, + (Ellipsis,) + ) + + rf_slice = core.reformat_slices(Ellipsis, 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = core.reformat_slices(tuple()) + self.assertEqual( + rf_slice, + (Ellipsis,) + ) + + rf_slice = core.reformat_slices(tuple(), 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = core.reformat_slices(slice(None), 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = core.reformat_slices((slice(None),), 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = core.reformat_slices(( + -1, + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2) + )) + self.assertEqual( + rf_slice, + ( + -1, + slice(0, None, 1), + slice(3, None, 1), + slice(0, 5, 1), + slice(0, None, 2) + ) + ) + + rf_slice = core.reformat_slices( + ( + -1, + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2), + [-1, -2, -1, 1, 5] + ), + (12, 10, 13, 15, 20, 10) + ) + self.assertEqual( + rf_slice, + ( + 11, + slice(0, 10, 1), + slice(3, 13, 1), + slice(0, 5, 1), + slice(0, 20, 2), + [9, 8, 9, 1, 5] + ) + ) + + rf_slice = core.reformat_slices( + Ellipsis, + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 2, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 5, 1) + ) + ) + + rf_slice = core.reformat_slices( + ( + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 2, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 1, 1) + ) + ) + + rf_slice = core.reformat_slices( + ( + slice(0, 1), + Ellipsis + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 1, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 5, 1) + ) + ) + + rf_slice = core.reformat_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 1, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 1, 1) + ) + ) + + rf_slice = core.reformat_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1), + slice(0, 1), + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 1, 1), + slice(0, 1, 1), + slice(0, 1, 1), + slice(0, 1, 1) + ) + ) + + + def test_len_slice(self): + with self.assertRaises(core.UnknownSliceLengthException): + core.len_slice(slice(None)) + + for size in [10, 11, 12]: + excess = size + 3 + each_range = range(size) + for start in itertools.chain([None], irange(-excess, excess)): + for stop in itertools.chain([None], irange(-excess, excess)): + for step in itertools.chain(irange(-excess, excess)): + step = None if step == 0 else step + + a_slice = slice(start, stop, step) + + l = core.len_slice(a_slice, size) + self.assertEqual( + l, + len(each_range[a_slice]) + ) + + a_slice = list() + a_slice.append(0 if start is None else start) + a_slice.append(0 if stop is None else stop) + a_slice.append(0 if step is None else step) + + a_op = operator.itemgetter(*a_slice) + + expected_result = None + try: + expected_result = a_op(each_range) + except IndexError: + pass + + if expected_result is not None: + l = core.len_slice(a_slice, size) + self.assertEqual(len(expected_result), l) + + if start is not None: + a_slice = start + + with self.assertRaises(TypeError): + core.len_slice(a_slice, size) + + self.assertEqual( + core.len_slice(Ellipsis, size), + len(each_range[:]) + ) + + self.assertEqual( + core.len_slice(tuple(), size), + len(each_range[:]) + ) + + + def test_len_slices(self): + with self.assertRaises(core.UnknownSliceLengthException): + core.len_slices(( + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2) + )) + + l = core.len_slices(Ellipsis, 10) + self.assertEqual( + l, + (10,) + ) + + l = core.len_slices(tuple(), 10) + self.assertEqual( + l, + (10,) + ) + + l = core.len_slices(slice(None), 10) + self.assertEqual( + l, + (10,) + ) + + l = core.len_slices((slice(None),), 10) + self.assertEqual( + l, + (10,) + ) + + l = core.len_slices( + ( + -1, + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2), + [-1, -2, -1, 1, 5] + ), + (12, 10, 13, 15, 20, 10) + ) + self.assertEqual( + l, + (10, 10, 5, 10, 5) + ) + + l = core.len_slices( + Ellipsis, + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (2, 3, 4, 5) + ) + + l = core.len_slices( + ( + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (2, 3, 4, 1) + ) + + l = core.len_slices( + ( + slice(0, 1), + Ellipsis + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 3, 4, 5) + ) + + l = core.len_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 3, 4, 1) + ) + + l = core.len_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1), + slice(0, 1), + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 1, 1, 1) + ) + + + def test_split_blocks(self): + with self.assertRaises(ValueError) as e: + core.split_blocks((1,), (1, 2), (1, 2, 3)) + + self.assertEqual( + str(e.exception), + "The dimensions of `space_shape`, `block_shape`, and `block_halo`" + " should be the same." + ) + + with self.assertRaises(ValueError) as e: + core.split_blocks((1,), (1, 2)) + + self.assertEqual( + str(e.exception), + "The dimensions of `space_shape` and `block_shape` should be the" + " same." + ) + + blocks = core.split_blocks((2,), (1,)) + self.assertEqual( + blocks, + ([(slice(0, 1, 1),), (slice(1, 2, 1),)], + [(slice(0, 1, 1),), (slice(1, 2, 1),)], + [(slice(0, 1, 1),), (slice(0, 1, 1),)]) + ) + + blocks = core.split_blocks((2,), (-1,)) + self.assertEqual( + blocks, + ([(slice(0, 2, 1),)], + [(slice(0, 2, 1),)], + [(slice(0, 2, 1),)]) + ) + + blocks = core.split_blocks((2, 3,), (1, 1,)) + self.assertEqual( + blocks, + ([(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1))]) + ) + + blocks = core.split_blocks((2, 3,), (1, 1,), (0, 0)) + self.assertEqual( + blocks, + ([(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1))]) + ) + + blocks = core.split_blocks((10, 12,), (3, 2,), (4, 3,)) + self.assertEqual( + blocks, + ([(slice(0, 3, 1), slice(0, 2, 1)), + (slice(0, 3, 1), slice(2, 4, 1)), + (slice(0, 3, 1), slice(4, 6, 1)), + (slice(0, 3, 1), slice(6, 8, 1)), + (slice(0, 3, 1), slice(8, 10, 1)), + (slice(0, 3, 1), slice(10, 12, 1)), + (slice(3, 6, 1), slice(0, 2, 1)), + (slice(3, 6, 1), slice(2, 4, 1)), + (slice(3, 6, 1), slice(4, 6, 1)), + (slice(3, 6, 1), slice(6, 8, 1)), + (slice(3, 6, 1), slice(8, 10, 1)), + (slice(3, 6, 1), slice(10, 12, 1)), + (slice(6, 9, 1), slice(0, 2, 1)), + (slice(6, 9, 1), slice(2, 4, 1)), + (slice(6, 9, 1), slice(4, 6, 1)), + (slice(6, 9, 1), slice(6, 8, 1)), + (slice(6, 9, 1), slice(8, 10, 1)), + (slice(6, 9, 1), slice(10, 12, 1)), + (slice(9, 10, 1), slice(0, 2, 1)), + (slice(9, 10, 1), slice(2, 4, 1)), + (slice(9, 10, 1), slice(4, 6, 1)), + (slice(9, 10, 1), slice(6, 8, 1)), + (slice(9, 10, 1), slice(8, 10, 1)), + (slice(9, 10, 1), slice(10, 12, 1))], + [(slice(0, 7, 1), slice(0, 5, 1)), + (slice(0, 7, 1), slice(0, 7, 1)), + (slice(0, 7, 1), slice(1, 9, 1)), + (slice(0, 7, 1), slice(3, 11, 1)), + (slice(0, 7, 1), slice(5, 12, 1)), + (slice(0, 7, 1), slice(7, 12, 1)), + (slice(0, 10, 1), slice(0, 5, 1)), + (slice(0, 10, 1), slice(0, 7, 1)), + (slice(0, 10, 1), slice(1, 9, 1)), + (slice(0, 10, 1), slice(3, 11, 1)), + (slice(0, 10, 1), slice(5, 12, 1)), + (slice(0, 10, 1), slice(7, 12, 1)), + (slice(2, 10, 1), slice(0, 5, 1)), + (slice(2, 10, 1), slice(0, 7, 1)), + (slice(2, 10, 1), slice(1, 9, 1)), + (slice(2, 10, 1), slice(3, 11, 1)), + (slice(2, 10, 1), slice(5, 12, 1)), + (slice(2, 10, 1), slice(7, 12, 1)), + (slice(5, 10, 1), slice(0, 5, 1)), + (slice(5, 10, 1), slice(0, 7, 1)), + (slice(5, 10, 1), slice(1, 9, 1)), + (slice(5, 10, 1), slice(3, 11, 1)), + (slice(5, 10, 1), slice(5, 12, 1)), + (slice(5, 10, 1), slice(7, 12, 1))], + [(slice(0, 3, 1), slice(0, 2, 1)), + (slice(0, 3, 1), slice(2, 4, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(0, 2, 1)), + (slice(3, 6, 1), slice(2, 4, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(0, 2, 1)), + (slice(4, 7, 1), slice(2, 4, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(0, 2, 1)), + (slice(4, 7, 1), slice(2, 4, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1))]) + ) + + + def tearDown(self): + pass + + + +if __name__ == '__main__': + sys.exit(unittest.main()) diff --git a/tests/test_kenjutsu.py b/tests/test_kenjutsu.py deleted file mode 100644 index 2f19ed5..0000000 --- a/tests/test_kenjutsu.py +++ /dev/null @@ -1,729 +0,0 @@ -#!/usr/bin/env python - -# -*- coding: utf-8 -*- - - -import doctest -import itertools -import math -import operator -import sys -import unittest - -from kenjutsu import kenjutsu - - -try: - irange = xrange -except NameError: - irange = range - - -# Load doctests from `kenjutsu`. -def load_tests(loader, tests, ignore): - tests.addTests(doctest.DocTestSuite(kenjutsu)) - return tests - - -class TestKenjutsu(unittest.TestCase): - def setUp(self): - pass - - - def test_reformat_slice(self): - with self.assertRaises(ValueError) as e: - kenjutsu.reformat_slice(None) - - self.assertEqual( - str(e.exception), - "Expected a `slice` type. Instead got `None`." - ) - - with self.assertRaises(ValueError) as e: - kenjutsu.reformat_slice(slice(None, None, 0)) - - self.assertEqual( - str(e.exception), - "Slice cannot have a step size of `0`." - ) - - with self.assertRaises(ValueError) as e: - kenjutsu.reformat_slice([None]) - - self.assertEqual( - str(e.exception), - "Arbitrary sequences not permitted." - " All elements must be of integral type." - ) - - for size in [10, 11, 12]: - excess = size + 3 - each_range = range(size) - - for start in itertools.chain([None], irange(-excess, excess)): - for stop in itertools.chain([None], irange(-excess, excess)): - for step in itertools.chain(irange(-excess, excess)): - step = None if step == 0 else step - - a_slice = slice(start, stop, step) - - rf_slice = kenjutsu.reformat_slice(a_slice) - self.assertEqual( - each_range[a_slice], - each_range[rf_slice] - ) - - rf_slice = kenjutsu.reformat_slice(a_slice, size) - self.assertEqual( - each_range[a_slice], - each_range[rf_slice] - ) - - new_start = rf_slice.start - new_stop = rf_slice.stop - new_step = rf_slice.step - - if (new_step is not None and - new_step < 0 and - new_stop is None): - new_stop = -1 - - l = float(new_stop - new_start)/float(new_step) - self.assertEqual( - int(math.ceil(l)), - len(each_range[a_slice]) - ) - - a_slice = list() - a_slice.append(0 if start is None else start) - a_slice.append(0 if stop is None else stop) - a_slice.append(0 if step is None else step) - - a_op = operator.itemgetter(*a_slice) - - expected_result = None - try: - expected_result = a_op(each_range) - except IndexError: - pass - - if expected_result is not None: - rf_slice = kenjutsu.reformat_slice(a_slice) - rf_op = operator.itemgetter(*rf_slice) - self.assertEqual( - expected_result, - rf_op(each_range) - ) - - rf_slice = kenjutsu.reformat_slice(a_slice, size) - rf_op = operator.itemgetter(*rf_slice) - self.assertEqual( - expected_result, - rf_op(each_range) - ) - else: - kenjutsu.reformat_slice(a_slice) - with self.assertRaises(IndexError): - kenjutsu.reformat_slice(a_slice, size) - - if start is not None: - a_slice = start - - expected_result = None - try: - expected_result = each_range[a_slice] - except IndexError: - pass - - if expected_result is not None: - rf_slice = kenjutsu.reformat_slice(a_slice) - self.assertEqual( - expected_result, - each_range[rf_slice] - ) - - rf_slice = kenjutsu.reformat_slice(a_slice, size) - self.assertEqual( - expected_result, - each_range[rf_slice] - ) - else: - kenjutsu.reformat_slice(a_slice) - with self.assertRaises(IndexError): - kenjutsu.reformat_slice(a_slice, size) - - rf_slice = kenjutsu.reformat_slice(Ellipsis) - self.assertEqual( - each_range[:], - each_range[rf_slice] - ) - - rf_slice = kenjutsu.reformat_slice(Ellipsis, size) - self.assertEqual( - each_range[:], - each_range[rf_slice] - ) - - rf_slice = kenjutsu.reformat_slice(tuple()) - self.assertEqual( - each_range[:], - each_range[rf_slice] - ) - - rf_slice = kenjutsu.reformat_slice(tuple(), size) - self.assertEqual( - each_range[:], - each_range[rf_slice] - ) - - start = rf_slice.start - stop = rf_slice.stop - step = rf_slice.step - - if step is not None and step < 0 and stop is None: - stop = -1 - - l = float(stop - start)/float(step) - self.assertEqual( - int(math.ceil(l)), - len(each_range[:]) - ) - - - def test_reformat_slices(self): - with self.assertRaises(ValueError) as e: - kenjutsu.reformat_slices((slice(None),), (1, 2)) - - self.assertEqual( - str(e.exception), - "Shape must be the same as the number of slices." - ) - - with self.assertRaises(ValueError) as e: - kenjutsu.reformat_slices( - (slice(None), slice(None), Ellipsis), (1,) - ) - - self.assertEqual( - str(e.exception), - "Shape must be as large or larger than the number of slices" - " without the Ellipsis." - ) - - with self.assertRaises(ValueError) as e: - kenjutsu.reformat_slices( - (Ellipsis, Ellipsis), (1,) - ) - - self.assertEqual( - str(e.exception), - "Only one Ellipsis is permitted. Found multiple." - ) - - rf_slice = kenjutsu.reformat_slices(slice(None)) - self.assertEqual( - rf_slice, - (slice(0, None, 1),) - ) - - rf_slice = kenjutsu.reformat_slices((slice(None),)) - self.assertEqual( - rf_slice, - (slice(0, None, 1),) - ) - - rf_slice = kenjutsu.reformat_slices(Ellipsis) - self.assertEqual( - rf_slice, - (Ellipsis,) - ) - - rf_slice = kenjutsu.reformat_slices(Ellipsis, 10) - self.assertEqual( - rf_slice, - (slice(0, 10, 1),) - ) - - rf_slice = kenjutsu.reformat_slices(tuple()) - self.assertEqual( - rf_slice, - (Ellipsis,) - ) - - rf_slice = kenjutsu.reformat_slices(tuple(), 10) - self.assertEqual( - rf_slice, - (slice(0, 10, 1),) - ) - - rf_slice = kenjutsu.reformat_slices(slice(None), 10) - self.assertEqual( - rf_slice, - (slice(0, 10, 1),) - ) - - rf_slice = kenjutsu.reformat_slices((slice(None),), 10) - self.assertEqual( - rf_slice, - (slice(0, 10, 1),) - ) - - rf_slice = kenjutsu.reformat_slices(( - -1, - slice(None), - slice(3, None), - slice(None, 5), - slice(None, None, 2) - )) - self.assertEqual( - rf_slice, - ( - -1, - slice(0, None, 1), - slice(3, None, 1), - slice(0, 5, 1), - slice(0, None, 2) - ) - ) - - rf_slice = kenjutsu.reformat_slices( - ( - -1, - slice(None), - slice(3, None), - slice(None, 5), - slice(None, None, 2), - [-1, -2, -1, 1, 5] - ), - (12, 10, 13, 15, 20, 10) - ) - self.assertEqual( - rf_slice, - ( - 11, - slice(0, 10, 1), - slice(3, 13, 1), - slice(0, 5, 1), - slice(0, 20, 2), - [9, 8, 9, 1, 5] - ) - ) - - rf_slice = kenjutsu.reformat_slices( - Ellipsis, - (2, 3, 4, 5) - ) - self.assertEqual( - rf_slice, - ( - slice(0, 2, 1), - slice(0, 3, 1), - slice(0, 4, 1), - slice(0, 5, 1) - ) - ) - - rf_slice = kenjutsu.reformat_slices( - ( - Ellipsis, - slice(0, 1) - ), - (2, 3, 4, 5) - ) - self.assertEqual( - rf_slice, - ( - slice(0, 2, 1), - slice(0, 3, 1), - slice(0, 4, 1), - slice(0, 1, 1) - ) - ) - - rf_slice = kenjutsu.reformat_slices( - ( - slice(0, 1), - Ellipsis - ), - (2, 3, 4, 5) - ) - self.assertEqual( - rf_slice, - ( - slice(0, 1, 1), - slice(0, 3, 1), - slice(0, 4, 1), - slice(0, 5, 1) - ) - ) - - rf_slice = kenjutsu.reformat_slices( - ( - slice(0, 1), - Ellipsis, - slice(0, 1) - ), - (2, 3, 4, 5) - ) - self.assertEqual( - rf_slice, - ( - slice(0, 1, 1), - slice(0, 3, 1), - slice(0, 4, 1), - slice(0, 1, 1) - ) - ) - - rf_slice = kenjutsu.reformat_slices( - ( - slice(0, 1), - Ellipsis, - slice(0, 1), - slice(0, 1), - slice(0, 1) - ), - (2, 3, 4, 5) - ) - self.assertEqual( - rf_slice, - ( - slice(0, 1, 1), - slice(0, 1, 1), - slice(0, 1, 1), - slice(0, 1, 1) - ) - ) - - - def test_len_slice(self): - with self.assertRaises(kenjutsu.UnknownSliceLengthException): - kenjutsu.len_slice(slice(None)) - - for size in [10, 11, 12]: - excess = size + 3 - each_range = range(size) - for start in itertools.chain([None], irange(-excess, excess)): - for stop in itertools.chain([None], irange(-excess, excess)): - for step in itertools.chain(irange(-excess, excess)): - step = None if step == 0 else step - - a_slice = slice(start, stop, step) - - l = kenjutsu.len_slice(a_slice, size) - self.assertEqual( - l, - len(each_range[a_slice]) - ) - - a_slice = list() - a_slice.append(0 if start is None else start) - a_slice.append(0 if stop is None else stop) - a_slice.append(0 if step is None else step) - - a_op = operator.itemgetter(*a_slice) - - expected_result = None - try: - expected_result = a_op(each_range) - except IndexError: - pass - - if expected_result is not None: - l = kenjutsu.len_slice(a_slice, size) - self.assertEqual(len(expected_result), l) - - if start is not None: - a_slice = start - - with self.assertRaises(TypeError): - kenjutsu.len_slice(a_slice, size) - - self.assertEqual( - kenjutsu.len_slice(Ellipsis, size), - len(each_range[:]) - ) - - self.assertEqual( - kenjutsu.len_slice(tuple(), size), - len(each_range[:]) - ) - - - def test_len_slices(self): - with self.assertRaises(kenjutsu.UnknownSliceLengthException): - kenjutsu.len_slices(( - slice(None), - slice(3, None), - slice(None, 5), - slice(None, None, 2) - )) - - l = kenjutsu.len_slices(Ellipsis, 10) - self.assertEqual( - l, - (10,) - ) - - l = kenjutsu.len_slices(tuple(), 10) - self.assertEqual( - l, - (10,) - ) - - l = kenjutsu.len_slices(slice(None), 10) - self.assertEqual( - l, - (10,) - ) - - l = kenjutsu.len_slices((slice(None),), 10) - self.assertEqual( - l, - (10,) - ) - - l = kenjutsu.len_slices( - ( - -1, - slice(None), - slice(3, None), - slice(None, 5), - slice(None, None, 2), - [-1, -2, -1, 1, 5] - ), - (12, 10, 13, 15, 20, 10) - ) - self.assertEqual( - l, - (10, 10, 5, 10, 5) - ) - - l = kenjutsu.len_slices( - Ellipsis, - (2, 3, 4, 5) - ) - self.assertEqual( - l, - (2, 3, 4, 5) - ) - - l = kenjutsu.len_slices( - ( - Ellipsis, - slice(0, 1) - ), - (2, 3, 4, 5) - ) - self.assertEqual( - l, - (2, 3, 4, 1) - ) - - l = kenjutsu.len_slices( - ( - slice(0, 1), - Ellipsis - ), - (2, 3, 4, 5) - ) - self.assertEqual( - l, - (1, 3, 4, 5) - ) - - l = kenjutsu.len_slices( - ( - slice(0, 1), - Ellipsis, - slice(0, 1) - ), - (2, 3, 4, 5) - ) - self.assertEqual( - l, - (1, 3, 4, 1) - ) - - l = kenjutsu.len_slices( - ( - slice(0, 1), - Ellipsis, - slice(0, 1), - slice(0, 1), - slice(0, 1) - ), - (2, 3, 4, 5) - ) - self.assertEqual( - l, - (1, 1, 1, 1) - ) - - - def test_split_blocks(self): - with self.assertRaises(ValueError) as e: - kenjutsu.split_blocks((1,), (1, 2), (1, 2, 3)) - - self.assertEqual( - str(e.exception), - "The dimensions of `space_shape`, `block_shape`, and `block_halo`" - " should be the same." - ) - - with self.assertRaises(ValueError) as e: - kenjutsu.split_blocks((1,), (1, 2)) - - self.assertEqual( - str(e.exception), - "The dimensions of `space_shape` and `block_shape` should be the" - " same." - ) - - blocks = kenjutsu.split_blocks((2,), (1,)) - self.assertEqual( - blocks, - ([(slice(0, 1, 1),), (slice(1, 2, 1),)], - [(slice(0, 1, 1),), (slice(1, 2, 1),)], - [(slice(0, 1, 1),), (slice(0, 1, 1),)]) - ) - - blocks = kenjutsu.split_blocks((2,), (-1,)) - self.assertEqual( - blocks, - ([(slice(0, 2, 1),)], - [(slice(0, 2, 1),)], - [(slice(0, 2, 1),)]) - ) - - blocks = kenjutsu.split_blocks((2, 3,), (1, 1,)) - self.assertEqual( - blocks, - ([(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(1, 2, 1)), - (slice(0, 1, 1), slice(2, 3, 1)), - (slice(1, 2, 1), slice(0, 1, 1)), - (slice(1, 2, 1), slice(1, 2, 1)), - (slice(1, 2, 1), slice(2, 3, 1))], - [(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(1, 2, 1)), - (slice(0, 1, 1), slice(2, 3, 1)), - (slice(1, 2, 1), slice(0, 1, 1)), - (slice(1, 2, 1), slice(1, 2, 1)), - (slice(1, 2, 1), slice(2, 3, 1))], - [(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1))]) - ) - - blocks = kenjutsu.split_blocks((2, 3,), (1, 1,), (0, 0)) - self.assertEqual( - blocks, - ([(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(1, 2, 1)), - (slice(0, 1, 1), slice(2, 3, 1)), - (slice(1, 2, 1), slice(0, 1, 1)), - (slice(1, 2, 1), slice(1, 2, 1)), - (slice(1, 2, 1), slice(2, 3, 1))], - [(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(1, 2, 1)), - (slice(0, 1, 1), slice(2, 3, 1)), - (slice(1, 2, 1), slice(0, 1, 1)), - (slice(1, 2, 1), slice(1, 2, 1)), - (slice(1, 2, 1), slice(2, 3, 1))], - [(slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1)), - (slice(0, 1, 1), slice(0, 1, 1))]) - ) - - blocks = kenjutsu.split_blocks((10, 12,), (3, 2,), (4, 3,)) - self.assertEqual( - blocks, - ([(slice(0, 3, 1), slice(0, 2, 1)), - (slice(0, 3, 1), slice(2, 4, 1)), - (slice(0, 3, 1), slice(4, 6, 1)), - (slice(0, 3, 1), slice(6, 8, 1)), - (slice(0, 3, 1), slice(8, 10, 1)), - (slice(0, 3, 1), slice(10, 12, 1)), - (slice(3, 6, 1), slice(0, 2, 1)), - (slice(3, 6, 1), slice(2, 4, 1)), - (slice(3, 6, 1), slice(4, 6, 1)), - (slice(3, 6, 1), slice(6, 8, 1)), - (slice(3, 6, 1), slice(8, 10, 1)), - (slice(3, 6, 1), slice(10, 12, 1)), - (slice(6, 9, 1), slice(0, 2, 1)), - (slice(6, 9, 1), slice(2, 4, 1)), - (slice(6, 9, 1), slice(4, 6, 1)), - (slice(6, 9, 1), slice(6, 8, 1)), - (slice(6, 9, 1), slice(8, 10, 1)), - (slice(6, 9, 1), slice(10, 12, 1)), - (slice(9, 10, 1), slice(0, 2, 1)), - (slice(9, 10, 1), slice(2, 4, 1)), - (slice(9, 10, 1), slice(4, 6, 1)), - (slice(9, 10, 1), slice(6, 8, 1)), - (slice(9, 10, 1), slice(8, 10, 1)), - (slice(9, 10, 1), slice(10, 12, 1))], - [(slice(0, 7, 1), slice(0, 5, 1)), - (slice(0, 7, 1), slice(0, 7, 1)), - (slice(0, 7, 1), slice(1, 9, 1)), - (slice(0, 7, 1), slice(3, 11, 1)), - (slice(0, 7, 1), slice(5, 12, 1)), - (slice(0, 7, 1), slice(7, 12, 1)), - (slice(0, 10, 1), slice(0, 5, 1)), - (slice(0, 10, 1), slice(0, 7, 1)), - (slice(0, 10, 1), slice(1, 9, 1)), - (slice(0, 10, 1), slice(3, 11, 1)), - (slice(0, 10, 1), slice(5, 12, 1)), - (slice(0, 10, 1), slice(7, 12, 1)), - (slice(2, 10, 1), slice(0, 5, 1)), - (slice(2, 10, 1), slice(0, 7, 1)), - (slice(2, 10, 1), slice(1, 9, 1)), - (slice(2, 10, 1), slice(3, 11, 1)), - (slice(2, 10, 1), slice(5, 12, 1)), - (slice(2, 10, 1), slice(7, 12, 1)), - (slice(5, 10, 1), slice(0, 5, 1)), - (slice(5, 10, 1), slice(0, 7, 1)), - (slice(5, 10, 1), slice(1, 9, 1)), - (slice(5, 10, 1), slice(3, 11, 1)), - (slice(5, 10, 1), slice(5, 12, 1)), - (slice(5, 10, 1), slice(7, 12, 1))], - [(slice(0, 3, 1), slice(0, 2, 1)), - (slice(0, 3, 1), slice(2, 4, 1)), - (slice(0, 3, 1), slice(3, 5, 1)), - (slice(0, 3, 1), slice(3, 5, 1)), - (slice(0, 3, 1), slice(3, 5, 1)), - (slice(0, 3, 1), slice(3, 5, 1)), - (slice(3, 6, 1), slice(0, 2, 1)), - (slice(3, 6, 1), slice(2, 4, 1)), - (slice(3, 6, 1), slice(3, 5, 1)), - (slice(3, 6, 1), slice(3, 5, 1)), - (slice(3, 6, 1), slice(3, 5, 1)), - (slice(3, 6, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(0, 2, 1)), - (slice(4, 7, 1), slice(2, 4, 1)), - (slice(4, 7, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(0, 2, 1)), - (slice(4, 7, 1), slice(2, 4, 1)), - (slice(4, 7, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(3, 5, 1)), - (slice(4, 7, 1), slice(3, 5, 1))]) - ) - - - def tearDown(self): - pass - - - -if __name__ == '__main__': - sys.exit(unittest.main()) From 8bb081561429668b1c561de85ea5445a2447625a Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Thu, 8 Dec 2016 11:38:44 -0500 Subject: [PATCH 3/4] Re-add stubs for old `kenjutsu.kenjutsu`. --- kenjutsu/kenjutsu.py | 4 ++++ tests/test_kenjutsu.py | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 kenjutsu/kenjutsu.py create mode 100644 tests/test_kenjutsu.py diff --git a/kenjutsu/kenjutsu.py b/kenjutsu/kenjutsu.py new file mode 100644 index 0000000..eb17259 --- /dev/null +++ b/kenjutsu/kenjutsu.py @@ -0,0 +1,4 @@ +__author__ = "John Kirkham " +__date__ = "$Sep 08, 2016 15:46:46 EDT$" + + diff --git a/tests/test_kenjutsu.py b/tests/test_kenjutsu.py new file mode 100644 index 0000000..6752452 --- /dev/null +++ b/tests/test_kenjutsu.py @@ -0,0 +1,4 @@ +__author__ = "John Kirkham " +__date__ = "$Oct 02, 2016 15:38:07 EDT$" + + From 2f03b58ee7168c1387356aebf3dce2e642c953b1 Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Thu, 8 Dec 2016 12:04:42 -0500 Subject: [PATCH 4/4] Add deprecated stubs for back compat. --- kenjutsu/kenjutsu.py | 244 ++++++++++++++ tests/test_kenjutsu.py | 729 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 973 insertions(+) diff --git a/kenjutsu/kenjutsu.py b/kenjutsu/kenjutsu.py index eb17259..b80c7a6 100644 --- a/kenjutsu/kenjutsu.py +++ b/kenjutsu/kenjutsu.py @@ -1,4 +1,248 @@ +""" +The module ``kenjutsu.kenjutsu`` is deprecated. Please use ``kenjutsu.core``. +""" + +from __future__ import absolute_import + + __author__ = "John Kirkham " __date__ = "$Sep 08, 2016 15:46:46 EDT$" +import warnings + + +warnings.warn( + "Please use `kenjutsu.core` instead.", + DeprecationWarning +) + + +def reformat_slice(a_slice, a_length=None): + """ + Takes a slice and reformats it to fill in as many undefined values as + possible. + + Warning: + This function is deprecated. Please use + ``kenjutsu.core.reformat_slice`` instead. + + Args: + a_slice(slice): a slice to reformat. + + a_length(int): a length to fill for stopping if not + provided. + + Returns: + (slice): a new slice with as many values filled in as + possible. + + Examples: + + >>> reformat_slice(slice(2, -1, None)) + slice(2, -1, 1) + + >>> reformat_slice(slice(2, -1, None), 10) + slice(2, 9, 1) + """ + + warnings.warn( + "Please use `kenjutsu.core.reformat_slice` instead.", + DeprecationWarning + ) + + from kenjutsu import core + + return core.reformat_slice(a_slice, a_length) + + +def reformat_slices(slices, lengths=None): + """ + Takes a tuple of slices and reformats them to fill in as many undefined + values as possible. + + Warning: + This function is deprecated. Please use + ``kenjutsu.core.reformat_slices`` instead. + + Args: + slices(tuple(slice)): a tuple of slices to reformat. + lengths(tuple(int)): a tuple of lengths to fill. + + Returns: + (slice): a tuple of slices with all default + values filled if possible. + + Examples: + + >>> reformat_slices( + ... ( + ... slice(None), + ... slice(3, None), + ... slice(None, 5), + ... slice(None, None, 2) + ... ), + ... (10, 13, 15, 20) + ... ) + (slice(0, 10, 1), slice(3, 13, 1), slice(0, 5, 1), slice(0, 20, 2)) + """ + + warnings.warn( + "Please use `kenjutsu.core.reformat_slices` instead.", + DeprecationWarning + ) + + from kenjutsu import core + + return core.reformat_slices(slices, lengths) + + +from kenjutsu.core import UnknownSliceLengthException + + +def len_slice(a_slice, a_length=None): + """ + Determines how many elements a slice will contain. + + Warning: + This function is deprecated. Please use + ``kenjutsu.core.len_slice`` instead. + + Raises: + UnknownSliceLengthException: Will raise an exception if + a_slice.stop and a_length is None. + + Args: + a_slice(slice): a slice to reformat. + + a_length(int): a length to fill for stopping if not + provided. + + Returns: + (slice): a new slice with as many values filled in as + possible. + + Examples: + + >>> len_slice(slice(2, None), 10) + 8 + + >>> len_slice(slice(2, 6)) + 4 + """ + + warnings.warn( + "Please use `kenjutsu.core.len_slice` instead.", + DeprecationWarning + ) + + from kenjutsu import core + + return core.len_slice(a_slice, a_length) + + +def len_slices(slices, lengths=None): + """ + Takes a tuple of slices and reformats them to fill in as many undefined + values as possible. + + Warning: + This function is deprecated. Please use + ``kenjutsu.core.len_slices`` instead. + + Args: + slices(tuple(slice)): a tuple of slices to reformat. + lengths(tuple(int)): a tuple of lengths to fill. + + Returns: + (slice): a tuple of slices with all default + values filled if possible. + + Examples: + + >>> len_slices( + ... ( + ... slice(None), + ... slice(3, None), + ... slice(None, 5), + ... slice(None, None, 2) + ... ), + ... (10, 13, 15, 20) + ... ) + (10, 10, 5, 10) + """ + + warnings.warn( + "Please use `kenjutsu.core.len_slices` instead.", + DeprecationWarning + ) + + from kenjutsu import core + + return core.len_slices(slices, lengths) + + +def split_blocks(space_shape, block_shape, block_halo=None): + """ + Return a list of slicings to cut each block out of an array or other. + + Takes an array with ``space_shape`` and ``block_shape`` for every + dimension and a ``block_halo`` to extend each block on each side. From + this, it can compute slicings to use for cutting each block out from + the original array, HDF5 dataset or other. + + Warning: + This function is deprecated. Please use + ``kenjutsu.core.split_blocks`` instead. + + Note: + Blocks on the boundary that cannot extend the full range will + be truncated to the largest block that will fit. This will raise + a warning, which can be converted to an exception, if needed. + + Args: + space_shape(tuple): Shape of array to slice + block_shape(tuple): Size of each block to take + block_halo(tuple): Halo to tack on to each block + + Returns: + collections.Sequence of \ + tuples of slices: Provides tuples of slices for \ + retrieving blocks. + + Examples: + + >>> split_blocks( + ... (2, 3,), (1, 1,), (1, 1,) + ... ) #doctest: +NORMALIZE_WHITESPACE + ([(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + + [(slice(0, 2, 1), slice(0, 2, 1)), + (slice(0, 2, 1), slice(0, 3, 1)), + (slice(0, 2, 1), slice(1, 3, 1)), + (slice(0, 2, 1), slice(0, 2, 1)), + (slice(0, 2, 1), slice(0, 3, 1)), + (slice(0, 2, 1), slice(1, 3, 1))], + + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(1, 2, 1))]) + + """ + + warnings.warn( + "Please use `kenjutsu.core.split_blocks` instead.", + DeprecationWarning + ) + + from kenjutsu import core + + return core.split_blocks(space_shape, block_shape, block_halo) diff --git a/tests/test_kenjutsu.py b/tests/test_kenjutsu.py index 6752452..48e8b33 100644 --- a/tests/test_kenjutsu.py +++ b/tests/test_kenjutsu.py @@ -1,4 +1,733 @@ +#!/usr/bin/env python + +# -*- coding: utf-8 -*- + + __author__ = "John Kirkham " __date__ = "$Oct 02, 2016 15:38:07 EDT$" +import doctest +import itertools +import math +import operator +import sys +import unittest + +from kenjutsu import kenjutsu + + +try: + irange = xrange +except NameError: + irange = range + + +# Load doctests from `kenjutsu`. +def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(kenjutsu)) + return tests + + +class TestKenjutsu(unittest.TestCase): + def setUp(self): + pass + + + def test_reformat_slice(self): + with self.assertRaises(ValueError) as e: + kenjutsu.reformat_slice(None) + + self.assertEqual( + str(e.exception), + "Expected a `slice` type. Instead got `None`." + ) + + with self.assertRaises(ValueError) as e: + kenjutsu.reformat_slice(slice(None, None, 0)) + + self.assertEqual( + str(e.exception), + "Slice cannot have a step size of `0`." + ) + + with self.assertRaises(ValueError) as e: + kenjutsu.reformat_slice([None]) + + self.assertEqual( + str(e.exception), + "Arbitrary sequences not permitted." + " All elements must be of integral type." + ) + + for size in [10, 11, 12]: + excess = size + 3 + each_range = range(size) + + for start in itertools.chain([None], irange(-excess, excess)): + for stop in itertools.chain([None], irange(-excess, excess)): + for step in itertools.chain(irange(-excess, excess)): + step = None if step == 0 else step + + a_slice = slice(start, stop, step) + + rf_slice = kenjutsu.reformat_slice(a_slice) + self.assertEqual( + each_range[a_slice], + each_range[rf_slice] + ) + + rf_slice = kenjutsu.reformat_slice(a_slice, size) + self.assertEqual( + each_range[a_slice], + each_range[rf_slice] + ) + + new_start = rf_slice.start + new_stop = rf_slice.stop + new_step = rf_slice.step + + if (new_step is not None and + new_step < 0 and + new_stop is None): + new_stop = -1 + + l = float(new_stop - new_start)/float(new_step) + self.assertEqual( + int(math.ceil(l)), + len(each_range[a_slice]) + ) + + a_slice = list() + a_slice.append(0 if start is None else start) + a_slice.append(0 if stop is None else stop) + a_slice.append(0 if step is None else step) + + a_op = operator.itemgetter(*a_slice) + + expected_result = None + try: + expected_result = a_op(each_range) + except IndexError: + pass + + if expected_result is not None: + rf_slice = kenjutsu.reformat_slice(a_slice) + rf_op = operator.itemgetter(*rf_slice) + self.assertEqual( + expected_result, + rf_op(each_range) + ) + + rf_slice = kenjutsu.reformat_slice(a_slice, size) + rf_op = operator.itemgetter(*rf_slice) + self.assertEqual( + expected_result, + rf_op(each_range) + ) + else: + kenjutsu.reformat_slice(a_slice) + with self.assertRaises(IndexError): + kenjutsu.reformat_slice(a_slice, size) + + if start is not None: + a_slice = start + + expected_result = None + try: + expected_result = each_range[a_slice] + except IndexError: + pass + + if expected_result is not None: + rf_slice = kenjutsu.reformat_slice(a_slice) + self.assertEqual( + expected_result, + each_range[rf_slice] + ) + + rf_slice = kenjutsu.reformat_slice(a_slice, size) + self.assertEqual( + expected_result, + each_range[rf_slice] + ) + else: + kenjutsu.reformat_slice(a_slice) + with self.assertRaises(IndexError): + kenjutsu.reformat_slice(a_slice, size) + + rf_slice = kenjutsu.reformat_slice(Ellipsis) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + rf_slice = kenjutsu.reformat_slice(Ellipsis, size) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + rf_slice = kenjutsu.reformat_slice(tuple()) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + rf_slice = kenjutsu.reformat_slice(tuple(), size) + self.assertEqual( + each_range[:], + each_range[rf_slice] + ) + + start = rf_slice.start + stop = rf_slice.stop + step = rf_slice.step + + if step is not None and step < 0 and stop is None: + stop = -1 + + l = float(stop - start)/float(step) + self.assertEqual( + int(math.ceil(l)), + len(each_range[:]) + ) + + + def test_reformat_slices(self): + with self.assertRaises(ValueError) as e: + kenjutsu.reformat_slices((slice(None),), (1, 2)) + + self.assertEqual( + str(e.exception), + "Shape must be the same as the number of slices." + ) + + with self.assertRaises(ValueError) as e: + kenjutsu.reformat_slices( + (slice(None), slice(None), Ellipsis), (1,) + ) + + self.assertEqual( + str(e.exception), + "Shape must be as large or larger than the number of slices" + " without the Ellipsis." + ) + + with self.assertRaises(ValueError) as e: + kenjutsu.reformat_slices( + (Ellipsis, Ellipsis), (1,) + ) + + self.assertEqual( + str(e.exception), + "Only one Ellipsis is permitted. Found multiple." + ) + + rf_slice = kenjutsu.reformat_slices(slice(None)) + self.assertEqual( + rf_slice, + (slice(0, None, 1),) + ) + + rf_slice = kenjutsu.reformat_slices((slice(None),)) + self.assertEqual( + rf_slice, + (slice(0, None, 1),) + ) + + rf_slice = kenjutsu.reformat_slices(Ellipsis) + self.assertEqual( + rf_slice, + (Ellipsis,) + ) + + rf_slice = kenjutsu.reformat_slices(Ellipsis, 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = kenjutsu.reformat_slices(tuple()) + self.assertEqual( + rf_slice, + (Ellipsis,) + ) + + rf_slice = kenjutsu.reformat_slices(tuple(), 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = kenjutsu.reformat_slices(slice(None), 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = kenjutsu.reformat_slices((slice(None),), 10) + self.assertEqual( + rf_slice, + (slice(0, 10, 1),) + ) + + rf_slice = kenjutsu.reformat_slices(( + -1, + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2) + )) + self.assertEqual( + rf_slice, + ( + -1, + slice(0, None, 1), + slice(3, None, 1), + slice(0, 5, 1), + slice(0, None, 2) + ) + ) + + rf_slice = kenjutsu.reformat_slices( + ( + -1, + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2), + [-1, -2, -1, 1, 5] + ), + (12, 10, 13, 15, 20, 10) + ) + self.assertEqual( + rf_slice, + ( + 11, + slice(0, 10, 1), + slice(3, 13, 1), + slice(0, 5, 1), + slice(0, 20, 2), + [9, 8, 9, 1, 5] + ) + ) + + rf_slice = kenjutsu.reformat_slices( + Ellipsis, + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 2, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 5, 1) + ) + ) + + rf_slice = kenjutsu.reformat_slices( + ( + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 2, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 1, 1) + ) + ) + + rf_slice = kenjutsu.reformat_slices( + ( + slice(0, 1), + Ellipsis + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 1, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 5, 1) + ) + ) + + rf_slice = kenjutsu.reformat_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 1, 1), + slice(0, 3, 1), + slice(0, 4, 1), + slice(0, 1, 1) + ) + ) + + rf_slice = kenjutsu.reformat_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1), + slice(0, 1), + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + rf_slice, + ( + slice(0, 1, 1), + slice(0, 1, 1), + slice(0, 1, 1), + slice(0, 1, 1) + ) + ) + + + def test_len_slice(self): + with self.assertRaises(kenjutsu.UnknownSliceLengthException): + kenjutsu.len_slice(slice(None)) + + for size in [10, 11, 12]: + excess = size + 3 + each_range = range(size) + for start in itertools.chain([None], irange(-excess, excess)): + for stop in itertools.chain([None], irange(-excess, excess)): + for step in itertools.chain(irange(-excess, excess)): + step = None if step == 0 else step + + a_slice = slice(start, stop, step) + + l = kenjutsu.len_slice(a_slice, size) + self.assertEqual( + l, + len(each_range[a_slice]) + ) + + a_slice = list() + a_slice.append(0 if start is None else start) + a_slice.append(0 if stop is None else stop) + a_slice.append(0 if step is None else step) + + a_op = operator.itemgetter(*a_slice) + + expected_result = None + try: + expected_result = a_op(each_range) + except IndexError: + pass + + if expected_result is not None: + l = kenjutsu.len_slice(a_slice, size) + self.assertEqual(len(expected_result), l) + + if start is not None: + a_slice = start + + with self.assertRaises(TypeError): + kenjutsu.len_slice(a_slice, size) + + self.assertEqual( + kenjutsu.len_slice(Ellipsis, size), + len(each_range[:]) + ) + + self.assertEqual( + kenjutsu.len_slice(tuple(), size), + len(each_range[:]) + ) + + + def test_len_slices(self): + with self.assertRaises(kenjutsu.UnknownSliceLengthException): + kenjutsu.len_slices(( + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2) + )) + + l = kenjutsu.len_slices(Ellipsis, 10) + self.assertEqual( + l, + (10,) + ) + + l = kenjutsu.len_slices(tuple(), 10) + self.assertEqual( + l, + (10,) + ) + + l = kenjutsu.len_slices(slice(None), 10) + self.assertEqual( + l, + (10,) + ) + + l = kenjutsu.len_slices((slice(None),), 10) + self.assertEqual( + l, + (10,) + ) + + l = kenjutsu.len_slices( + ( + -1, + slice(None), + slice(3, None), + slice(None, 5), + slice(None, None, 2), + [-1, -2, -1, 1, 5] + ), + (12, 10, 13, 15, 20, 10) + ) + self.assertEqual( + l, + (10, 10, 5, 10, 5) + ) + + l = kenjutsu.len_slices( + Ellipsis, + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (2, 3, 4, 5) + ) + + l = kenjutsu.len_slices( + ( + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (2, 3, 4, 1) + ) + + l = kenjutsu.len_slices( + ( + slice(0, 1), + Ellipsis + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 3, 4, 5) + ) + + l = kenjutsu.len_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 3, 4, 1) + ) + + l = kenjutsu.len_slices( + ( + slice(0, 1), + Ellipsis, + slice(0, 1), + slice(0, 1), + slice(0, 1) + ), + (2, 3, 4, 5) + ) + self.assertEqual( + l, + (1, 1, 1, 1) + ) + + + def test_split_blocks(self): + with self.assertRaises(ValueError) as e: + kenjutsu.split_blocks((1,), (1, 2), (1, 2, 3)) + + self.assertEqual( + str(e.exception), + "The dimensions of `space_shape`, `block_shape`, and `block_halo`" + " should be the same." + ) + + with self.assertRaises(ValueError) as e: + kenjutsu.split_blocks((1,), (1, 2)) + + self.assertEqual( + str(e.exception), + "The dimensions of `space_shape` and `block_shape` should be the" + " same." + ) + + blocks = kenjutsu.split_blocks((2,), (1,)) + self.assertEqual( + blocks, + ([(slice(0, 1, 1),), (slice(1, 2, 1),)], + [(slice(0, 1, 1),), (slice(1, 2, 1),)], + [(slice(0, 1, 1),), (slice(0, 1, 1),)]) + ) + + blocks = kenjutsu.split_blocks((2,), (-1,)) + self.assertEqual( + blocks, + ([(slice(0, 2, 1),)], + [(slice(0, 2, 1),)], + [(slice(0, 2, 1),)]) + ) + + blocks = kenjutsu.split_blocks((2, 3,), (1, 1,)) + self.assertEqual( + blocks, + ([(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1))]) + ) + + blocks = kenjutsu.split_blocks((2, 3,), (1, 1,), (0, 0)) + self.assertEqual( + blocks, + ([(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(1, 2, 1)), + (slice(0, 1, 1), slice(2, 3, 1)), + (slice(1, 2, 1), slice(0, 1, 1)), + (slice(1, 2, 1), slice(1, 2, 1)), + (slice(1, 2, 1), slice(2, 3, 1))], + [(slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1)), + (slice(0, 1, 1), slice(0, 1, 1))]) + ) + + blocks = kenjutsu.split_blocks((10, 12,), (3, 2,), (4, 3,)) + self.assertEqual( + blocks, + ([(slice(0, 3, 1), slice(0, 2, 1)), + (slice(0, 3, 1), slice(2, 4, 1)), + (slice(0, 3, 1), slice(4, 6, 1)), + (slice(0, 3, 1), slice(6, 8, 1)), + (slice(0, 3, 1), slice(8, 10, 1)), + (slice(0, 3, 1), slice(10, 12, 1)), + (slice(3, 6, 1), slice(0, 2, 1)), + (slice(3, 6, 1), slice(2, 4, 1)), + (slice(3, 6, 1), slice(4, 6, 1)), + (slice(3, 6, 1), slice(6, 8, 1)), + (slice(3, 6, 1), slice(8, 10, 1)), + (slice(3, 6, 1), slice(10, 12, 1)), + (slice(6, 9, 1), slice(0, 2, 1)), + (slice(6, 9, 1), slice(2, 4, 1)), + (slice(6, 9, 1), slice(4, 6, 1)), + (slice(6, 9, 1), slice(6, 8, 1)), + (slice(6, 9, 1), slice(8, 10, 1)), + (slice(6, 9, 1), slice(10, 12, 1)), + (slice(9, 10, 1), slice(0, 2, 1)), + (slice(9, 10, 1), slice(2, 4, 1)), + (slice(9, 10, 1), slice(4, 6, 1)), + (slice(9, 10, 1), slice(6, 8, 1)), + (slice(9, 10, 1), slice(8, 10, 1)), + (slice(9, 10, 1), slice(10, 12, 1))], + [(slice(0, 7, 1), slice(0, 5, 1)), + (slice(0, 7, 1), slice(0, 7, 1)), + (slice(0, 7, 1), slice(1, 9, 1)), + (slice(0, 7, 1), slice(3, 11, 1)), + (slice(0, 7, 1), slice(5, 12, 1)), + (slice(0, 7, 1), slice(7, 12, 1)), + (slice(0, 10, 1), slice(0, 5, 1)), + (slice(0, 10, 1), slice(0, 7, 1)), + (slice(0, 10, 1), slice(1, 9, 1)), + (slice(0, 10, 1), slice(3, 11, 1)), + (slice(0, 10, 1), slice(5, 12, 1)), + (slice(0, 10, 1), slice(7, 12, 1)), + (slice(2, 10, 1), slice(0, 5, 1)), + (slice(2, 10, 1), slice(0, 7, 1)), + (slice(2, 10, 1), slice(1, 9, 1)), + (slice(2, 10, 1), slice(3, 11, 1)), + (slice(2, 10, 1), slice(5, 12, 1)), + (slice(2, 10, 1), slice(7, 12, 1)), + (slice(5, 10, 1), slice(0, 5, 1)), + (slice(5, 10, 1), slice(0, 7, 1)), + (slice(5, 10, 1), slice(1, 9, 1)), + (slice(5, 10, 1), slice(3, 11, 1)), + (slice(5, 10, 1), slice(5, 12, 1)), + (slice(5, 10, 1), slice(7, 12, 1))], + [(slice(0, 3, 1), slice(0, 2, 1)), + (slice(0, 3, 1), slice(2, 4, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(0, 3, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(0, 2, 1)), + (slice(3, 6, 1), slice(2, 4, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(3, 6, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(0, 2, 1)), + (slice(4, 7, 1), slice(2, 4, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(0, 2, 1)), + (slice(4, 7, 1), slice(2, 4, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1)), + (slice(4, 7, 1), slice(3, 5, 1))]) + ) + + + def tearDown(self): + pass + + + +if __name__ == '__main__': + sys.exit(unittest.main())