syncoder 0.0.0__tar.gz → 0.0.2a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ #some temp and build directories
2
+ __pycache__
3
+ dist/*
4
+ Docs/
5
+ data/
6
+ out/
7
+ #ingnore data files that may be used or generated during dev.
8
+ *.ipynb
9
+ *.dna
10
+ *.pdf
11
+ *.txt
12
+ *.json
13
+ *.zip
14
+ *.jpg
15
+ *.bin
16
+ .pypirc
17
+ #any actual py files are in src. anything in root is supposed to just for development.
18
+ /*.py
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: syncoder
3
+ Version: 0.0.2a1
4
+ Summary: A codec for DNA data storage
5
+ Project-URL: Homepage, https://example.com/TBD
6
+ Project-URL: Issues, https://example.com/TBD/issues
7
+ Author-email: Christopher Takahashi <cnt@uw.edu>
8
+ License-Expression: BSD-3-Clause
9
+ License-File: LICENSE
10
+ Requires-Python: >=3.9
11
+ Requires-Dist: galois>=0.4
12
+ Requires-Dist: numpy>=1.20
13
+ Requires-Dist: reedsolo>=1.7
14
+ Description-Content-Type: text/markdown
15
+
16
+ # SyNCODER
17
+
18
+ ## Project Overview
19
+ A DNA codec. (Fill in.)
20
+
21
+ ## Features
22
+ Fill in here.
23
+
24
+ ## Installation
25
+ `codec.py` Can be included in a project and used as a local module or installed and used from `pip` :
26
+ ```
27
+ pip install --pre syncoder
28
+ ```
29
+ Note the requirement for the `--pre` flag as there is no official post release yet.
30
+
31
+ ## Usage
32
+ To be completed. For now see `tests/test_codec_pytest.py` for examples
33
+
34
+ ## Testing
35
+ It is sufficient to run
36
+ ```
37
+ pytest
38
+ ```
39
+ from the root directory. Currently coverage is only for features and parameters in active use. More tests to come.
40
+
41
+ ## License
42
+ BSD-3 clause. see `LICENSE` file in this directory.
@@ -0,0 +1,27 @@
1
+ # SyNCODER
2
+
3
+ ## Project Overview
4
+ A DNA codec. (Fill in.)
5
+
6
+ ## Features
7
+ Fill in here.
8
+
9
+ ## Installation
10
+ `codec.py` Can be included in a project and used as a local module or installed and used from `pip` :
11
+ ```
12
+ pip install --pre syncoder
13
+ ```
14
+ Note the requirement for the `--pre` flag as there is no official post release yet.
15
+
16
+ ## Usage
17
+ To be completed. For now see `tests/test_codec_pytest.py` for examples
18
+
19
+ ## Testing
20
+ It is sufficient to run
21
+ ```
22
+ pytest
23
+ ```
24
+ from the root directory. Currently coverage is only for features and parameters in active use. More tests to come.
25
+
26
+ ## License
27
+ BSD-3 clause. see `LICENSE` file in this directory.
@@ -2,7 +2,7 @@ name: codec-user
2
2
  channels:
3
3
  - conda-forge
4
4
  dependencies:
5
- - python=3.12
5
+ - python>=3.12
6
6
  - numpy=2.*
7
7
  - pytest
8
8
  - reedsolo=1.7.*
@@ -2,7 +2,7 @@ name: codec-dev
2
2
  channels:
3
3
  - conda-forge
4
4
  dependencies:
5
- - python=3.12
5
+ - python>=3.12
6
6
  - numpy=2.*
7
7
  - matplotlib=3.*
8
8
  - pytest
@@ -13,7 +13,8 @@ dependencies:
13
13
  - biopython
14
14
  - jupyter
15
15
  - ipykernel
16
- - build
17
16
  - pip:
18
17
  - galois==0.4.*
18
+ - build
19
+ - twine
19
20
 
@@ -2,7 +2,7 @@ name: codec-test
2
2
  channels:
3
3
  - conda-forge
4
4
  dependencies:
5
- - python=3.12
5
+ - python>=3.12
6
6
  - numpy=2.*
7
7
  - pytest
8
8
  - reedsolo=1.7.*
@@ -1,6 +1,11 @@
1
1
  [project]
2
2
  name = "syncoder"
3
- version = "0.0.0"
3
+ dynamic = ["version"]
4
+ dependencies = [
5
+ "numpy>=1.20",
6
+ "galois>=0.4",
7
+ "reedsolo>=1.7"
8
+ ]
4
9
  description = "A codec for DNA data storage"
5
10
  authors = [
6
11
  { name="Christopher Takahashi", email="cnt@uw.edu" }
@@ -19,4 +24,12 @@ packages = ["syncoder"]
19
24
 
20
25
  [build-system]
21
26
  requires = ["hatchling >= 1.26"]
22
- build-backend = "hatchling.build"
27
+ build-backend = "hatchling.build"
28
+
29
+ [tool.pytest.ini_options]
30
+ testpaths = [
31
+ "tests"
32
+ ]
33
+
34
+ [tool.hatch.version]
35
+ path = "src/syncoder/__init__.py"
@@ -0,0 +1,6 @@
1
+ from .codec import *
2
+ from .codec import _default_b32_alphabet, _default_b32_alphabet_alt
3
+ default_b32_alphabet = _default_b32_alphabet
4
+ default_b32_alphabet_alt = _default_b32_alphabet_alt
5
+
6
+ __version__ = "0.0.2a1"
@@ -2,7 +2,8 @@ import os
2
2
  import struct
3
3
  import itertools
4
4
  import logging
5
- from typing import Optional,Union, Callable #requires python 3.5 or later
5
+ import typing
6
+ from typing import Optional,Union, Callable, Tuple #requires python 3.5 or later
6
7
 
7
8
 
8
9
  import numpy as np
@@ -18,7 +19,7 @@ import reedsolo
18
19
 
19
20
 
20
21
  lipsum = b"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum." #cspell:disable-line
21
-
22
+
22
23
 
23
24
  def _int_to_baseN(n:int,base:int,length:int=-1)->list[int]:
24
25
  """Converts an integer to a base N number in little endian order.
@@ -141,6 +142,7 @@ class BaseNBlockCodec:
141
142
  # ^ = self.index_bits <-- in bits not bytes.
142
143
  # note: actual k remains alphabet_size-1, but the code is shorted (0 padded) to inner_n symbols.
143
144
  k = inner_n - (inner_d - 1)
145
+ self.inner_k = k
144
146
  q = self.inner_coder.field.order
145
147
 
146
148
  #number of bytes needed to store the index
@@ -247,7 +249,7 @@ class BaseNBlockCodec:
247
249
  chunk += index<<(self.data_chunk_size*8)
248
250
  index += 1
249
251
  #but.. we still need to pad out the symbol stream.
250
- chunk = _int_to_baseN(chunk,self.inner_coder.field.order,length=self.inner_coder.k)
252
+ chunk = _int_to_baseN(chunk,self.inner_coder.field.order,length=self.inner_k)
251
253
  chunks.append(chunk)
252
254
 
253
255
  #apply inner code
@@ -255,7 +257,7 @@ class BaseNBlockCodec:
255
257
 
256
258
  return ic_chunks
257
259
 
258
- def decode(self, data: list[list[int]], index_start: int = 0) -> bytes:
260
+ def decode(self, data: list[list[int]], index_start: int = 0) -> Tuple[bytes,ArrayLike,ArrayLike,ArrayLike]:
259
261
  """
260
262
  Decodes the given data.
261
263
 
@@ -274,18 +276,20 @@ class BaseNBlockCodec:
274
276
  """
275
277
  # inner decode
276
278
  ## TODO: handle erasures in inner code.
277
- chunked_data = [self.inner_coder.decode(d,errors=True) for d in data]
279
+
280
+ # I'm sure it'll be a (FieldArray,int) ----------------V
281
+ chunked_data:list[Tuple[galois.FieldArray,int]] = [self.inner_coder.decode(d,errors=True) for d in data] #type:ignore
278
282
 
279
283
 
280
284
  # handle outer decode
281
- ordered_chunks = [None] * self.n_strands
282
- chunk_errors = [-1] * self.n_strands
285
+ ordered_chunks:list[bytes|None] = [None] * self.n_strands
286
+ chunk_errors:list[int] = [-1] * self.n_strands
283
287
  for chunk in chunked_data:
284
288
  chunk_int = _baseN_to_int(chunk[0].tolist(), self.inner_coder.field.order)
285
289
  chunk_index = chunk_int >> (self.data_chunk_size * 8) - index_start
286
290
  _mask = (2 ** (self.data_chunk_size * 8)) - 1
287
291
  chunk_int = chunk_int & _mask
288
- chunk_bytes = chunk_int.to_bytes(self.data_chunk_size, 'little')
292
+ chunk_bytes:bytes = chunk_int.to_bytes(self.data_chunk_size, 'little')
289
293
  #print(chunk_index)
290
294
  try:
291
295
  ordered_chunks[chunk_index] = chunk_bytes
@@ -299,6 +303,8 @@ class BaseNBlockCodec:
299
303
  if c is None:
300
304
  erasures.append(i) # remember where erasure is
301
305
  ordered_chunks[i] = b'\x00' * self.data_chunk_size # fill in erasures with zeros
306
+ #we're all bytes now, but the checker can't tell that.
307
+ ordered_chunks_bytes:list[bytes] = typing.cast(list[bytes],ordered_chunks)
302
308
 
303
309
  if self.outer_coder.c_exp==8:
304
310
  outer_dtype = np.uint8
@@ -306,7 +312,7 @@ class BaseNBlockCodec:
306
312
  outer_dtype = np.uint16
307
313
  else:
308
314
  raise ValueError("Unsupported outer code byte size.")
309
- data_np_encoded = np.array([np.frombuffer(c, dtype=outer_dtype) for c in ordered_chunks]).transpose()
315
+ data_np_encoded = np.array([np.frombuffer(c, dtype=outer_dtype) for c in ordered_chunks_bytes]).transpose()
310
316
  data_decoded_results = [self.outer_coder.decode(dc, erase_pos=erasures) for dc in data_np_encoded]
311
317
  data_decoded_chunks = np.array([d[0] for d in data_decoded_results],dtype=outer_dtype).transpose()
312
318
  data_decoded = data_decoded_chunks.flatten().tobytes()
@@ -336,7 +342,7 @@ class BaseNBlockCodec:
336
342
  _default_b32_alphabet = ['GCT', 'ACT', 'AGT', 'CTC', 'TGC', 'GAG', 'GCA', 'ATG', 'AGA', 'AGC', 'CAC', 'CAT', 'CAG', 'CTG', 'TCT', 'GAC', 'GTC', 'GTG', 'ACA', 'ACG', 'ATC', 'CTA', 'CGA', 'CGT', 'TAC', 'TAG', 'TCA', 'TCG', 'TGA', 'TGT', 'GAT', 'GTA']
337
343
  _default_b32_alphabet_alt = ['ATA', 'AAC', 'CCT', 'TAT', 'CCG', 'TGG', 'CGC', 'CGG', 'GTT', 'GGT', 'GCG', 'TCC', 'CCA', 'GGA', 'ACC', 'AGG', 'TAA', 'GCC', 'AAA', 'CCC', 'GGG', 'GGC', 'TTA', 'TTT', 'GAA', 'AAG', 'CTT', 'TTG', 'TTC', 'AAT', 'CAA', 'ATT']
338
344
 
339
- def find_avoid(seq:str, avoids:list)->tuple[int]:
345
+ def find_avoid(seq:str, avoids:list)->tuple[int,int]:
340
346
  """find index and length of avoided sequence """
341
347
  for a in avoids:
342
348
  loc = seq.find(a)
@@ -374,39 +380,52 @@ def longest_binder(a:str,b:str):
374
380
 
375
381
 
376
382
 
377
- def b32_to_DNA_optimize(file_data:list[list[int]],words:list[str], alternate_words:list[str], mask:Union[list[int],None]=None, penalty_fn:Union[Callable[[str],int],None]=None)->list[str]:
383
+ def b32_to_DNA_optimize(file_data:list[ArrayLike],words:list[str], alternate_words:list[str], mask:Union[ArrayLike,None]=None, nmasked=-1, penalty_fn:Union[Callable[[str],int],None]=None)->list[Tuple[bytes,int]]:
378
384
  """
379
385
  TODO: rename to bN_to_DNA_optimize, and add test.
386
+ file_data: 2d list of integral types (symbols)
380
387
  mask: list of ints. 0 for word, 1 for alternate word, -1 for don't care. None for no mask.
388
+ nmasked: number of masked words. -1 for all.
381
389
  penalty_fn: function that takes a DNA sequence and returns a score. penalty_fn(DNA_seq:str)->int
382
- """
383
- return [b32_to_DNA_optimize_single(x,words,alternate_words,penalty_fn=penalty_fn) for x in file_data]
390
+ """
391
+ if nmasked<0: #mask all
392
+ return [b32_to_DNA_optimize_single(x,words,alternate_words,mask,penalty_fn=penalty_fn) for x in file_data]
393
+ else:
394
+ masked_part = [b32_to_DNA_optimize_single(x,words,alternate_words,mask,penalty_fn=penalty_fn) for x in file_data[0:nmasked]]
395
+ unmasked_part = [b32_to_DNA_optimize_single(x,words,alternate_words,mask=None,penalty_fn=penalty_fn) for x in file_data[nmasked:]]
396
+ return masked_part + unmasked_part
384
397
 
385
398
 
386
- def b32_to_DNA_optimize_single(strand_data:list[int],words:list[str], alternate_words:list[str], mask:Union[list[int],None]=None ,penalty_fn=None)->str:
399
+ def b32_to_DNA_optimize_single(strand_data:ArrayLike, words:list[str], alternate_words:list[str], mask:Union[ArrayLike,None]=None ,penalty_fn=None)->Tuple[bytes,int]:
387
400
  """
388
401
  TODO: rename to bN_to_DNA_optimize_single
389
- TODO: add mask support.
402
+ strand_data: 1d list of integral types
390
403
  mask: list of ints. 0 for word, 1 for alternate word, -1 for don't care. None for no mask.
391
404
  """
405
+ strand_data = np.array(strand_data)
406
+
392
407
  if mask is not None:
393
- mutable_ind = np.where(np.array(mask)==-1)
408
+ mask = np.array(mask)
409
+ _m = np.full(len(strand_data),-1,dtype=np.int8)
410
+ _m[:len(mask)] = np.array(mask.copy()) #Probably don't need this copy.
411
+ mask = _m
412
+ mutable_ind = np.where(mask==-1)
413
+ mask[mutable_ind] = 0
394
414
  #raise NotImplementedError("mask not implemented yet")
395
415
  else:
416
+ mask = np.zeros(len(strand_data),dtype=np.uint8)
396
417
  mutable_ind = np.arange(len(strand_data))
397
- mask = mask.copy()
398
- mask[mutable_ind] = 0
399
-
400
- strand_data = np.array(strand_data)
401
- words = np.array(list(zip(words,alternate_words)),dtype="S")
418
+
419
+ words_lookup = np.array(list(zip(words,alternate_words)),dtype="S")
402
420
  picks = np.zeros(len(strand_data),dtype=np.uint8)
403
421
  picks[0:len(mask)] = mask
404
- dna_seq = b"".join(words[strand_data,picks])
422
+ dna_seq = b"".join(words_lookup[strand_data,picks])
405
423
  if penalty_fn is None:
406
424
  return dna_seq,0
407
425
  score = penalty_fn(dna_seq)
408
426
 
409
427
  #greedy search, stop when you can't find a better solution one step away.
428
+ #this may end on a local minimum.
410
429
  icount = 0
411
430
  while True:
412
431
  iteration_best_picks = picks.copy()
@@ -415,7 +434,7 @@ def b32_to_DNA_optimize_single(strand_data:list[int],words:list[str], alternate_
415
434
  for i in mutable_ind:
416
435
  new_picks = picks.copy()
417
436
  new_picks[i] = not new_picks[i]
418
- new_dna_seq = b"".join(words[strand_data,new_picks])
437
+ new_dna_seq = b"".join(words_lookup[strand_data,new_picks])
419
438
  new_score = penalty_fn(new_dna_seq)
420
439
  if new_score < iteration_best_score:
421
440
  iteration_best_picks = new_picks
@@ -457,11 +476,11 @@ def b32_to_DNA(file_data:list[list[int]],words:list[str], alternate_words:list[s
457
476
  dna.append(_s)
458
477
  return dna
459
478
 
460
- def dna_to_bN(dna: Union[list[str],list[bytes]] ,words:list[str], alternate_words:list[str])->list[int]:
461
- """ takes a list of strings and converts them to a list of base N ints"""
479
+ def dna_to_bN(dna: Union[list[str],list[bytes]] ,words:list[str], alternate_words:list[str])->list[list[int]]:
480
+ """ takes a list of strings and converts them to a list of lists of base N ints"""
462
481
 
463
- if isinstance(dna[0],bytes):
464
- dna = [x.decode() for x in dna]
482
+ if isinstance(dna[0],bytes): #assume the rest are bytes too.
483
+ dna = [x.decode() for x in typing.cast(list[bytes],dna)]
465
484
 
466
485
  wordlen = len(words[0])
467
486
  #build the reverse lookup table
@@ -471,16 +490,16 @@ def dna_to_bN(dna: Union[list[str],list[bytes]] ,words:list[str], alternate_word
471
490
  bNlut[alternate_words[ind]] = ind
472
491
 
473
492
 
474
- bNdatalist = []
493
+ bNdatalist:list[list[int]] = []
475
494
  for d in dna:
476
- bNdata = []
495
+ bNdata:list[int] = []
477
496
  for ind in range(0,len(d),wordlen):
478
497
  w = d[ind:ind+wordlen]
479
498
  bNdata.append(bNlut[w])
480
499
  bNdatalist.append(bNdata)
481
500
  return bNdatalist
482
501
 
483
- def dna_to_b32(dna: Union[list[str],list[bytes]] ,words:list[str], alternate_words:list[str])->list[int]:
502
+ def dna_to_b32(dna: Union[list[str],list[bytes]] ,words:list[str], alternate_words:list[str])->list[list[int]]:
484
503
  assert len(words) == 32
485
504
  return dna_to_bN(dna,words,alternate_words)
486
505
 
@@ -602,15 +621,21 @@ def remove_bytes(data:bytes, insert:int|bytes, chunk_len:int, num_inserts:int) -
602
621
  Returns:
603
622
  bytes: The modified byte array with the inserted bytes removed.
604
623
  """
624
+ have_insert_seq = False
605
625
  if isinstance(insert,bytes):
606
626
  insert_len = len(insert)
627
+ have_insert_seq = True
628
+ elif isinstance(insert,int):
629
+ if insert <= 0:
630
+ raise ValueError("insert must be a positive integer")
631
+ insert_len:int = insert
607
632
  else:
608
- insert = None
633
+ raise ValueError("insert must be a bytes or int")
609
634
 
610
635
  data = bytearray(data)
611
636
  for i in reversed(range(num_inserts)):
612
637
  ins_index = i * chunk_len
613
- if insert is not None:
638
+ if have_insert_seq:
614
639
  if data[ins_index:ins_index + insert_len] != insert:
615
640
  raise ValueError("insert bytes do not match bytes to be removed")
616
641
  del data[ins_index:ins_index + insert_len]
@@ -4,7 +4,7 @@ import random
4
4
  import numpy as np
5
5
  from itertools import product
6
6
 
7
- import codec
7
+ import syncoder as codec
8
8
 
9
9
  #logging.basicConfig(level=logging.DEBUG,stream=sys.stdout)
10
10
  #numba_logger = logging.getLogger('numba')
@@ -23,12 +23,14 @@ def test_encode_decode():
23
23
 
24
24
  out_text = c.decode( coded )[0]
25
25
  assert in_text == out_text
26
+ #check inner_n is causing the correct number of bases
27
+ assert len(coded[0]) == 30
26
28
 
27
29
  def test_coded_to_bases():
28
30
  #TODO: test optimize too
29
31
 
30
- test_encoded_data = np.random.randint(0,32,(10,31))
31
- DNA_with_scores = codec.b32_to_DNA_optimize(test_encoded_data,codec._default_b32_alphabet,codec._default_b32_alphabet_alt)
32
+ test_encoded_data = np.random.randint(0,32,(10,31),dtype=np.uint8)
33
+ DNA_with_scores = codec.b32_to_DNA_optimize(test_encoded_data,codec._default_b32_alphabet,codec._default_b32_alphabet_alt) # type: ignore
32
34
  DNA = [x[0] for x in DNA_with_scores]
33
35
  test_decoded_data = codec.dna_to_b32(DNA,codec._default_b32_alphabet,codec._default_b32_alphabet_alt)
34
36
  print(test_encoded_data.flatten())
@@ -54,6 +56,8 @@ def test_dna_to_bN():
54
56
  assert np.array_equal(data, decode_data)
55
57
 
56
58
  def test_dna_to_bytes():
59
+ _int_to_baseN = codec.codec._int_to_baseN
60
+
57
61
  #TODO: adapt to workwith not b32 too.
58
62
  #answer = [64, 79, 231, 126, 228, 124, 3]
59
63
  alphabet =codec._default_b32_alphabet
@@ -70,7 +74,7 @@ def test_dna_to_bytes():
70
74
 
71
75
  dna_bytes, mask = codec.dna_to_bytes(DNA,alphabet,alphabet_alt)
72
76
 
73
- dna_baseN = codec._int_to_baseN(int.from_bytes(dna_bytes,"little"),len(alphabet))
77
+ dna_baseN = _int_to_baseN(int.from_bytes(dna_bytes,"little"),len(alphabet))
74
78
  assert dna_baseN == answer32
75
79
 
76
80
  _t = np.array(dna_baseN)
@@ -78,3 +82,28 @@ def test_dna_to_bytes():
78
82
  generated_dna = "".join(full_alphabet[dna_baseN_with_alt])
79
83
 
80
84
  assert generated_dna == DNA
85
+
86
+ def test_inserts():
87
+ coder = codec.BaseNBlockCodec(inner_alphabet_size=32,inner_d=5,inner_n=29,n_strands=2000,n_redundant_strands=100)
88
+ n_inserts = 200
89
+ insert = "GCTCTTCCCACCACCATTGCCTTCTTCTTG"
90
+ insert_bytes, imask = codec.dna_to_bytes(insert,codec._default_b32_alphabet,codec._default_b32_alphabet_alt)
91
+ total_ibytes = len(insert_bytes)*n_inserts
92
+ data_size = coder.block_capacity_bytes - total_ibytes
93
+
94
+
95
+ test_data = np.random.randint(0,255,data_size,dtype=np.uint8).tobytes()
96
+
97
+ inserted_data = codec.insert_bytes(test_data,insert_bytes,coder.data_chunk_size,n_inserts)
98
+ assert len(inserted_data) == coder.block_capacity_bytes
99
+
100
+ enc_d = coder.encode(inserted_data)
101
+ data_decodec,__oe,__oer,__ie = coder.decode(enc_d)
102
+ assert data_decodec == inserted_data
103
+
104
+ test_dna_out = codec.b32_to_DNA_optimize(enc_d,codec._default_b32_alphabet,codec._default_b32_alphabet_alt,mask=imask,nmasked=n_inserts) # type: ignore
105
+ test_dna = [x[0] for x in test_dna_out]
106
+ for strand in test_dna[:n_inserts]:
107
+ assert strand.decode().startswith(insert)
108
+
109
+ assert codec.dna_to_bN(test_dna,codec._default_b32_alphabet,codec._default_b32_alphabet_alt) == enc_d
syncoder-0.0.0/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- __pycache__/*
2
- Docs/
3
- *.ipynb
4
- *.dna
5
- *.pdf
6
- *.txt
7
- *.json
8
- *.zip
9
- *.jpg
10
- *.bin
11
- debugging.py
syncoder-0.0.0/PKG-INFO DELETED
@@ -1,13 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: syncoder
3
- Version: 0.0.0
4
- Summary: A codec for DNA data storage
5
- Project-URL: Homepage, https://example.com/TBD
6
- Project-URL: Issues, https://example.com/TBD/issues
7
- Author-email: Christopher Takahashi <cnt@uw.edu>
8
- License-Expression: BSD-3-Clause
9
- License-File: LICENSE
10
- Requires-Python: >=3.9
11
- Description-Content-Type: text/markdown
12
-
13
- Readme coming soon.
syncoder-0.0.0/README.md DELETED
@@ -1 +0,0 @@
1
- Readme coming soon.
syncoder-0.0.0/foo.py DELETED
@@ -1,4 +0,0 @@
1
-
2
- from src import *
3
-
4
- print(nextfn(5))
@@ -1 +0,0 @@
1
- __all__ = ["bar.nextfn"]
File without changes
File without changes