Skip to content

Instantly share code, notes, and snippets.

@laben
Created August 24, 2015 07:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laben/fdfe48a4c14ddb26e060 to your computer and use it in GitHub Desktop.
Save laben/fdfe48a4c14ddb26e060 to your computer and use it in GitHub Desktop.
multi sub HYPER(&operator, Iterable:D \left, Iterable:D \right, :$dwim-left, :$dwim-right) {
my @result;
my \lefti := left.iterator;
my \righti := right.iterator;
# Check whether any side is lazy. They must not be to proceed.
if lefti.is-lazy {
X::HyperOp::Infinite.new(:side<both>, :&operator).throw if righti.is-lazy;
X::HyperOp::Infinite.new(:side<left>, :&operator).throw;
}
X::HyperOp::Infinite.new(:side<right>, :&operator).throw if righti.is-lazy;
# Prepare some variables
my $left-ended = False;
my $right-ended = False;
my $left-elems = 0;
my $right-elems = 0;
my $elems = 0;
my \leftb := IterationBuffer.new;
my \rightb := IterationBuffer.new;
# First, we do the non dwimmy results and keep count of elements passed
# We end this loop as soon as the shorter has been cycled through
# TODO Whatever as last elem makes the list infinitely prolungable with the elem before the Whatever
loop {
my \leftv := lefti.pull-one;
my \rightv := righti.pull-one;
$left-ended = leftv =:= IterationEnd;
$right-ended = rightv =:= IterationEnd;
leftb.push(leftv) unless $left-ended;
rightb.push(rightv) unless $right-ended;
last if $left-ended || $right-ended;
@result[$elems++] := HYPER(&operator, leftv, rightv, :$dwim-left, :$dwim-right);
}
$left-elems = $right-elems = $elems;
# If any side was empty and dwimmy (or both are empty), return the empty list
return () if $elems == 0;
# Return now if the dwim side is on the longer one
if ($left-ended && $right-ended) ||
(!$left-ended && $dwim-left && !$dwim-right) ||
(!$right-ended && $dwim-right && !$dwim-left) {
my $type = left.WHAT;
return nqp::iscont(left) ?? $type(|@result.eager).item !! $type(|@result.eager);
}
# Check whether a side is longer than the other and the shorter one is non dwimmy
if (!$left-ended && !$dwim-right) || (!$right-ended && !$dwim-left) {
X::HyperOp::NonDWIM.new(:&operator, :$left-elems, :$right-elems).throw
}
my $dwim-count = 0;
my $dwim-elems = $elems;
# Handle the dangling elem we got form the longer side when the shorter one ended
if $right-ended {
@result[$dwim-elems++] := HYPER(&operator, leftb[*-1], rightb[$dwim-count++], :$dwim-left, :$dwim-right);
}
else { ## $left-ended
@result[$dwim-elems++] := HYPER(&operator, leftb[$dwim-count++], rightb[*-1], :$dwim-left, :$dwim-right);
}
# Get the rest
my $dwim-ended = False;
my \dwim-iter := $right-ended ?? lefti !! righti;
loop {
my \dwimv := dwim-iter.pull-one;
$dwim-ended = dwimv =:= IterationEnd;
last if $dwim-ended;
if $right-ended {
@result[$dwim-elems++] := HYPER(&operator, dwimv, rightb[$dwim-count], :$dwim-left, :$dwim-right);
}
else { ## $left-ended
@result[$dwim-elems++] := HYPER(&operator, leftb[$dwim-count], dwimv, :$dwim-left, :$dwim-right);
}
$dwim-count = ($dwim-count + 1) % $elems;
}
# Coerce to the original type
my $type = left.WHAT;
return nqp::iscont(left) ?? $type(|@result.eager).item !! $type(|@result.eager);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment