diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py
index 2dca2218f9a89c1e9b39531e61f2f1155e05bb36..b211765148a7458ddf5230d696d46ce70a42c97b 100644
--- a/pyopencl/__init__.py
+++ b/pyopencl/__init__.py
@@ -285,17 +285,17 @@ def link_program(context, programs, options=[], devices=None):
 
 def _add_functionality():
     cls_to_info_cls = {
-        _cl.Platform: (_cl.Platform.get_info, _cl.platform_info),
-        _cl.Device: (_cl.Device.get_info, _cl.device_info),
-        _cl.Context: (_cl.Context.get_info, _cl.context_info),
-        _cl.CommandQueue: (_cl.CommandQueue.get_info, _cl.command_queue_info),
-        _cl.Event: (_cl.Event.get_info, _cl.event_info),
-        _cl.MemoryObjectHolder: (MemoryObjectHolder.get_info, _cl.mem_info),
-        Image: (_cl.Image.get_image_info, _cl.image_info),
-        Program: (Program.get_info, _cl.program_info),
-        Kernel: (Kernel.get_info, _cl.kernel_info),
-        _cl.Sampler: (Sampler.get_info, _cl.sampler_info),
-        }
+            _cl.Platform: (_cl.Platform.get_info, _cl.platform_info),
+            _cl.Device: (_cl.Device.get_info, _cl.device_info),
+            _cl.Context: (_cl.Context.get_info, _cl.context_info),
+            _cl.CommandQueue: (_cl.CommandQueue.get_info, _cl.command_queue_info),
+            _cl.Event: (_cl.Event.get_info, _cl.event_info),
+            _cl.MemoryObjectHolder: (MemoryObjectHolder.get_info, _cl.mem_info),
+            Image: (_cl.Image.get_image_info, _cl.image_info),
+            Program: (Program.get_info, _cl.program_info),
+            Kernel: (Kernel.get_info, _cl.kernel_info),
+            _cl.Sampler: (Sampler.get_info, _cl.sampler_info),
+            }
 
     def to_string(cls, value, default_format=None):
         for name in dir(cls):
@@ -802,8 +802,6 @@ def create_some_context(interactive=None, answers=None):
 
     if not platforms:
         raise Error("no platforms found")
-    elif len(platforms) == 1:
-        platform, = platforms
     else:
         if not answers:
             cc_print("Choose platform:")
diff --git a/pyopencl/array.py b/pyopencl/array.py
index d00253a4a0f7ad16b836ef23064c7f7236dae9fd..8d29b6c1d74db82d4230cb3f513a41e820bad7a8 100644
--- a/pyopencl/array.py
+++ b/pyopencl/array.py
@@ -1288,20 +1288,118 @@ class Array(object):
             raise TypeError("unexpected keyword arguments: %s"
                     % kwargs.keys())
 
+        if order not in "CF":
+            raise ValueError("order must be either 'C' or 'F'")
+
         # TODO: add more error-checking, perhaps
+
         if isinstance(shape[0], tuple) or isinstance(shape[0], list):
             shape = tuple(shape[0])
 
+        if -1 in shape:
+            shape = list(shape)
+            idx = shape.index(-1)
+            size = -reduce(lambda x, y: x * y, shape, 1)
+            shape[idx] = self.size // size
+            if any(s < 0 for s in shape):
+                raise ValueError("can only specify one unknown dimension")
+            shape = tuple(shape)
+
         if shape == self.shape:
-            return self
+            return self._new_with_changes(
+                    data=self.base_data, offset=self.offset, shape=shape,
+                    strides=self.strides)
 
-        size = reduce(lambda x, y: x * y, shape, 1)
+        import operator
+        size = reduce(operator.mul, shape, 1)
         if size != self.size:
             raise ValueError("total size of new array must be unchanged")
 
+        # {{{ determine reshaped strides
+
+        # copied and translated from
+        # https://github.com/numpy/numpy/blob/4083883228d61a3b571dec640185b5a5d983bf59/numpy/core/src/multiarray/shape.c  # noqa
+
+        newdims = shape
+        newnd = len(newdims)
+
+        # Remove axes with dimension 1 from the old array. They have no effect
+        # but would need special cases since their strides do not matter.
+
+        olddims = []
+        oldstrides = []
+        for oi in range(len(self.shape)):
+            s = self.shape[oi]
+            if s != 1:
+                olddims.append(s)
+                oldstrides.append(self.strides[oi])
+
+        oldnd = len(olddims)
+
+        newstrides = [-1]*len(newdims)
+
+        # oi to oj and ni to nj give the axis ranges currently worked with
+        oi = 0
+        oj = 1
+        ni = 0
+        nj = 1
+        while ni < newnd and oi < oldnd:
+            np = newdims[ni]
+            op = olddims[oi]
+
+            while np != op:
+                if np < op:
+                    # Misses trailing 1s, these are handled later
+                    np *= newdims[nj]
+                    nj += 1
+                else:
+                    op *= olddims[oj]
+                    oj += 1
+
+            # Check whether the original axes can be combined
+            for ok in range(oi, oj-1):
+                if order == "F":
+                    if oldstrides[ok+1] != olddims[ok]*oldstrides[ok]:
+                        raise ValueError("cannot reshape without copy")
+                else:
+                    # C order
+                    if (oldstrides[ok] != olddims[ok+1]*oldstrides[ok+1]):
+                        raise ValueError("cannot reshape without copy")
+
+            # Calculate new strides for all axes currently worked with
+            if order == "F":
+                newstrides[ni] = oldstrides[oi]
+                for nk in xrange(ni+1, nj):
+                    newstrides[nk] = newstrides[nk - 1]*newdims[nk - 1]
+            else:
+                # C order
+                newstrides[nj - 1] = oldstrides[oj - 1]
+                for nk in range(nj-1, ni, -1):
+                    newstrides[nk - 1] = newstrides[nk]*newdims[nk]
+
+            ni = nj
+            nj += 1
+
+            oi = oj
+            oj += 1
+
+        # Set strides corresponding to trailing 1s of the new shape.
+        if ni >= 1:
+            last_stride = newstrides[ni - 1]
+        else:
+            last_stride = self.dtype.itemsize
+
+        if order == "F":
+            last_stride *= newdims[ni - 1]
+
+        for nk in range(ni, len(shape)):
+            newstrides[nk] = last_stride
+
+        # }}}
+
         return self._new_with_changes(
                 data=self.base_data, offset=self.offset, shape=shape,
-                strides=_make_strides(self.dtype.itemsize, shape, order))
+                strides=tuple(newstrides))
 
     def ravel(self):
         """Returns flattened array containing the same data."""
diff --git a/pyopencl/ipython_ext.py b/pyopencl/ipython_ext.py
index 81fbcdf8669f01415966ec183f2677ed0d5f451b..bedbf77f039ba2f04280239ec13d274a10a3e82a 100644
--- a/pyopencl/ipython_ext.py
+++ b/pyopencl/ipython_ext.py
@@ -3,6 +3,7 @@ from __future__ import division
 from IPython.core.magic import (magics_class, Magics, cell_magic, line_magic)
 
 import pyopencl as cl
+import sys
 
 
 def _try_to_utf8(text):
@@ -14,8 +15,10 @@ def _try_to_utf8(text):
 @magics_class
 class PyOpenCLMagics(Magics):
     def _run_kernel(self, kernel, options):
-        kernel = _try_to_utf8(kernel)
-        options = _try_to_utf8(options).strip()
+        if sys.version_info < (3,):
+            kernel = _try_to_utf8(kernel)
+            options = _try_to_utf8(options).strip()
+
         try:
             ctx = self.shell.user_ns["cl_ctx"]
         except KeyError:
@@ -34,37 +37,33 @@ class PyOpenCLMagics(Magics):
             raise RuntimeError("unable to locate cl context, which must be "
                     "present in namespace as 'cl_ctx' or 'ctx'")
 
-        prg = cl.Program(ctx, kernel).build(options=options)
+        prg = cl.Program(ctx, kernel).build(options=options.split())
 
         for knl in prg.all_kernels():
             self.shell.user_ns[knl.function_name] = knl
 
-
     @cell_magic
     def cl_kernel(self, line, cell):
         kernel = cell
 
-        opts, args = self.parse_options(line,'o:')
+        opts, args = self.parse_options(line, 'o:')
         build_options = opts.get('o', '')
 
         self._run_kernel(kernel, build_options)
 
-
     def _load_kernel_and_options(self, line):
-        opts, args = self.parse_options(line,'o:f:')
+        opts, args = self.parse_options(line, 'o:f:')
 
         build_options = opts.get('o')
         kernel = self.shell.find_user_code(opts.get('f') or args)
 
         return kernel, build_options
 
-
     @line_magic
     def cl_kernel_from_file(self, line):
         kernel, build_options = self._load_kernel_and_options(line)
         self._run_kernel(kernel, build_options)
 
-
     @line_magic
     def cl_load_edit_kernel(self, line):
         kernel, build_options = self._load_kernel_and_options(line)
diff --git a/setup.py b/setup.py
index 86a6bdad7e6f570048796aa19d885ab8609e4ff3..b6f26b30f046b0575b27b31531635f307b6d7626 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,14 @@
 #!/usr/bin/env python
 # -*- coding: latin-1 -*-
+from setuptools.command.build_ext import build_ext as _build_ext
+
+class build_ext(_build_ext):
+    def finalize_options(self):
+        _build_ext.finalize_options(self)
+        # Prevent numpy from thinking it is still in its setup process:
+        __builtins__.__NUMPY_SETUP__ = False
+        import numpy
+        self.include_dirs.append(numpy.get_include())
 
 __copyright__ = """
 Copyright (C) 2009-14 Andreas Kloeckner
@@ -222,6 +231,7 @@ def main():
                 ],
 
             install_requires=[
+		"numpy",
                 "pytools>=2014.2",
                 "pytest>=2",
                 "decorator>=3.2.0",
diff --git a/test/test_array.py b/test/test_array.py
index eb29b9b6cac86e134da49293b975f6c2a90644fc..acb82ec8876b0b96867379f9c129a24c3d15ccb9 100644
--- a/test/test_array.py
+++ b/test/test_array.py
@@ -148,7 +148,7 @@ def test_mix_complex(ctx_factory):
 
                     err = la.norm(host_result-dev_result)/la.norm(host_result)
                     print(err)
-                    correct = err < 1e-5
+                    correct = err < 1e-4
                     if not correct:
                         print(host_result)
                         print(dev_result)
@@ -719,6 +719,24 @@ def test_view_and_strides(ctx_factory):
         assert (y.get() == X.get()[:3, :5]).all()
 
 
+def test_meshmode_view(ctx_factory):
+    context = ctx_factory()
+    queue = cl.CommandQueue(context)
+
+    n = 2
+    result = cl.array.empty(queue, (2, n*6), np.float32)
+
+    def view(z):
+        return z[..., n*3:n*6].reshape(z.shape[:-1] + (n, 3))
+
+    result = result.with_queue(queue)
+    result.fill(0)
+    view(result)[0].fill(1)
+    view(result)[1].fill(1)
+    x = result.get()
+    assert (view(x) == 1).all()
+
+
 def test_event_management(ctx_factory):
     context = ctx_factory()
     queue = cl.CommandQueue(context)
@@ -758,6 +776,28 @@ def test_event_management(ctx_factory):
     assert len(x.events) < 100
 
 
+def test_reshape(ctx_factory):
+    context = ctx_factory()
+    queue = cl.CommandQueue(context)
+
+    a = np.arange(128).reshape(8, 16).astype(np.float32)
+    a_dev = cl_array.to_device(queue, a)
+
+    # different ways to specify the shape
+    a_dev.reshape(4, 32)
+    a_dev.reshape((4, 32))
+    a_dev.reshape([4, 32])
+
+    # using -1 as unknown dimension
+    assert a_dev.reshape(-1, 32).shape == (4, 32)
+    assert a_dev.reshape((32, -1)).shape == (32, 4)
+    assert a_dev.reshape(((8, -1, 4))).shape == (8, 4, 4)
+
+    import pytest
+    with pytest.raises(ValueError):
+        a_dev.reshape(-1, -1, 4)
+
+
 if __name__ == "__main__":
     # make sure that import failures get reported, instead of skipping the
     # tests.