Improve 'A1 to R1C1' and 'R1C1 to A1' formula conversion.
diff --git a/EPPlus/ExcelCellBase.cs b/EPPlus/ExcelCellBase.cs
index b60f79f..a6bbc1c 100644
--- a/EPPlus/ExcelCellBase.cs
+++ b/EPPlus/ExcelCellBase.cs
@@ -134,10 +134,14 @@
                 {
                     ret += c;
                 }
+                else if (c == ':')  // Keep Range expressions together
+                {
+                    part += c;
+                }
                 else
                 {
                     if ((c == '-' || c == '+' || c == '*' || c == '/' ||
-                        c == '=' || c == '^' || c == ',' || c == ':' ||
+                        c == '=' || c == '^' || c == ',' ||
                         c == '<' || c == '>' || c == '(' || c == ')' || c == '!' ||
                         c == ' ' || c == '&' || c == '%') &&
                         (pos == 0 || value[pos - 1] != '[')) //Last part to allow for R1C1 style [-x]
@@ -169,6 +173,13 @@
         private static string ToR1C1(string part, int row, int col, int rowIncr, int colIncr)
         {
             int addrRow, addrCol;
+
+            // Handle range expressions
+            if ((part.Length > 1) && (part.IndexOf(':', 1) > 0))
+            {
+                return RangeToR1C1(part, row, col, rowIncr, colIncr);
+            }
+
             string Ret = "R";
             if (GetRowCol(part, out addrRow, out addrCol, false))
             {
@@ -203,6 +214,66 @@
                 return part;
             }
         }
+
+        private static string RangeToR1C1(string part, int row, int col, int rowIncr, int colIncr)
+        {
+            // Split range expression
+            string[] cellValues = part.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
+
+            // Convert range expressions
+            string result = "";
+            result += RangeCellToR1C1(cellValues[0], row, col, rowIncr, colIncr);
+            result += ":";
+            result += RangeCellToR1C1(cellValues[1], row, col, rowIncr, colIncr);
+
+            // Return converted range expression
+            return result;
+        }
+
+        private static string RangeCellToR1C1(string part, int row, int col, int rowIncr, int colIncr)
+        {
+            int addrRow, addrCol;
+            bool fixedRow, fixedCol;
+
+            string result = "";
+            if (GetRowCol(part, out addrRow, out addrCol, false, out fixedRow, out fixedCol))
+            {
+                if (addrRow > 0)
+                {
+                    result += "R";
+                    if (fixedRow)
+                    {
+                        // Absolute row
+                        result += addrRow.ToString();
+                    }
+                    else if (addrRow - row != 0)
+                    {
+                        // Relative row
+                        result += string.Format("[{0}]", addrRow - row);
+                    }
+                }
+
+                if (addrCol > 0)
+                {
+                    result += "C";
+                    if (fixedCol)
+                    {
+                        // Absolute column
+                        result += addrCol;
+                    }
+                    else if (addrCol - col != 0)
+                    {
+                        // Relative column
+                        result += string.Format("[{0}]", addrCol - col);
+                    }
+               }
+               return result;
+            }
+            else
+            {
+                return part;
+            }
+        }
         /// <summary>
         /// Translates to absolute address
         /// </summary>
@@ -212,46 +283,250 @@
         /// <param name="rowIncr"></param>
         /// <param name="colIncr"></param>
         /// <returns></returns>
+        /// 
+
+        
         private static string ToAbs(string part, int row, int col, int rowIncr, int colIncr)
         {
-            string check = part.ToUpper(CultureInfo.InvariantCulture);
+            bool absoluteCol = false;
+            bool absoluteRow = false;
+            int colNum = -1;
+            int rowNum = -1;
+            int num;
+            int numLength;
+            int pos = 0;
 
-            int rStart = check.IndexOf("R");
-            if (rStart != 0)
-                return part;
-            if (part.Length == 1) //R
+
+            // Handle range expressions
+            if ((part.Length > 1) && (part.IndexOf(':', 1) > 0))
             {
-                return GetAddress(row, col);
+                return RangeToA1(part, row, col, rowIncr, colIncr);
             }
 
-            int cStart = check.IndexOf("C");
-            bool absoluteRow, absoluteCol;
-            if (cStart == -1)
+            // Ensure part is present
+            if (string.IsNullOrWhiteSpace(part))
+                return "";
+
+            // Convert to upper case
+            string check = part.ToUpper(CultureInfo.InvariantCulture);
+          
+            // Parse "R", if any
+            if (pos < part.Length && check[pos] == 'R')
             {
-                int RNum = GetRC(part, row, out absoluteRow);
-                if (RNum > int.MinValue)
+                pos += 1;
+
+                if (pos >= part.Length)
                 {
-                    return GetAddress(RNum, absoluteRow, col, false);
+                    // Only "R" present
+                    absoluteRow = false;
+                    rowNum = row;
                 }
-                else
+                else if (pos < part.Length && check[pos] == 'C')
                 {
-                    return part;
+                    // "R" followed by "C"
+                    absoluteRow = false;
+                    rowNum = row;
                 }
+                else if (pos < part.Length && check[pos] == '[')
+                {
+                    // "R" followed by relative row number
+                    pos += 1;
+                    num = GetNumber(check.Substring(pos, part.Length - pos), out numLength);
+                    if (num == Int32.MinValue)
+                        return part;
+                    pos += numLength;
+
+                    if (pos < part.Length && check[pos] == ']')
+                    {
+                        pos += 1;
+                    }
+                    else
+                    {
+                        return part;
+                    }
+
+                    absoluteRow = false;
+                    rowNum = row + num;
+
+                }
+                else if (pos < part.Length)
+                {
+                    // "R" followed by absolute row number
+                    num = GetNumber(check.Substring(pos, part.Length - pos), out numLength);
+                    if (rowNum == Int32.MinValue)
+                        return part;
+                    pos += numLength;
+
+                    absoluteRow = true;
+                    rowNum = num;
+                }
+            }
+
+            // Parse "C", if any
+            if (pos < part.Length && check[pos] == 'C')
+            {
+                pos += 1;
+
+                if (pos >= part.Length)
+                {
+                    // Only "C" present
+                    absoluteCol = false;
+                    colNum = col;
+                }
+                else if (pos < part.Length && check[pos] == '[')
+                {
+                    // "C" followed by relative column number
+                    pos += 1;
+                    num = GetNumber(check.Substring(pos, part.Length - pos), out numLength);
+                    if (num == Int32.MinValue)
+                        return part;
+                    pos += numLength;
+
+                    if (pos < part.Length && check[pos] == ']')
+                    {
+                        pos += 1;
+                    }
+                    else
+                    {
+                        return part;
+                    }
+
+                    absoluteCol = false;
+                    colNum = col + num;
+
+                }
+                else if (pos < part.Length)
+                {
+                    // "C" followed by absolute column number
+                    num = GetNumber(check.Substring(pos, part.Length - pos), out numLength);
+                    if (num == Int32.MinValue)
+                        return part;
+                    pos += numLength;
+
+                    absoluteCol = true;
+                    colNum = num;
+                }
+            }
+
+            // Ensure nothing remains unparsed
+            if (pos < part.Length)
+                return part;
+
+            // Exit if neither row nor column is present
+            if ((rowNum == Int32.MinValue) && (colNum == Int32.MinValue))
+                return part;
+
+            // Append column
+            string result = "";
+            if (colNum >= 0)
+            {
+                if (absoluteCol)
+                    result += "$";
+                result += GetColumnLetter(colNum);
+            }
+
+            // Append row
+            if (rowNum >= 0)
+            {
+                if (absoluteRow)
+                    result += "$";
+                result += rowNum.ToString();
+            }
+
+            // Return result
+            return result;
+        }
+
+        private static int GetNumber(string value, out int length)
+        {            
+            // Get number length
+            length = 0;
+
+            // Ensure value is present
+            if (string.IsNullOrWhiteSpace(value))
+                return Int32.MinValue;
+
+            // Check for sign
+            if ((length < value.Length) && ((value[length] == '-') || (value[length] == '+')))
+            {
+                length += 1;
+            }
+
+            // Get number length
+            while (length < value.Length && value[length] >= '0' && value[length] <= '9')
+            {
+                length += 1;
+            }
+
+            // No number found
+            if (length == 0)
+                return Int32.MinValue;
+
+            // Return number value
+            int result;
+            return (int.TryParse(value.Substring(0, length), out result)) ? result : Int32.MinValue;
+        }
+
+        private static string RangeToA1(string part, int row, int col, int rowIncr, int colIncr)
+        {
+            // Split range expression
+            string[] cellValues = part.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
+
+            // Convert range expressions
+            string result = "";
+            result += ToAbs(cellValues[0], row, col, rowIncr, colIncr);
+            result += ":";
+            result += ToAbs(cellValues[1], row, col, rowIncr, colIncr);
+
+            // Return converted range expression
+            return result;
+        }
+
+        private static string RangeCellToA1(string part, int row, int col, int rowIncr, int colIncr)
+        {
+            int addrRow, addrCol;
+            bool fixedRow, fixedCol;
+
+            string result = "";
+            if (GetRowCol(part, out addrRow, out addrCol, false, out fixedRow, out fixedCol))
+            {
+                if (addrRow > 0)
+                {
+                    result += "R";
+                    if (fixedRow)
+                    {
+                        // Absolute row
+                        result += addrRow.ToString();
+                    }
+                    else if (addrRow - row != 0)
+                    {
+                        // Relative row
+                        result += string.Format("[{0}]", addrRow - row);
+                    }
+                }
+
+                if (addrCol > 0)
+                {
+                    result += "C";
+                    if (fixedCol)
+                    {
+                        // Absolute column
+                        result += addrCol;
+                    }
+                    else if (addrCol - col != 0)
+                    {
+                        // Relative column
+                        result += string.Format("[{0}]", addrCol - col);
+                    }
+                }
+                return result;
             }
             else
             {
-                int RNum = GetRC(part.Substring(1, cStart - 1), row, out absoluteRow);
-                int CNum = GetRC(part.Substring(cStart + 1, part.Length - cStart - 1), col, out absoluteCol);
-                if (RNum > int.MinValue && CNum > int.MinValue)
-                {
-                    return GetAddress(RNum, absoluteRow, CNum, absoluteCol);
-                }
-                else
-                {
-                    return part;
-                }
+                return part;
             }
         }
+
         /// <summary>
         /// Adds or subtracts a row or column to an address
         /// </summary>
@@ -317,16 +592,18 @@
         /// <returns></returns>
         private static int GetRC(string value, int OffsetValue, out bool fixedAddr)
         {
-            if (value == "")
+            if ((value == "") || (value == "R") || (value == "C"))
             {
+                // Relative address with no offset
                 fixedAddr = false;
                 return OffsetValue;
             }
             int num;
-            if (value[0] == '[' && value[value.Length - 1] == ']') //Offset?                
+            if (value[1] == '[' && value[value.Length - 1] == ']') //Offset?                
             {
+                // Relative address
                 fixedAddr = false;
-                if (int.TryParse(value.Substring(1, value.Length - 2), out num))
+                if (int.TryParse(value.Substring(2, value.Length - 3), out num))
                 {
                     return (OffsetValue + num);
                 }
@@ -337,8 +614,9 @@
             }
             else
             {
+                // Absolute address
                 fixedAddr = true;
-                if (int.TryParse(value, out num))
+                if (int.TryParse(value.Substring(1, value.Length - 1), out num))
                 {
                     return num;
                 }
@@ -483,6 +761,7 @@
         internal static bool GetRowCol(string address, out int row, out int col, bool throwException, out bool fixedRow, out bool fixedCol)
         {
           bool colPart = true;
+          bool isFixed = false;
           int colStartIx = 0;
           int colLength = 0;
           col = 0;
@@ -506,31 +785,37 @@
           for (int i = colStartIx; i < address.Length; i++)
           {
             char c = address[i];
-            if (colPart && (c >= 'A' && c <= 'Z') && colLength <= 3)
+            if (c == '$')
             {
+                // Absolute address
+                isFixed = true;
+            }
+            else if (colPart && (c >= 'A' && c <= 'Z') && colLength <= 3)
+            {
+                // Column portion of address
+              if (isFixed)
+              {
+                fixedCol = true;
+                isFixed = false;
+              }
+
               col *= 26;
               col += ((int)c) - 64;
               colLength++;
             }
             else if (c >= '0' && c <= '9')
             {
+                // Row portion of address
+              if (isFixed)
+              {
+                  fixedRow = true;
+                  isFixed = false;
+              }
+
               row *= 10;
               row += ((int)c) - 48;
               colPart = false;
             }
-            else if (c == '$')
-            {
-              if (i == colStartIx)
-              {
-                colStartIx++;
-                fixedCol = true;
-              }
-              else
-              {
-                colPart = false;
-                fixedRow = true;
-              }
-            }
             else
             {
               row = 0;