Index: DataLoader/data_info.py
===================================================================
--- DataLoader/data_info.py	(revision 8780e9a7e98f59c06b1819f1e068b7e84f496efc)
+++ DataLoader/data_info.py	(revision 9198b8310ebd4e2c3eabca485fa5c08200add7cf)
@@ -20,4 +20,7 @@
 
 from sans.guitools.plottables import Data1D as plottable_1D
+from data_util.uncertainty import Uncertainty
+import numpy
+import math
 
 class Data2D:
@@ -276,41 +279,5 @@
     ## Loading errors
     errors = []
-    
-    def __add__(self, data):
-        """
-            Add two data sets
-            
-            @param data: data set to add to the current one
-            @return: new data set
-            @raise ValueError: raised when two data sets are incompatible
-        """
-        raise RuntimeError, "DataInfo addition is not implemented yet"
-    
-    def __sub__(self, data):
-        """
-            Subtract two data sets
-            
-            @param data: data set to subtract from the current one
-            @return: new data set
-            @raise ValueError: raised when two data sets are incompatible
-        """
-        raise RuntimeError, "DataInfo subtraction is not implemented yet"
-    
-    def __mul__(self, constant):
-        """
-            Multiply every entry of the current data set by a constant
-            
-            @param constant: constant to multiply the data by
-            @return: new data set
-            @raise ValueError: raised when the constant is not a float
-        """
-        raise RuntimeError, "DataInfo multiplication is not implemented yet"
-    
-    def __div__(self, constant):
-        """
-        """
-        raise RuntimeError, "DataInfo division is not implemented yet"
-    
-        
+            
 class Data1D(plottable_1D, DataInfo):
     """
@@ -351,5 +318,176 @@
         return _str
 
-
-
-
+    def clone_without_data(self, length=0):
+        from copy import deepcopy
+        
+        x  = numpy.zeros(length) 
+        dx = numpy.zeros(length) 
+        y  = numpy.zeros(length) 
+        dy = numpy.zeros(length) 
+        
+        clone = Data1D(x, y, dx=dx, dy=dy)
+        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
+        """
+        dy_other = None
+        if isinstance(other, Data1D):
+            # Check that data lengths are the same
+            if len(self.x) != len(other.x) or \
+                len(self.y) != len(other.y):
+                raise ValueError, "Unable to perform operation: data length are not equal"
+            
+            # Here we could also extrapolate between data points
+            for i in range(len(self.x)):
+                if self.x[i] != other.x[i]:
+                    raise ValueError, "Incompatible data sets: x-values do not match"
+            
+            # Check that the other data set has errors, otherwise
+            # create zero vector
+            dy_other = other.dy
+            if other.dy==None or (len(other.dy) != len(other.y)):
+                dy_other = numpy.zeros(len(other.y))
+            
+        # Check that we have errors, otherwise create zero vector
+        dy = self.dy
+        if self.dy==None or (len(self.dy) != len(self.y)):
+            dy = numpy.zeros(len(self.y))            
+            
+        return dy, dy_other
+
+    def _perform_operation(self, other, operation):
+        """
+        """
+        # First, check the data compatibility
+        dy, dy_other = self._validity_check(other)
+        result = self.clone_without_data(len(self.x))
+        
+        for i in range(len(self.x)):
+            result.x[i] = self.x[i]
+            if self.dx is not None and len(self.x)==len(self.dx):
+                result.dx[i] = self.dx[i]
+            
+            a = Uncertainty(self.y[i], dy[i]**2)
+            if isinstance(other, Data1D):
+                b = Uncertainty(other.y[i], dy_other[i]**2)
+            else:
+                b = other
+            
+            output = operation(a, b)
+            result.y[i] = output.x
+            result.dy[i] = math.sqrt(math.fabs(output.variance))
+        return result
+        
+
+    def __add__(self, other):
+        """
+            Add two data sets
+            
+            @param other: data set to add to the current one
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return a+b
+        return self._perform_operation(other, operation)
+        
+    def __radd__(self, other):
+        """
+            Add two data sets
+            
+            @param other: data set to add to the current one
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return b+a
+        return self._perform_operation(other, operation)
+        
+    def __sub__(self, other):
+        """
+            Subtract two data sets
+            
+            @param other: data set to subtract from the current one
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return a-b
+        return self._perform_operation(other, operation)
+        
+    def __rsub__(self, other):
+        """
+            Subtract two data sets
+            
+            @param other: data set to subtract from the current one
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return b-a
+        return self._perform_operation(other, operation)
+        
+    def __mul__(self, other):
+        """
+            Multiply two data sets
+            
+            @param other: data set to subtract from the current one
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return a*b
+        return self._perform_operation(other, operation)
+        
+    def __rmul__(self, other):
+        """
+            Multiply two data sets
+            
+            @param other: data set to subtract from the current one
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return b*a
+        return self._perform_operation(other, operation)
+        
+    def __div__(self, other):
+        """
+            Divided a data set by another
+            
+            @param other: data set that the current one is divided by
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return a/b
+        return self._perform_operation(other, operation)
+        
+    def __rdiv__(self, other):
+        """
+            Divided a data set by another
+            
+            @param other: data set that the current one is divided by
+            @return: new data set
+            @raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b): return b/a
+        return self._perform_operation(other, operation)
+        
+
+
Index: DataLoader/release_notes.txt
===================================================================
--- DataLoader/release_notes.txt	(revision 9198b8310ebd4e2c3eabca485fa5c08200add7cf)
+++ DataLoader/release_notes.txt	(revision 9198b8310ebd4e2c3eabca485fa5c08200add7cf)
@@ -0,0 +1,50 @@
+Release Notes
+=============
+
+Package name: DataLoader 0.2
+
+1- 	Version 0.1
+	- Release date: ?
+	- First version of SANS data loader
+	- Formats: IGOR 1D, IGOR 2D, CanSAS 1D, 2- or 3-column ascii
+	- Includes SANS data class, with arithmetic operations
+	
+2- Downloading and Installing
+
+	2.1- System Requirements:
+		- Python version >= 2.4 should be running on the system
+
+	2.2- Installing:
+		- Get the code from svn://danse.us/sans/releases/DataLoader-0.1
+		- The following modules are required:
+			* numpy
+			* data_util from svn://danse.us/common/releases/data_util-0.1
+
+3- Known Issues
+
+	3.1- All systems:
+		- None
+
+	3.2- Windows:
+		- None
+
+	3.3- Linux:
+		- None
+
+4- Troubleshooting
+
+	- None
+
+5- Frequently Asked Questions
+
+	- None
+
+6- Other Resources
+
+	- None
+
+	
+
+	
+
+
Index: DataLoader/test/utest_manipulations.py
===================================================================
--- DataLoader/test/utest_manipulations.py	(revision 9198b8310ebd4e2c3eabca485fa5c08200add7cf)
+++ DataLoader/test/utest_manipulations.py	(revision 9198b8310ebd4e2c3eabca485fa5c08200add7cf)
@@ -0,0 +1,133 @@
+"""
+    Unit tests for data manipulations
+"""
+
+import unittest
+import numpy, math
+from DataLoader.loader import  Loader
+from DataLoader.data_info import Data1D
+ 
+import os.path
+
+class data_info_tests(unittest.TestCase):
+    
+    def setUp(self):
+        self.data = Loader().load("cansas1d.xml")
+        
+    def test_clone1D(self):
+        """
+            Test basic cloning
+        """
+        clone = self.data.clone_without_data()
+        
+        for i in range(len(self.data.detector)):
+            self.assertEqual(self.data.detector[i].distance, clone.detector[i].distance)
+            
+
+class manip_tests(unittest.TestCase):
+    
+    def setUp(self):
+        # Create two data sets to play with
+        x_0 = numpy.ones(5)
+        for i in range(5):
+            x_0[i] = x_0[i]*(i+1.0)
+            
+        y_0 = 2.0*numpy.ones(5)
+        dy_0 = 0.5*numpy.ones(5)
+        self.data = Data1D(x_0, y_0, dy=dy_0)
+        
+        x = self.data.x
+        y = numpy.ones(5)
+        dy = numpy.ones(5)
+        self.data2 = Data1D(x, y, dy=dy)
+        
+        
+    def test_load(self):
+        """
+            Test whether the test file was loaded properly
+        """
+        # There should be 5 entries in the file
+        self.assertEqual(len(self.data.x), 5)
+        
+        for i in range(5):
+            # The x values should be from 1 to 5
+            self.assertEqual(self.data.x[i], float(i+1))
+        
+            # All y-error values should be 0.5
+            self.assertEqual(self.data.dy[i], 0.5)    
+            
+            # All y values should be 2.0
+            self.assertEqual(self.data.y[i], 2.0)    
+        
+    def test_add(self):
+        result = self.data2+self.data
+        for i in range(5):
+            self.assertEqual(result.y[i], 3.0)
+            self.assertEqual(result.dy[i], math.sqrt(0.5**2+1.0))
+        
+    def test_sub(self):
+        result = self.data2-self.data
+        for i in range(5):
+            self.assertEqual(result.y[i], -1.0)
+            self.assertEqual(result.dy[i], math.sqrt(0.5**2+1.0))
+        
+    def test_mul(self):
+        result = self.data2*self.data
+        for i in range(5):
+            self.assertEqual(result.y[i], 2.0)
+            self.assertEqual(result.dy[i], 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):
+            self.assertEqual(result.y[i], 0.5)
+            self.assertEqual(result.dy[i], 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):
+            self.assertEqual(result.y[i], 5.0)
+            self.assertEqual(result.dy[i], 0.5)
+            
+        result = 3.0+self.data
+        for i in range(5):
+            self.assertEqual(result.y[i], 5.0)
+            self.assertEqual(result.dy[i], 0.5)
+            
+    def test_rsub(self):
+        result = self.data-3.0
+        for i in range(5):
+            self.assertEqual(result.y[i], -1.0)
+            self.assertEqual(result.dy[i], 0.5)
+            
+        result = 3.0-self.data
+        for i in range(5):
+            self.assertEqual(result.y[i], 1.0)
+            self.assertEqual(result.dy[i], 0.5)
+            
+    def test_rmul(self):
+        result = self.data*3.0
+        for i in range(5):
+            self.assertEqual(result.y[i], 6.0)
+            self.assertEqual(result.dy[i], 1.5)
+            
+        result = 3.0*self.data
+        for i in range(5):
+            self.assertEqual(result.y[i], 6.0)
+            self.assertEqual(result.dy[i], 1.5)
+            
+    def test_rdiv(self):
+        result = self.data/4.0
+        for i in range(5):
+            self.assertEqual(result.y[i], 0.5)
+            self.assertEqual(result.dy[i], 0.125)
+            
+        result = 6.0/self.data
+        for i in range(5):
+            self.assertEqual(result.y[i], 3.0)
+            self.assertEqual(result.dy[i], 6.0*0.5/4.0)
+            
+
+if __name__ == '__main__':
+    unittest.main()
+   
