Index: DataLoader/data_info.py
===================================================================
--- DataLoader/data_info.py	(revision 579ba85b3ae066f0c232974fcfbc64c128eec44f)
+++ DataLoader/data_info.py	(revision 442f42fc528a8171701cedfb4110b6e4fabe9210)
@@ -81,6 +81,6 @@
     
     def __init__(self, data=None, err_data=None):
-        self.data = data
-        self.err_data = err_data
+        self.data = numpy.asarray(data)
+        self.err_data = numpy.asarray(err_data)
         
     def xaxis(self, label, unit):
@@ -687,2 +687,96 @@
         return _str
   
+    def clone_without_data(self, length=0):
+        """
+            Clone the current object, without copying the data (which
+            will be filled out by a subsequent operation).
+            The data arrays will be initialized to zero.
+            
+            @param length: length of the data array to be initialized
+        """
+        from copy import deepcopy
+        
+        data     = numpy.zeros(length) 
+        err_data = numpy.zeros(length) 
+        
+        clone = Data2D(data, err_data)
+        clone.title       = self.title
+        clone.run         = self.run
+        clone.filename    = self.filename
+        clone.notes       = deepcopy(self.notes) 
+        clone.process     = deepcopy(self.process) 
+        clone.detector    = deepcopy(self.detector) 
+        clone.sample      = deepcopy(self.sample) 
+        clone.source      = deepcopy(self.source) 
+        clone.collimation = deepcopy(self.collimation) 
+        clone.meta_data   = deepcopy(self.meta_data) 
+        clone.errors      = deepcopy(self.errors) 
+        
+        return clone
+  
+  
+    def _validity_check(self, other):
+        """
+            Checks that the data lengths are compatible.
+            Checks that the x vectors are compatible.
+            Returns errors vectors equal to original
+            errors vectors if they were present or vectors
+            of zeros when none was found.
+            
+            @param other: other data set for operation
+            @return: dy for self, dy for other [numpy arrays]
+            @raise ValueError: when lengths are not compatible
+        """
+        err_other = None
+        if isinstance(other, Data2D):
+            # Check that data lengths are the same
+            if numpy.size(self.data) != numpy.size(other.data):
+                raise ValueError, "Unable to perform operation: data length are not equal"
+               
+            # Check that the scales match
+            #TODO: matching scales?     
+            
+            # Check that the other data set has errors, otherwise
+            # create zero vector
+            #TODO: test this
+            err_other = other.err_data
+            if other.err_data==None or (numpy.size(other.err_data) != numpy.size(other.data)):
+                err_other = numpy.zeros([numpy.size(other.data,0), numpy.size(other.data,1)])
+            
+        # Check that we have errors, otherwise create zero vector
+        err = self.err_data
+        if self.err_data==None or (numpy.size(self.err_data) != numpy.size(self.data)):
+            err = numpy.zeros([numpy.size(self.data,0), numpy.size(self.data,1)])
+            
+        return err, err_other
+  
+  
+    def _perform_operation(self, other, operation):
+        """
+            Perform 2D operations between data sets
+            
+            @param other: other data set
+            @param operation: function defining the operation
+        """
+        # First, check the data compatibility
+        dy, dy_other = self._validity_check(other)
+    
+        result = self.clone_without_data([numpy.size(self.data,0), numpy.size(self.data,1)])
+        
+        for i in range(numpy.size(self.data,0)):
+            for j in range(numpy.size(self.data,1)):
+                result.data[i][j] = self.data[i][j]
+                if self.err_data is not None and numpy.size(self.data)==numpy.size(self.err_data):
+                    result.err_data[i][j] = self.err_data[i][j]
+                
+                a = Uncertainty(self.data[i][j], dy[i][j]**2)
+                if isinstance(other, Data2D):
+                    b = Uncertainty(other.data[i][j], dy_other[i][j]**2)
+                else:
+                    b = other
+                
+                output = operation(a, b)
+                result.data[i][j] = output.x
+                result.err_data[i][j] = math.sqrt(math.fabs(output.variance))
+        return result
+    
Index: DataLoader/test/utest_manipulations.py
===================================================================
--- DataLoader/test/utest_manipulations.py	(revision 9198b8310ebd4e2c3eabca485fa5c08200add7cf)
+++ DataLoader/test/utest_manipulations.py	(revision 442f42fc528a8171701cedfb4110b6e4fabe9210)
@@ -2,9 +2,10 @@
     Unit tests for data manipulations
 """
+#TODO: what happens if you add a Data1D to a Data2D?
 
 import unittest
 import numpy, math
 from DataLoader.loader import  Loader
-from DataLoader.data_info import Data1D
+from DataLoader.data_info import Data1D, Data2D
  
 import os.path
@@ -128,4 +129,113 @@
             self.assertEqual(result.dy[i], 6.0*0.5/4.0)
             
+class manip_2D(unittest.TestCase):
+    
+    def setUp(self):
+        # Create two data sets to play with
+        x_0 = 2.0*numpy.ones([5,4])
+        dx_0 = 0.5*numpy.ones([5,4])
+        self.data = Data2D(x_0, dx_0)
+        
+        y = numpy.ones([5,4])
+        dy = numpy.ones([5,4])
+        self.data2 = Data2D(y, dy)
+        
+        
+    def test_load(self):
+        """
+            Test whether the test file was loaded properly
+        """
+        # There should be 5 entries in the file
+        self.assertEqual(numpy.size(self.data.data, 0), 5)
+        self.assertEqual(numpy.size(self.data.data, 1), 4)
+        
+        for i in range(5):
+            for j in range(4):
+                # All y-error values should be 0.5
+                self.assertEqual(self.data.err_data[i][j], 0.5)    
+                
+                # All y values should be 2.0
+                self.assertEqual(self.data.data[i][j], 2.0)    
+        
+    def test_add(self):
+        result = self.data2+self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 3.0)
+                self.assertEqual(result.err_data[i][j], math.sqrt(0.5**2+1.0))
+        
+    def test_sub(self):
+        result = self.data2-self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], -1.0)
+                self.assertEqual(result.err_data[i][j], math.sqrt(0.5**2+1.0))
+        
+    def test_mul(self):
+        result = self.data2*self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 2.0)
+                self.assertEqual(result.err_data[i][j], math.sqrt((0.5*1.0)**2+(1.0*2.0)**2))
+        
+    def test_div(self):
+        result = self.data2/self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 0.5)
+                self.assertEqual(result.err_data[i][j], math.sqrt((1.0/2.0)**2+(0.5*1.0/4.0)**2))
+        
+    def test_radd(self):
+        result = self.data+3.0
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 5.0)
+                self.assertEqual(result.err_data[i][j], 0.5)
+            
+        result = 3.0+self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 5.0)
+                self.assertEqual(result.err_data[i][j], 0.5)
+            
+    def test_rsub(self):
+        result = self.data-3.0
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], -1.0)
+                self.assertEqual(result.err_data[i][j], 0.5)
+            
+        result = 3.0-self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 1.0)
+                self.assertEqual(result.err_data[i][j], 0.5)
+            
+    def test_rmul(self):
+        result = self.data*3.0
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 6.0)
+                self.assertEqual(result.err_data[i][j], 1.5)
+            
+        result = 3.0*self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 6.0)
+                self.assertEqual(result.err_data[i][j], 1.5)
+            
+    def test_rdiv(self):
+        result = self.data/4.0
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 0.5)
+                self.assertEqual(result.err_data[i][j], 0.125)
+            
+        result = 6.0/self.data
+        for i in range(5):
+            for j in range(4):
+                self.assertEqual(result.data[i][j], 3.0)
+                self.assertEqual(result.err_data[i][j], 6.0*0.5/4.0)
+            
 
 if __name__ == '__main__':
