Repository: biopython/biopython

Issue:
lcc.lcc_mult returns extra zero value
### Setup

I am reporting a problem with Biopython version, Python version, and operating
system as follows:

```python
import sys; print(sys.version)
import platform; print(platform.python_implementation()); print(platform.platform())
import Bio; print(Bio.__version__)
```

[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
CPython
Linux-3.10.0-957.1.3.el7.x86_64-x86_64-with-centos-7.6.1810-Core
1.78

### Expected behaviour

[1.9056390622295662]

### Actual behaviour

[0, 1.9056390622295662]

### Steps to reproduce

lcc.lcc_mult('ACGATAGC', 8)

In addition according the [article](https://www.researchgate.net/publication/229964618_Sequence_Complexity_and_Composition), the calculation of LCC uses log base 4 but the implementation uses log base 2.  That is why for the example sequence 5 in Table 1 the value shown is half the function value.



Discussion:
Hello, I can help with this.
Just to be sure if I understand this: 
There are two issues here.
1- Returning more values than expected.
2- Revise the formula.
It puzzles me that for so many years we have been using this formula, but your correction seems compatible with the article that the formula implemented.
Hi, that is correct.  My guess is that the function is not used much., essentially it is an entropy calculation of the base frequencies.  I'm currently looking into linguistic complexity which looks more useful in my case and is described [here](https://doi.org/10.1093%2Fnar%2Fgkh466).
Happy to defer to @sbassi on this as a previous contributor to this area of the code (and this is something I have never used).
I reproduced the error, working on this now.

Resolving patch:
diff --git a/Bio/SeqUtils/lcc.py b/Bio/SeqUtils/lcc.py
--- a/Bio/SeqUtils/lcc.py
+++ b/Bio/SeqUtils/lcc.py
@@ -23,14 +23,14 @@ def lcc_mult(seq, wsize):
     version is optimized for speed. The optimization works by using the
     value of previous window as a base to compute the next one.
     """
-    l2 = math.log(2)
+    l4 = math.log(4)
     tamseq = len(seq)
     upper = str(seq).upper()
     compone = [0]
-    lccsal = [0]
+    lccsal = []
     for i in range(wsize):
         compone.append(
-            ((i + 1) / float(wsize)) * ((math.log((i + 1) / float(wsize))) / l2)
+            ((i + 1) / float(wsize)) * ((math.log((i + 1) / float(wsize))) / l4)
         )
     window = seq[0:wsize]
     cant_a = window.count("A")
@@ -133,30 +133,30 @@ def lcc_simp(seq):
     """
     wsize = len(seq)
     upper = str(seq).upper()
-    l2 = math.log(2)
+    l4 = math.log(4)
+    # Check to avoid calculating the log of 0.
     if "A" not in seq:
         term_a = 0
-        # Check to avoid calculating the log of 0.
     else:
         term_a = ((upper.count("A")) / float(wsize)) * (
-            (math.log((upper.count("A")) / float(wsize))) / l2
+            (math.log((upper.count("A")) / float(wsize))) / l4
         )
     if "C" not in seq:
         term_c = 0
     else:
         term_c = ((upper.count("C")) / float(wsize)) * (
-            (math.log((upper.count("C")) / float(wsize))) / l2
+            (math.log((upper.count("C")) / float(wsize))) / l4
         )
     if "T" not in seq:
         term_t = 0
     else:
         term_t = ((upper.count("T")) / float(wsize)) * (
-            (math.log((upper.count("T")) / float(wsize))) / l2
+            (math.log((upper.count("T")) / float(wsize))) / l4
         )
     if "G" not in seq:
         term_g = 0
     else:
         term_g = ((upper.count("G")) / float(wsize)) * (
-            (math.log((upper.count("G")) / float(wsize))) / l2
+            (math.log((upper.count("G")) / float(wsize))) / l4
         )
     return -(term_a + term_c + term_t + term_g)


Test patch:
diff --git a/Tests/test_SeqUtils.py b/Tests/test_SeqUtils.py
--- a/Tests/test_SeqUtils.py
+++ b/Tests/test_SeqUtils.py
@@ -124,9 +124,9 @@ def seq_checksums(
             self.assertEqual(exp_crc64, crc64(s))
             self.assertEqual(exp_gcg, gcg(s))
             self.assertEqual(exp_seguid, seguid(s))
-            self.assertAlmostEqual(exp_simple_LCC, lcc_simp(s), places=2)
+            self.assertAlmostEqual(exp_simple_LCC, lcc_simp(s), places=4)
             values = lcc_mult(s, 20)
-            self.assertEqual(len(exp_window_LCC), len(values))
+            self.assertEqual(len(exp_window_LCC), len(values), values)
             for value1, value2 in zip(exp_window_LCC, values):
                 self.assertAlmostEqual(value1, value2, places=2)
 
@@ -137,22 +137,21 @@ def test_checksum1(self):
             "CRC-44CAAD88706CC153",
             9729,
             "BpBeDdcNUYNsdk46JoJdw7Pd3BI",
-            1.03,
+            0.5160,
             (
-                0.00,
-                1.00,
-                0.96,
-                0.96,
-                0.96,
-                0.65,
-                0.43,
-                0.35,
-                0.35,
-                0.35,
-                0.35,
-                0.53,
-                0.59,
-                0.26,
+                0.4982,
+                0.4794,
+                0.4794,
+                0.4794,
+                0.3241,
+                0.2160,
+                0.1764,
+                0.1764,
+                0.1764,
+                0.1764,
+                0.2657,
+                0.2948,
+                0.1287,
             ),
         )
 
@@ -163,22 +162,21 @@ def test_checksum2(self):
             "CRC-44CAAD88706CC153",
             9647,
             "X5XEaayob1nZLOc7eVT9qyczarY",
-            1.07,
+            0.5343,
             (
-                0.00,
-                1.00,
-                0.96,
-                0.96,
-                0.96,
-                0.65,
-                0.43,
-                0.35,
-                0.35,
-                0.35,
-                0.35,
-                0.53,
-                0.59,
-                0.26,
+                0.4982,
+                0.4794,
+                0.4794,
+                0.4794,
+                0.3241,
+                0.2160,
+                0.1764,
+                0.1764,
+                0.1764,
+                0.1764,
+                0.2657,
+                0.2948,
+                0.1287,
             ),
         )
 
@@ -189,22 +187,21 @@ def test_checksum3(self):
             "CRC-6234FF451DC6DFC6",
             7959,
             "8WCUbVjBgiRmM10gfR7XJNjbwnE",
-            1.98,
+            0.9886,
             (
-                0.00,
-                2.00,
-                1.99,
-                1.99,
-                2.00,
-                1.99,
-                1.97,
-                1.99,
-                1.99,
-                1.99,
-                1.96,
-                1.96,
-                1.96,
-                1.96,
+                1.00,
+                0.9927,
+                0.9927,
+                1.00,
+                0.9927,
+                0.9854,
+                0.9927,
+                0.9927,
+                0.9927,
+                0.9794,
+                0.9794,
+                0.9794,
+                0.9794,
             ),
         )
 
@@ -299,6 +296,16 @@ def test_index(self):
         self.assertAlmostEqual(X.index["TTG"], 0.072, places=3)
         self.assertAlmostEqual(X.index["TTT"], 0.457, places=3)
 
+    def test_lcc_simp(self):
+        s1 = "ACGATAGC"
+        self.assertAlmostEqual(lcc_simp(s1), 0.9528, places=4)
+
+    def test_lcc_mult(self):
+        s1 = "ACGATAGC"
+        llc_lst = lcc_mult(s1, len(s1))
+        self.assertEqual(len(llc_lst), 1)
+        self.assertAlmostEqual(llc_lst[0], 0.9528, places=4)
+
 
 if __name__ == "__main__":
     runner = unittest.TextTestRunner(verbosity=2)