ACGT=: 'ACGT'
MUTS=: ;: 'del ins mut'

NB. generate sequence of size y of uniformly selected nucleotides.
NB. represent sequences as ints in range i.4 pretty printed. nuc
NB. defined separately to avoid fixing value inside mutation
NB. functions.
nuc=: monad : '?4'
dna=: nuc"0 @ i.

NB. randomly mutate nucleotide at a random index by deletion insertion
NB. or mutation of a nucleotide.
del=: {.,[:}.}.
ins=: {.,[email protected]],}.
mut=: {.,[email protected]],[:}.}.

NB. pretty print nucleotides in rows of 50 with numbering
seq=: [: (;~ [: (4&":"0) 50*[email protected]#) _50]\{&ACGT

sim=: monad define
'n k ws'=. y        NB. initial size, mutations, and weights for mutations
ws=. (% +/) ws      NB. normalize weights
A=.0$]D0=.D=. dna n NB. initial dna and history of actions

NB. k times do a random action according to weights and record it
for. i.k do.
  D=.". action=. (":?#D),' ',(":MUTS{::~(+/\ws)I.?0),' D'
  A=. action ; A
end.

echo 'actions';,. A-.a:
echo ('mutation';'probability') , MUTS ,. <"0 ws
('start';'end'),.(seq D0) ,: seq D
)

simulate=: ([email protected](1 1 1&; &. |. ))`[email protected].(3=#)


import java.util.Arrays;
import java.util.Random;

public class SequenceMutation {
    public static void main(String[] args) {
        SequenceMutation sm = new SequenceMutation();
        sm.setWeight(OP_CHANGE, 3);
        String sequence = sm.generateSequence(250);
        System.out.println("Initial sequence:");
        printSequence(sequence);
        int count = 10;
        for (int i = 0; i < count; ++i)
            sequence = sm.mutateSequence(sequence);
        System.out.println("After " + count + " mutations:");
        printSequence(sequence);
    }

    public SequenceMutation() {
        totalWeight_ = OP_COUNT;
        Arrays.fill(operationWeight_, 1);
    }

    public String generateSequence(int length) {
        char[] ch = new char[length];
        for (int i = 0; i < length; ++i)
            ch[i] = getRandomBase();
        return new String(ch);
    }

    public void setWeight(int operation, int weight) {
        totalWeight_ -= operationWeight_[operation];
        operationWeight_[operation] = weight;
        totalWeight_ += weight;
    }

    public String mutateSequence(String sequence) {
        char[] ch = sequence.toCharArray();
        int pos = random_.nextInt(ch.length);
        int operation = getRandomOperation();
        if (operation == OP_CHANGE) {
            char b = getRandomBase();
            System.out.println("Change base at position " + pos + " from "
                               + ch[pos] + " to " + b);
            ch[pos] = b;
        } else if (operation == OP_ERASE) {
            System.out.println("Erase base " + ch[pos] + " at position " + pos);
            char[] newCh = new char[ch.length - 1];
            System.arraycopy(ch, 0, newCh, 0, pos);
            System.arraycopy(ch, pos + 1, newCh, pos, ch.length - pos - 1);
            ch = newCh;
        } else if (operation == OP_INSERT) {
            char b = getRandomBase();
            System.out.println("Insert base " + b + " at position " + pos);
            char[] newCh = new char[ch.length + 1];
            System.arraycopy(ch, 0, newCh, 0, pos);
            System.arraycopy(ch, pos, newCh, pos + 1, ch.length - pos);
            newCh[pos] = b;
            ch = newCh;
        }
        return new String(ch);
    }

    public static void printSequence(String sequence) {
        int[] count = new int[BASES.length];
        for (int i = 0, n = sequence.length(); i < n; ++i) {
            if (i % 50 == 0) {
                if (i != 0)
                    System.out.println();
                System.out.printf("%3d: ", i);
            }
            char ch = sequence.charAt(i);
            System.out.print(ch);
            for (int j = 0; j < BASES.length; ++j) {
                if (BASES[j] == ch) {
                    ++count[j];
                    break;
                }
            }
        }
        System.out.println();
        System.out.println("Base counts:");
        int total = 0;
        for (int j = 0; j < BASES.length; ++j) {
            total += count[j];
            System.out.print(BASES[j] + ": " + count[j] + ", ");
        }
        System.out.println("Total: " + total);
    }

    private char getRandomBase() {
        return BASES[random_.nextInt(BASES.length)];
    }

    private int getRandomOperation() {
        int n = random_.nextInt(totalWeight_), op = 0;
        for (int weight = 0; op < OP_COUNT; ++op) {
            weight += operationWeight_[op];
            if (n < weight)
                break;
        }
        return op;
    }

    private final Random random_ = new Random();
    private int[] operationWeight_ = new int[OP_COUNT];
    private int totalWeight_ = 0;

    private static final int OP_CHANGE = 0;
    private static final int OP_ERASE = 1;
    private static final int OP_INSERT = 2;
    private static final int OP_COUNT = 3;
    private static final char[] BASES = {'A', 'C', 'G', 'T'};
}


with javascript_semantics
include timedate.e

constant cycles = {"Physical day ", "Emotional day", "Mental day   "},
         lengths = {23, 28, 33},
         quadrants = {{"up and rising",    "peak"},
                      {"up but falling",   "transition"},
                      {"down and falling", "valley"},
                      {"down but rising",  "transition"}}

procedure biorhythms(string birthDate, targetDate)
    timedate bd = parse_date_string(birthDate,{"YYYY-MM-DD"}),
             td = parse_date_string(targetDate,{"YYYY-MM-DD"})
    integer days = floor(timedate_diff(bd, td, DT_DAY)/(60*60*24))
    printf(1,"Born %s, Target %s\n",{birthDate,targetDate})
    printf(1,"Day %d\n",days)
    for i=1 to 3 do
        integer len = lengths[i],
                posn = remainder(days,len),
                quadrant = floor(posn/len*4)+1
        atom percent  = floor(sin(2*PI*posn/len)*1000)/10
        string cycle = cycles[i],
               desc = iff(percent>95 ? " peak" :
                      iff(percent<-95 ? " valley" :
                      iff(abs(percent)<5 ? " critical transition" : "other")))
        if desc == "other" then
            timedate t = adjust_timedate(td,timedelta(days:=floor(quadrant/4*len)-posn))
            string transition = format_timedate(t,"YYYY-MM-DD"),
                   {trend,next} = quadrants[quadrant]
            desc = sprintf("%5.1f%% (%s, next %s %s)", {percent, trend, next, transition})
        end if
        printf(1,"%s %2d : %s\n", {cycle, posn, desc})
    end for
    printf(1,"\n")
end procedure

constant datePairs = {
    {"1943-03-09", "1972-07-11"},
    {"1809-01-12", "1863-11-19"},
    {"1809-02-12", "1863-11-19"}  // correct DOB for Abraham Lincoln
}
for i=1 to length(datePairs) do biorhythms(datePairs[i][1], datePairs[i][2]) end for


// Basic set-up
const numBases = 250
const numMutations = 30
const bases = ['A', 'C', 'G', 'T'];

// Utility functions
/**
 * Return a shallow copy of an array
 * @param {Array<*>} arr
 * @returns {*[]}
 */
const copy = arr => [...arr];

/**
 * Get a random int up to but excluding the the given number
 * @param {number} max
 * @returns {number}
 */
const randTo = max => (Math.random() * max) | 0;

/**
 * Given an array return a random element and the index of that element from
 * the array.
 * @param {Array<*>} arr
 * @returns {[*[], number]}
 */
const randSelect = arr => {
  const at = randTo(arr.length);
  return [arr[at], at];
};

/**
 * Given a number or string, return a left padded string
 * @param {string|number} v
 * @returns {string}
 */
const pad = v => ('' + v).padStart(4, ' ');

/**
 * Count the number of elements that match the given value in an array
 * @param {Array<string>} arr
 * @returns {function(string): number}
 */
const filterCount = arr => s => arr.filter(e => e === s).length;

/**
 * Utility logging function
 * @param {string|number} v
 * @param {string|number} n
 */
const print = (v, n) => console.log(`${pad(v)}:\t${n}`)

/**
 * Utility function to randomly select a new base, and an index in the given
 * sequence.
 * @param {Array<string>} seq
 * @param {Array<string>} bases
 * @returns {[string, string, number]}
 */
const getVars = (seq, bases) => {
  const [newBase, _] = randSelect(bases);
  const [extBase, randPos] = randSelect(seq);
  return [newBase, extBase, randPos];
};

// Bias the operations
/**
 * Given a map of function to ratio, return an array of those functions
 * appearing ratio number of times in the array.
 * @param weightMap
 * @returns {Array<function>}
 */
const weightedOps = weightMap => {
  return [...weightMap.entries()].reduce((p, [op, weight]) =>
      [...p, ...(Array(weight).fill(op))], []);
};

// Pretty Print functions
const prettyPrint = seq => {
  let idx = 0;
  const rem = seq.reduce((p, c) => {
    const s = p + c;
    if (s.length === 50) {
      print(idx, s);
      idx = idx + 50;
      return '';
    }
    return s;
  }, '');
  if (rem !== '') {
    print(idx, rem);
  }
}

const printBases = seq => {
  const filterSeq = filterCount(seq);
  let tot = 0;
  [...bases].forEach(e => {
    const cnt = filterSeq(e);
    print(e, cnt);
    tot = tot + cnt;
  })
  print('Σ', tot);
}

// Mutation definitions
const swap = ([hist, seq]) => {
  const arr = copy(seq);
  const [newBase, extBase, randPos] = getVars(arr, bases);
  arr.splice(randPos, 1, newBase);
  return [[...hist, `Swapped ${extBase} for ${newBase} at ${randPos}`], arr];
};

const del = ([hist, seq]) => {
  const arr = copy(seq);
  const [newBase, extBase, randPos] = getVars(arr, bases);
  arr.splice(randPos, 1);
  return [[...hist, `Deleted ${extBase} at ${randPos}`], arr];
}

const insert = ([hist, seq]) => {
  const arr = copy(seq);
  const [newBase, extBase, randPos] = getVars(arr, bases);
  arr.splice(randPos, 0, newBase);
  return [[...hist, `Inserted ${newBase} at ${randPos}`], arr];
}

// Create the starting sequence
const seq = Array(numBases).fill(undefined).map(
    () => randSelect(bases)[0]);

// Create a weighted set of mutations
const weightMap = new Map()
    .set(swap, 1)
    .set(del, 1)
    .set(insert, 1);
const operations = weightedOps(weightMap);
const mutations = Array(numMutations).fill(undefined).map(
    () => randSelect(operations)[0]);

// Mutate the sequence
const [hist, mut] = mutations.reduce((p, c) => c(p), [[], seq]);

console.log('ORIGINAL SEQUENCE:')
prettyPrint(seq);

console.log('\nBASE COUNTS:')
printBases(seq);

console.log('\nMUTATION LOG:')
hist.forEach((e, i) => console.log(`${i}:\t${e}`));

console.log('\nMUTATED SEQUENCE:')
prettyPrint(mut);

console.log('\nMUTATED BASE COUNTS:')
printBases(mut);


"""

Python implementation of

http://rosettacode.org/wiki/Biorhythms

"""

from datetime import date, timedelta
from math import floor, sin, pi

def biorhythms(birthdate,targetdate):
    """
    Print out biorhythm data for targetdate assuming you were
    born on birthdate.

    birthdate and targetdata are strings in this format:

    YYYY-MM-DD e.g. 1964-12-26
    """

    # print dates

    print("Born: "+birthdate+" Target: "+targetdate)

    # convert to date types - Python 3.7 or later

    birthdate = date.fromisoformat(birthdate)
    targetdate = date.fromisoformat(targetdate)

    # days between

    days = (targetdate - birthdate).days

    print("Day: "+str(days))

    # cycle logic - mostly from Julia example

    cycle_labels = ["Physical", "Emotional", "Mental"]
    cycle_lengths = [23, 28, 33]
    quadrants = [("up and rising", "peak"), ("up but falling", "transition"),
                   ("down and falling", "valley"), ("down but rising", "transition")]

    for i in range(3):
        label = cycle_labels[i]
        length = cycle_lengths[i]
        position = days % length
        quadrant = int(floor((4 * position) / length))
        percentage = int(round(100 * sin(2 * pi * position / length),0))
        transition_date = targetdate + timedelta(days=floor((quadrant + 1)/4 * length) - position)
        trend, next = quadrants[quadrant]

        if percentage > 95:
            description = "peak"
        elif percentage < -95:
             description = "valley"
        elif abs(percentage) < 5:
             description = "critical transition"
        else:
             description = str(percentage)+"% ("+trend+", next "+next+" "+str(transition_date)+")"
        print(label+" day "+str(position)+": "+description)

biorhythms("1943-03-09","1972-07-11")


bioR <- function(bDay, targetDay) {
    bDay <- as.Date(bDay)
    targetDay <- as.Date(targetDay)
    n <- as.numeric(targetDay - bDay)

    cycles <- c(23, 28, 33)
    mods <- n %% cycles
    bioR <- c(sin(2 * pi * mods / cycles))
    loc <- mods / cycles
    current <- ifelse(bioR > 0, ': Up', ': Down')
    current <- paste(current, ifelse(loc < 0.25 | loc > 0.75,
                                     "and rising",
                                     "and falling"))

    df <- data.frame(dates = seq.Date(from = targetDay - 30,
                                      to = targetDay + 30,
                                      by = 1))
    df$n <- as.numeric(df$dates - bDay)
    df$P <- sin(2 * pi * (df$n %% cycles[1]) / cycles[1])
    df$E <- sin(2 * pi * (df$n %% cycles[2]) / cycles[2])
    df$M <- sin(2 * pi * (df$n %% cycles[3]) / cycles[3])

    plot(df$dates, df$P, col = 'blue',
         main = paste(targetDay, 'Biorhythm for Birthday on', bDay),
         xlab = "",
         ylab = "Intensity")
    points(df$dates, df$E, col = 'green')
    points(df$dates, df$M, col = 'red')
    abline(v = targetDay)
    legend('topleft', legend = c("Phys", "Emot", "Ment"),
           col =c("blue", "green", "red"),
           cex = 0.8,
           pch = 21)

    cat(paste0('Birthday = ', as.character(bDay),
               '\nTarget Date = ', as.character(targetDay),
               '\n', n, ' days',
               '\nPhysical = ', mods[1], current[1],
               '\nEmotional = ', mods[2], current[2],
               '\nMental = ', mods[3], current[3]))
}

bioR('1943-03-09', '1972-07-11')


dnabases = ['A', 'C', 'G', 'T']
randpos(seq) = rand(1:length(seq))                                      # 1
mutateat(pos, seq) = (s = seq[:]; s[pos] = rand(dnabases); s)           # 2-1
deleteat(pos, seq) = [seq[1:pos-1]; seq[pos+1:end]]                     # 2-2
randinsertat(pos, seq) = [seq[1:pos]; rand(dnabases); seq[pos+1:end]]   # 2-3

function weightedmutation(seq, pos, weights=[1, 1, 1], verbose=true)    # Extra credit
    p, r = weights ./ sum(weights), rand()
    f = (r <= p[1]) ? mutateat : (r < p[1] + p[2]) ? deleteat : randinsertat
    verbose && print("Mutate by ", f == mutateat ? "swap" :
                                   f == deleteat ? "delete" : "insert")
    return f(pos, seq)
end

function weightedrandomsitemutation(seq, weights=[1, 1, 1], verbose=true)
    position = randpos(seq)
    newseq = weightedmutation(seq, position, weights, verbose)
    verbose && println(" at position $position")
    return newseq
end

randdnasequence(n) = rand(dnabases, n)                                  # 3

function dnasequenceprettyprint(seq, colsize=50)                        # 4
    println(length(seq), "nt DNA sequence:\n")
    rows = [seq[i:min(length(seq), i + colsize - 1)] for i in 1:colsize:length(seq)]
    for (i, r) in enumerate(rows)
        println(lpad(colsize * (i - 1), 5), "   ", String(r))
    end
    bases = [[c, 0] for c in dnabases]
    for c in seq, base in bases
        if c == base[1]
            base[2] += 1
        end
    end
    println("\nNucleotide counts:\n")
    for base in bases
        println(lpad(base[1], 10), lpad(string(base[2]), 12))
    end
    println(lpad("Other", 10), lpad(string(length(seq) - sum(x[2] for x in bases)), 12))
    println("     _________________\n", lpad("Total", 10), lpad(string(length(seq)), 12))
end

function testbioseq()
    sequence = randdnasequence(500)
    dnasequenceprettyprint(sequence)
    for _ in 1:10                                                       # 5
        sequence = weightedrandomsitemutation(sequence)
    end
    println("\n Mutated:"); dnasequenceprettyprint(sequence)            # 6
end

testbioseq()


math.randomseed(os.time())
bases = {"A","C","T","G"}
function randbase() return bases[math.random(#bases)] end

function mutate(seq)
  local i,h = math.random(#seq), "%-6s %3s at %3d"
  local old,new = seq:sub(i,i), randbase()
  local ops = {
    function(s) h=h:format("Swap", old..">"..new, i) return s:sub(1,i-1)..new..s:sub(i+1) end,
    function(s) h=h:format("Delete", " -"..old, i) return s:sub(1,i-1)..s:sub(i+1) end,
    function(s) h=h:format("Insert", " +"..new, i) return s:sub(1,i-1)..new..s:sub(i) end,
  }
  local weighted = { 1,1,2,3 }
  local n = weighted[math.random(#weighted)]
  return ops[n](seq), h
end

local seq,hist="",{} for i = 1, 200 do seq=seq..randbase() end
print("ORIGINAL:")
prettyprint(seq)
print()

for i = 1, 10 do seq,h=mutate(seq) hist[#hist+1]=h end
print("MUTATIONS:")
for i,h in ipairs(hist) do print("  "..h) end
print()

print("MUTATED:")
prettyprint(seq)


#!/usr/bin/env raku
unit sub MAIN($birthday=%*ENV<BIRTHDAY>, $date = Date.today()) {

my %cycles = ( :23Physical, :28Emotional, :33Mental );
my @quadrants = [ ('up and rising',    'peak'),
                  ('up but falling',   'transition'),
                  ('down and falling', 'valley'),
                  ('down but rising',  'transition') ];

if !$birthday {
    die "Birthday not specified.\n" ~
        "Supply --birthday option or set \$BIRTHDAY in environment.\n";
}

my ($bday, $target) = ($birthday, $date).map: { Date.new($_) };
my $days = $target - $bday;

say "Day $days:";
for %cycles.sort(+*.value)».kv -> ($label, $length) {
    my $position = $days % $length;
    my $quadrant = floor($position / $length * 4);
    my $percentage = floor(sin($position / $length * 2 * π )*1000)/10;
    my $description;
    if $percentage > 95 {
        $description = 'peak';
    } elsif $percentage < -95 {
        $description = 'valley';
    } elsif abs($percentage) < 5 {
        $description = 'critical transition'
    } else {
        my $transition = $target + floor(($quadrant + 1)/4 * $length) - $position;
        my ($trend, $next) = @quadrants[$quadrant];
        $description = "$percentage% ($trend, next $next $transition)";
    }
    say "$label day $position: $description";
  }
}


       ,            (a comma)        indicates today's date
       *            (an asterisk)    indicates today's date
       yyyy-mm-dd   where yyyy may be a 2- or 4-digit year, mm may be a 1- or 2-digit month, dd may be a 1- or 2-digit day of month
       mm/dd/yyyy   (as above)
       mm/dd        (as above),  but the current year is assumed
       dd\mm\yyyy   (as above)
       dd\mm        (as above),  but the current year is assumed


SeedRandom[13122345];
seq = BioSequence["DNA", "ATAAACGTACGTTTTTAGGCT"];
randompos = RandomInteger[seq["SequenceLength"]];
seq = StringReplacePart[seq, RandomChoice[{"A", "T", "C", "G"}], {randompos, randompos}];
randompos = RandomInteger[seq["SequenceLength"]];
seq = StringReplacePart[seq, "", {randompos, randompos}];
randompos = RandomInteger[seq["SequenceLength"]];
seq = StringInsert[seq, RandomChoice[{"A", "T", "C", "G"}], randompos];
seq = BioSequence["DNA", [email protected][{"A", "T", "C", "G"}, 250]];
size = 50;
parts = StringPartition[seq["SequenceString"], UpTo[size]];
begins = Most[Accumulate[Prepend[StringLength /@ parts, 1]]];
ends = Rest[Accumulate[Prepend[StringLength /@ parts, 0]]];
StringRiffle[MapThread[ToString[#1] <> "-" <> ToString[#2] <> ": " <> #3 &, {begins, ends, parts}], "\n"]
Tally[Characters[seq["SequenceString"]]]
Do[
 type = RandomChoice[{1, 2, 3}];
 Switch[type, 1,
  randompos = RandomInteger[seq["SequenceLength"]];
  seq = StringReplacePart[seq, RandomChoice[{"A", "T", "C", "G"}], {randompos, randompos}];
  , 2,
  randompos = RandomInteger[seq["SequenceLength"]];
  seq = StringReplacePart[seq, "", {randompos, randompos}];
  , 3,
  randompos = RandomInteger[seq["SequenceLength"]];
  seq = StringInsert[seq, RandomChoice[{"A", "T", "C", "G"}], randompos];
  ]
 ,
 {10}
 ]
parts = StringPartition[seq["SequenceString"], UpTo[size]];
begins = Most[Accumulate[Prepend[StringLength /@ parts, 1]]];
ends = Rest[Accumulate[Prepend[StringLength /@ parts, 0]]];
StringRiffle[MapThread[ToString[#1] <> "-" <> ToString[#2] <> ": " <> #3 &, {begins, ends, parts}], "\n"]
Tally[Characters[seq["SequenceString"]]]


import random
import strformat
import strutils

type

  # Enumeration type for bases.
  Base {.pure.} = enum A, C, G, T, Other = "other"

  # Sequence of bases.
  DnaSequence = string

  # Kind of mutation.
  Mutation = enum mutSwap, mutDelete, mutInsert

const MaxBaseVal = ord(Base.high) - 1   # Maximum base value.

#---------------------------------------------------------------------------------------------------

template toChar(base: Base): char = ($base)[0]

#---------------------------------------------------------------------------------------------------

proc newDnaSeq(length: Natural): DnaSequence =
  ## Create a DNA sequence of given length.

  result = newStringOfCap(length)
  for _ in 1..length:
    result.add($Base(rand(MaxBaseVal)))

#---------------------------------------------------------------------------------------------------

proc mutate(dnaSeq: var DnaSequence) =
  ## Mutate a sequence (it is changed in place).

  # Choose randomly the position of mutation.
  let idx = rand(dnaSeq.high)

  # Choose randomly the kind of mutation.
  let mut = Mutation(rand(ord(Mutation.high)))

  # Apply the mutation.
  case mut

  of mutSwap:
    let newBase = Base(rand(MaxBaseVal))
    echo fmt"Changing base at position {idx + 1} from {dnaSeq[idx]} to {newBase}"
    dnaSeq[idx] = newBase.toChar

  of mutDelete:
    echo fmt"Deleting base {dnaSeq[idx]} at position {idx + 1}"
    dnaSeq.delete(idx, idx)

  of mutInsert:
    let newBase = Base(rand(MaxBaseVal))
    echo fmt"Inserting base {newBase} at position {idx + 1}"
    dnaSeq.insert($newBase, idx)

#---------------------------------------------------------------------------------------------------

proc display(dnaSeq: DnaSequence) =
  ## Display a DNA sequence using EMBL format.

  var counts: array[Base, Natural]    # Count of bases.
  for c in dnaSeq:
    inc counts[parseEnum[Base]($c, Other)]  # Use Other as default value.

  # Display the SQ line.
  var sqline = fmt"SQ   {dnaSeq.len} BP; "
  for (base, count) in counts.pairs:
    sqline &= fmt"{count} {base}; "
  echo sqline

  # Display the sequence.
  var idx = 0
  var row = newStringOfCap(80)
  var remaining = dnaSeq.len

  while remaining > 0:
    row.setLen(0)
    row.add("     ")

    # Add groups of 10 bases.
    for group in 1..6:
      let nextIdx = idx + min(10, remaining)
      for i in idx..<nextIdx:
        row.add($dnaSeq[i])
      row.add(' ')
      dec remaining, nextIdx - idx
      idx = nextIdx
      if remaining == 0:
        break

    # Append the number of the last base in the row.
    row.add(spaces(72 - row.len))
    row.add(fmt"{idx:>8}")
    echo row

  # Add termination.
  echo "//"

#———————————————————————————————————————————————————————————————————————————————————————————————————

randomize()
var dnaSeq = newDnaSeq(200)
echo "Initial sequence"
echo "———————————————\n"
dnaSeq.display()

echo "\nMutations"
echo "—————————\n"
for _ in 1..10:
  dnaSeq.mutate()

echo "\nMutated sequence"
echo "————————————————\n"
dnaSeq.display()


use strict;
use warnings;
use feature 'say';

my @bases = <A C G T>;

my $dna;
$dna .= $bases[int rand 4] for 1..200;

my %cnt;
$cnt{$_}++ for split //, $dna;

sub pretty {
    my($string) = @_;
    my $chunk = 10;
    my $wrap  = 5 * ($chunk+1);
    ($string =~ s/(.{$chunk})/$1 /gr) =~ s/(.{$wrap})/$1\n/gr;
}

sub mutate {
    my($dna,$count) = @_;
    my $orig = $dna;
    substr($dna,rand length $dna,1) = $bases[int rand 4] while $count > diff($orig, $dna) =~ tr/acgt//;
    $dna
}

sub diff {
    my($orig, $repl) = @_;
    for my $i (0 .. -1+length $orig) {
        substr($repl,$i,1, lc substr $repl,$i,1) if substr($orig,$i,1) ne substr($repl,$i,1);
    }
    $repl;
}

say "Original DNA strand:\n" . pretty($dna);
say "Total bases: ". length $dna;
say "$_: $cnt{$_}" for @bases;

my $mutate = mutate($dna, 10);
%cnt = ();
$cnt{$_}++ for split //, $mutate;
say "\nMutated DNA strand:\n" . pretty diff $dna, $mutate;
say "Total bases: ". length $mutate;
say "$_: $cnt{$_}" for @bases;


require 'date'
CYCLES = {physical: 23, emotional: 28, mental: 33}

def biorhythms(date_of_birth, target_date = Date.today.to_s)
  days_alive = Date.parse(target_date) - Date.parse(date_of_birth)
  CYCLES.each do |name, num_days|
    cycle_day = days_alive % num_days
    state = case cycle_day
      when 0, num_days/2 then "neutral"
      when (1.. num_days/2) then "positive"
      when (num_days/2+1..num_days) then "negative"
    end
    puts "%-10s: cycle day %2s, %s" % [name, cycle_day.to_i, state]
  end
end

biorhythms("1943-03-09", "1972-07-11")


#!/usr/bin/env wish
# Biorhythm calculator
set today [clock format [clock seconds] -format %Y-%m-%d ]
proc main [list birthday [list target $today]] {
  set day [days-between $birthday $target]

  array set cycles {
    Physical  {23 red}
    Emotional {28 green}
    Mental    {33 blue}
  }

  set pi [expr atan2(0,-1)]

  canvas .c -width 306 -height 350 -bg black
  .c create rectangle 4 49 306 251 -outline grey
  .c create line 5 150 305 150 -fill grey
  .c create line 145 50 145 250 -fill cyan
  .c create text 145 15 -text "$target" -fill cyan
  .c create text 145 30 -text "(Day $day)" -fill cyan
  set ly 305
  foreach {name data} [array get cycles] {
    lassign $data length color
    .c create text 60 $ly -anchor nw -text $name -fill $color
    set pos [expr $day % $length]
    for {set dd -14} {$dd <= 16} {incr dd} {
      set d [expr $pos + $dd]
      set x [expr 145 + 10 * $dd]
      .c create line $x 145 $x 155 -fill grey
      set v [expr sin(2*$pi*$d/$length)]
      set y [expr 150 - 100 * $v]
      if {$dd == 0} {
        .c create text 10 $ly -anchor nw \
            -text "[format %+04.1f%% [expr $v * 100]]" -fill $color
      }
      if [info exists ox] {
        .c create line $ox $oy $x $y -fill $color
      }
      set ox $x
      set oy $y
    }
    unset ox oy
    set ly [expr $ly - 25]
  }
  pack .c
}

proc days-between {from to} {
  expr int([rd $to] - [rd $from])
}

# parse an (ISO-formatted) date into a day number
proc rd {date} {
  lassign [scan $date %d-%d-%d] year month day
  set elapsed [expr $year - 1]
  expr {$elapsed * 365 +
       floor($elapsed/4) -
       floor($elapsed/100) +
       floor($elapsed/400) +
       floor( (367*$month-362)/12 ) +
       ($month < 3 ? 0 : ([is-leap $year] ? -1 : -2)) +
       $day}
}

proc is-leap {year} {
  expr {$year % 4 == 0 && ($year % 100 || $year % 400 == 0)}
}

main {*}$argv


string dna = repeat(' ',200+rand(300))
for i=1 to length(dna) do dna[i] = "ACGT"[rand(4)] end for

procedure show()
    sequence acgt = repeat(0,5)
    for i=1 to length(dna) do
        acgt[find(dna[i],"ACGT")] += 1
    end for
    acgt[$] = sum(acgt)
    sequence s = split(trim(join_by(split(join_by(dna,1,10,""),"\n"),1,5," ")),"\n")
    for i=1 to length(s) do
        printf(1,"%3d: %s\n",{(i-1)*50+1,s[i]})
    end for
    printf(1,"\nBase counts: A:%d, C:%d, G:%d, T:%d, total:%d\n",acgt)
end procedure

procedure mutate()
    printf(1,"\n")
    for i=1 to 10 do
        integer p = rand(length(dna)),
                sdi = "SDI"[rand(3)],
                rep = "ACGT"[rand(4)],
                was = dna[p]
        switch sdi do
            case 'S':dna[p] = rep           printf(1,"swapped %c at %d for %c\n",{was,p,rep})
            case 'D':dna[p..p] = ""         printf(1,"deleted %c at %d\n",{was,p})
            case 'I':dna[p..p-1] = ""&rep   printf(1,"inserted %c at %d, before %c\n",{rep,p,was})
        end switch
    end for
    printf(1,"\n")
end procedure

show()
mutate()
show()


Function Biorhythm(Birthdate As Date, Targetdate As Date) As String

'Jagged Array
TextArray = Array(Array("up and rising", "peak"), Array("up but falling", "transition"), Array("down and falling", "valley"), Array("down but rising", "transition"))

DaysBetween = Targetdate - Birthdate

positionP = DaysBetween Mod 23
positionE = DaysBetween Mod 28
positionM = DaysBetween Mod 33

'return the positions - just to return something
Biorhythm = CStr(positionP) & "/" & CStr(positionE) & "/" & CStr(positionM)

quadrantP = Int(4 * positionP / 23)
quadrantE = Int(4 * positionE / 28)
quadrantM = Int(4 * positionM / 33)

percentageP = Round(100 * Sin(2 * WorksheetFunction.Pi * (positionP / 23)), 1)
percentageE = Round(100 * Sin(2 * WorksheetFunction.Pi * (positionE / 28)), 1)
percentageM = Round(100 * Sin(2 * WorksheetFunction.Pi * (positionM / 33)), 1)

transitionP = Targetdate + WorksheetFunction.Floor((quadrantP + 1) / 4 * 23, 1) - positionP
transitionE = Targetdate + WorksheetFunction.Floor((quadrantE + 1) / 4 * 28, 1) - positionE
transitionM = Targetdate + WorksheetFunction.Floor((quadrantM + 1) / 4 * 33, 1) - positionM

Select Case True
    Case percentageP > 95
        textP = "Physical day " & positionP & " : " & "peak"
    Case percentageP < -95
        textP = "Physical day " & positionP & " : " & "valley"
    Case percentageP < 5 And percentageP > -5
        textP = "Physical day " & positionP & " : " & "critical transition"
    Case Else
        textP = "Physical day " & positionP & " : " & percentageP & "% (" & TextArray(quadrantP)(0) & ", next " & TextArray(quadrantP)(1) & " " & transitionP & ")"
End Select

Select Case True
    Case percentageE > 95
        textE = "Emotional day " & positionE & " : " & "peak"
    Case percentageE < -95
        textE = "Emotional day " & positionE & " : " & "valley"
    Case percentageE < 5 And percentageE > -5
        textE = "Emotional day " & positionE & " : " & "critical transition"
    Case Else
        textE = "Emotional day " & positionE & " : " & percentageE & "% (" & TextArray(quadrantE)(0) & ", next " & TextArray(quadrantE)(1) & " " & transitionE & ")"
End Select

Select Case True
    Case percentageM > 95
        textM = "Mental day " & positionM & " : " & "peak"
    Case percentageM < -95
        textM = "Mental day " & positionM & " : " & "valley"
    Case percentageM < 5 And percentageM > -5
        textM = "Mental day " & positionM & " : " & "critical transition"
    Case Else
        textM = "Mental day " & positionM & " : " & percentageM & "% (" & TextArray(quadrantM)(0) & ", next " & TextArray(quadrantM)(1) & " " & transitionM & ")"
End Select

Header1Text = "Born " & Birthdate & ", Target " & Targetdate
Header2Text = "Day " & DaysBetween

'Print Result
Debug.Print Header1Text
Debug.Print Header2Text
Debug.Print textP
Debug.Print textE
Debug.Print textM
Debug.Print ""

End Function


import time
import math

const cycles = ["Physical day ", "Emotional day", "Mental day   "]
const lengths = [23, 28, 33]
const quadrants = [
    ["up and rising", "peak"],
    ["up but falling", "transition"],
    ["down and falling", "valley"],
    ["down but rising", "transition"],
]

// Parameters assumed to be in YYYY-MM-DD format.
fn biorhythms(birth_date string, target_date string) ? {
    bd := time.parse_iso8601(birth_date)?
    td := time.parse_iso8601(target_date)?
    days := int((td-bd).hours() / 24)
    println("Born $birth_date, Target $target_date")
    println("Day $days")
    for i in 0..3 {
        length := lengths[i]
        cycle := cycles[i]
        position := days % length
        quadrant := position * 4 / length
        mut percent := math.sin(2 * math.pi * f64(position) / f64(length))
        percent = math.floor(percent*1000) / 10
        mut descript := ""
        if percent > 95 {
            descript = " peak"
        } else if percent < -95 {
            descript = " valley"
        } else if math.abs(percent) < 5 {
            descript = " critical transition"
        } else {
            days_to_add := (quadrant+1)*length/4 - position
            transition := td.add(time.hour * 24 * time.Duration(days_to_add))
            trend := quadrants[quadrant][0]
            next := quadrants[quadrant][1]
            trans_str := transition.custom_format('YYYY-MM-DD')
            descript = "${percent:5.1f}% ($trend, next $next $trans_str)"
        }
        println("$cycle ${position:2} : $descript")
    }
    println('')
}

fn main() {
    date_pairs := [
        ["1943-03-09", "1972-07-11"],
        ["1809-01-12", "1863-11-19"],
        ["1809-02-12", "1863-11-19"], // correct DOB for Abraham Lincoln
	]
    for date_pair in date_pairs {
        biorhythms(date_pair[0], date_pair[1])?
    }
}


import "/date" for Date
import "/fmt" for Fmt

var cycles = ["Physical day ", "Emotional day", "Mental day   "]
var lengths = [23, 28, 33]
var quadrants = [
    ["up and rising",    "peak"],
    ["up but falling",   "transition"],
    ["down and falling", "valley"],
    ["down but rising",  "transition"]
]

var biorhythms = Fn.new { |birthDate, targetDate|
    var bd = Date.parse(birthDate)
    var td = Date.parse(targetDate)
    var days = (td - bd).days
    Date.default = Date.isoDate
    System.print("Born %(birthDate), Target %(targetDate)")
    System.print("Day %(days)")
    for (i in 0..2) {
        var length   = lengths[i]
        var cycle    = cycles[i]
        var position = days % length
        var quadrant = (position / length * 4).floor
        var percent  = ((2 * Num.pi * position / length).sin * 1000).floor / 10
        var descript = (percent > 95)    ? " peak" :
                       (percent < -95)   ? " valley" :
                       (percent.abs < 5) ? " critical transition" : "other"
        if (descript == "other") {
            var transition  = td.addDays(((quadrant + 1) / 4 * length).floor - position)
            var tn = quadrants[quadrant]
            var trend = tn[0]
            var next = tn[1]
            descript = Fmt.swrite("$5.1f\% ($s, next $s $s)", percent, trend, next, transition)
        }
        Fmt.print("$s $2d : $s", cycle, position, descript)
    }
    System.print()
}

var datePairs = [
    ["1943-03-09", "1972-07-11"],
    ["1809-01-12", "1863-11-19"],
    ["1809-02-12", "1863-11-19"]  // correct DOB for Abraham Lincoln
]
for (datePair in datePairs) biorhythms.call(datePair[0], datePair[1])


#BASE$="ACGT"
#SEQLEN=200
#PROTOCOL=#True

Global dna.s
Define i.i

Procedure pprint()
  Define p.i, cnt.i, sum.i

  For p=1 To Len(dna) Step 50
    Print(RSet(Str(p-1)+": ",5))
    PrintN(Mid(dna,p,50))
  Next
  PrintN("Base counts:")
  For p=1 To 4
    cnt=CountString(dna,Mid(#BASE$,p,1)) : sum+cnt
    Print(Mid(#BASE$,p,1)+": "+Str(cnt)+", ")
  Next
  PrintN("Total: "+Str(sum))
EndProcedure

Procedure InsertAtPos(basenr.i,position.i)
  If #PROTOCOL : PrintN("Insert base "+Mid(#BASE$,basenr,1)+" at position "+Str(position)) : EndIf
  dna=InsertString(dna,Mid(#BASE$,basenr,1),position)
EndProcedure

Procedure EraseAtPos(position.i)
  If #PROTOCOL : PrintN("Erase base "+Mid(dna,position,1)+" at position "+Str(position)) : EndIf
  If position>0 And position<=Len(dna)
    dna=Left(dna,position-1)+Right(dna,Len(dna)-position)
  EndIf
EndProcedure

Procedure OverwriteAtPos(basenr.i,position.i)
  If #PROTOCOL : PrintN("Change base at position "+Str(position)+" from "+Mid(dna,position,1)+" to "+Mid(#BASE$,basenr,1)) : EndIf
  If position>0 And position<=Len(dna)
    position-1
    PokeS(@dna+2*position,Mid(#BASE$,basenr,1),-1,#PB_String_NoZero)
  EndIf
EndProcedure

If OpenConsole()=0 : End 1 : EndIf
For i=1 To #SEQLEN : dna+Mid(#BASE$,Random(4,1),1) : Next
PrintN("Initial sequence:")
pprint()

For i=1 To 10
  Select Random(2)
    Case 0 : InsertAtPos(Random(4,1),Random(Len(dna),1))
    Case 1 : EraseAtPos(Random(Len(dna),1))
    Case 2 : OverwriteAtPos(Random(4,1),Random(Len(dna),1))
  EndSelect
Next

PrintN("After 10 mutations:")
pprint()
Input()


import random
from collections import Counter

def basecount(dna):
    return sorted(Counter(dna).items())

def seq_split(dna, n=50):
    return [dna[i: i+n] for i in range(0, len(dna), n)]

def seq_pp(dna, n=50):
    for i, part in enumerate(seq_split(dna, n)):
        print(f"{i*n:>5}: {part}")
    print("\n  BASECOUNT:")
    tot = 0
    for base, count in basecount(dna):
        print(f"    {base:>3}: {count}")
        tot += count
    base, count = 'TOT', tot
    print(f"    {base:>3}= {count}")

def seq_mutate(dna, count=1, kinds="IDSSSS", choice="ATCG" ):
    mutation = []
    k2txt = dict(I='Insert', D='Delete', S='Substitute')
    for _ in range(count):
        kind = random.choice(kinds)
        index = random.randint(0, len(dna))
        if kind == 'I':    # Insert
            dna = dna[:index] + random.choice(choice) + dna[index:]
        elif kind == 'D' and dna:  # Delete
            dna = dna[:index] + dna[index+1:]
        elif kind == 'S' and dna:  # Substitute
            dna = dna[:index] + random.choice(choice) + dna[index+1:]
        mutation.append((k2txt[kind], index))
    return dna, mutation

if __name__ == '__main__':
    length = 250
    print("SEQUENCE:")
    sequence = ''.join(random.choices('ACGT', weights=(1, 0.8, .9, 1.1), k=length))
    seq_pp(sequence)
    print("\n\nMUTATIONS:")
    mseq, m = seq_mutate(sequence, 10)
    for kind, index in m:
        print(f" {kind:>10} @{index}")
    print()
    seq_pp(mseq)


  [ $ "ACGT" 4 random peek ]      is randomgene     (   --> c )

  [ $ "" swap times
      [ randomgene join ] ]       is randomsequence ( n --> $ )

  [ dup size random
    3 random
    [ table
      [ pluck drop ]
      [ randomgene unrot stuff ]
      [ randomgene unrot poke ] ]
    do ]                          is mutate         ( $ --> $ )

   200 randomsequence
   dup prettyprint cr cr dup tallybases
   cr cr say "Mutating..." cr
   10 times mutate
   dup prettyprint cr cr tallybases


#lang racket

(define current-S-weight (make-parameter 1))
(define current-D-weight (make-parameter 1))
(define current-I-weight (make-parameter 1))

(define bases '(#\A #\C #\G #\T))

(define (fold-sequence seq kons #:finalise (finalise (λ x (apply values x))) . k0s)
  (define (recur seq . ks)
    (if (null? seq)
      (call-with-values (λ () (apply finalise ks)) (λ vs (apply values vs)))
      (call-with-values (λ () (apply kons (car seq) ks)) (λ ks+ (apply recur (cdr seq) ks+)))))
  (apply recur (if (string? seq) (string->list (regexp-replace* #px"[^ACGT]" seq "")) seq) k0s))

(define (sequence->pretty-printed-string seq)
  (define (fmt idx cs-rev) (format "~a: ~a" (~a idx #:width 3 #:align 'right) (list->string (reverse cs-rev))))
  (fold-sequence
    seq
    (λ (b n start-idx lns-rev cs-rev)
       (if (zero? (modulo n 50))
         (values (+ n 1) n (if (pair? cs-rev) (cons (fmt start-idx cs-rev) lns-rev) lns-rev) (cons b null))
         (values (+ n 1) start-idx lns-rev (cons b cs-rev))))
    0 0 null null
    #:finalise (λ (n idx lns-rev cs-rev)
                (string-join (reverse (if (null? cs-rev) lns-rev (cons (fmt idx cs-rev) lns-rev))) "\n"))))

(define (count-bases b as cs gs ts n)
  (values (+ as (if (eq? b #\A) 1 0)) (+ cs (if (eq? b #\C) 1 0)) (+ gs (if (eq? b #\T) 1 0)) (+ ts (if (eq? b #\G) 1 0)) (add1 n)))

(define (report-sequence s)
  (define-values (as cs gs ts n) (fold-sequence s count-bases 0 0 0 0 0))
  (printf "SEQUENCE:~%~a~%" (sequence->pretty-printed-string s))
  (printf "BASE COUNT:~%-----------~%~a~%"
          (string-join (map (λ (c n) (format " ~a :~a" c (~a #:width 4 #:align 'right n))) bases (list as ts cs gs)) "\n"))
  (printf "TOTAL: ~a~%" n))

(define (make-random-sequence-string l)
  (list->string (for/list ((_ l)) (list-ref bases (random 4)))))

(define (weighted-random-call weights-and-functions . args)
  (let loop ((r (random)) (wfs weights-and-functions))
    (if (<= r (car wfs)) (apply (cadr wfs) args) (loop (- r (car wfs)) (cddr wfs)))))

(define (mutate-S s)
  (let ((r (random (string-length s))) (i (string (list-ref bases (random 4)))))
    (printf "Mutate at ~a -> ~a~%" r i)
    (string-append (substring s 0 r) i (substring s (add1 r)))))

(define (mutate-D s)
  (let ((r (random (string-length s))))
    (printf "Delete at ~a~%" r)
    (string-append (substring s 0 r) (substring s (add1 r)))))

(define (mutate-I s)
  (let ((r (random (string-length s))) (i (string (list-ref bases (random 4)))))
    (printf "Insert at ~a -> ~a~%" r i)
    (string-append (substring s 0 r) i (substring s r))))

(define (mutate s)
  (define W (+ (current-S-weight) (current-D-weight) (current-I-weight)))
  (weighted-random-call
    (list (/ (current-S-weight) W) mutate-S (/ (current-D-weight) W) mutate-D (/ (current-I-weight) W) mutate-I)
    s))

(module+
  main
  (define initial-sequence (make-random-sequence-string 200))
  (report-sequence initial-sequence)
  (newline)
  (define s+ (for/fold ((s initial-sequence)) ((_ 10)) (mutate s)))
  (newline)
  (report-sequence s+)
  (newline)
  (define s+d (parameterize ((current-D-weight 5)) (for/fold ((s initial-sequence)) ((_ 10)) (mutate s))))
  (newline)
  (report-sequence s+d))


my @bases = <A C G T>;

# The DNA strand
my $dna = @bases.roll(200).join;

# The Task
put "ORIGINAL DNA STRAND:";
put pretty $dna;
put "\nTotal bases: ", +my $bases = $dna.comb.Bag;
put $bases.sort( ~*.key ).join: "\n";

put "\nMUTATED DNA STRAND:";
my $mutate = $dna.&mutate(10);
put pretty diff $dna, $mutate;
put "\nTotal bases: ", +my $mutated = $mutate.comb.Bag;
put $mutated.sort( ~*.key ).join: "\n";

# Helper subs
sub pretty ($string, $wrap = 50) {
    $string.comb($wrap).map( { sprintf "%8d: %s", $++ * $wrap, $_ } ).join: "\n"
}

sub mutate ($dna is copy, $count = 1) {
    $dna.substr-rw((^$dna.chars).roll, 1) = @bases.roll for ^$count;
    $dna
}

sub diff ($orig, $repl) {
    ($orig.comb Z $repl.comb).map( -> ($o, $r) { $o eq $r ?? $o !! $r.lc }).join
}


row = 0
dnaList = []
base = ["A","C","G","T"]
long = 20
see "Initial sequence:" + nl
see "     12345678901234567890" + nl
see " " + long + ": "

for nr = 1 to 200
    row = row + 1
    rnd = random(3)+1
    baseStr = base[rnd]
    see baseStr # + " "
    if (row%20) = 0 and long < 200
        long = long + 20
        see nl
        if long < 100
           see " " + long + ": "
        else
           see "" + long + ": "
        ok
    ok
    add(dnaList,baseStr)
next
see nl+ "     12345678901234567890" + nl

baseCount(dnaList)

for n = 1 to 10
    rnd = random(2)+1
    switch rnd
           on 1
              baseSwap(dnaList)
           on 2
              baseDelete(dnaList)
           on 3
              baseInsert(dnaList)
    off
next
showDna(dnaList)
baseCount(dnaList)

func baseInsert(dnaList)
     rnd1 = random(len(dnaList)-1)+1
     rnd2 = random(len(base)-1)+1
     insert(dnaList,rnd1,base[rnd2])
     see "Insert base " + base[rnd2] + " at position " + rnd1 + nl
     return dnaList

func baseDelete(dnaList)
     rnd = random(len(dnaList)-1)+1
     del(dnaList,rnd)
     see "Erase base " + dnaList[rnd] + " at position " + rnd + nl
     return dnaList

func baseSwap(dnaList)
     rnd1 = random(len(dnaList))
     rnd2 = random(3)+1
     see "Change base at position " + rnd1 + " from " + dnaList[rnd1] + " to " + base[rnd2] + nl
     dnaList[rnd1] = base[rnd2]

func showDna(dnaList)
     long = 20
     see nl + "After 10 mutations:" + nl
     see "     12345678901234567890" + nl
     see " " + long + ": "
     for nr = 1 to len(dnaList)
         row = row + 1
         see dnaList[nr]
         if (row%20) = 0 and long < 200
             long = long + 20
             see nl
             if long < 100
                see " " + long + ": "
             else
                see "" + long + ": "
             ok
         ok
     next
     see nl+ "     12345678901234567890" + nl

func baseCount(dnaList)
     dnaBase = [:A=0, :C=0, :G=0, :T=0]
     lenDna = len(dnaList)
     for n = 1 to lenDna
         dnaStr = dnaList[n]
         switch dnaStr
                on "A"
                   strA = dnaBase["A"]
                   strA++
                   dnaBase["A"] = strA
                on "C"
                   strC = dnaBase["C"]
                   strC++
                   dnaBase["C"] = strC
                on "G"
                   strG = dnaBase["G"]
                   strG++
                   dnaBase["G"] = strG
                on "T"
                   strT = dnaBase["T"]
                   strT++
                   dnaBase["T"] = strT
         off
     next
     see nl
     see "A: " + dnaBase["A"] + ", "
     see "T: " + dnaBase["T"] + ", "
     see "C: " + dnaBase["C"] + ", "
     see "G: " + dnaBase["G"] + ", "
     total = dnaBase["A"] + dnaBase["T"] + dnaBase["C"] + dnaBase["G"]
     see "Total: " + total+ nl + nl


F basecount(dna)
   DefaultDict[Char, Int] d
   L(c) dna
      d[c]++
   R sorted(d.items())

F seq_split(dna, n = 50)
   R (0 .< dna.len).step(n).map(i -> @dna[i .+ @n])

F seq_pp(dna, n = 50)
   L(part) seq_split(dna, n)
      print(‘#5: #.’.format(L.index * n, part))
   print("\n  BASECOUNT:")
   V tot = 0
   L(base, count) basecount(dna)
      print(‘    #3: #.’.format(base, count))
      tot += count
   V (base, count) = (‘TOT’, tot)
   print(‘    #3= #.’.format(base, count))

print(‘SEQUENCE:’)
V sequence = "\
CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG\
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG\
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT\
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT\
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG\
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA\
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT\
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG\
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC\
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"
seq_pp(sequence)


class DNA_Seq
  attr_accessor :seq

  def initialize(bases: %i[A C G T] , size: 0)
    @bases = bases
    @seq   = Array.new(size){ bases.sample }
  end

  def mutate(n = 10)
    n.times{|n| method([:s, :d, :i].sample).call}
  end

  def to_s(n = 50)
    just_size = @seq.size / n
    (0[email protected]size).step(n).map{|from|  "#{from.to_s.rjust(just_size)} " + @seq[from, n].join}.join("\n") +
    "\nTotal #{seq.size}: #{@seq.tally.sort.to_h.inspect}\n\n"
  end

  def s = @seq[rand_index]= @bases.sample
  def d = @seq.delete_at(rand_index)
  def i = @seq.insert(rand_index, @bases.sample )
  alias :swap   :s
  alias :delete :d
  alias :insert :i

  private
  def rand_index = rand( @seq.size )
end

puts test = DNA_Seq.new(size: 200)
test.mutate
puts test
test.delete
puts test


let bases: [Character] = ["A", "C", "G", "T"]

enum Action: CaseIterable {
  case swap, delete, insert
}

@discardableResult
func mutate(dna: inout String) -> Action {
  guard let i = dna.indices.shuffled().first(where: { $0 != dna.endIndex }) else {
    fatalError()
  }

  let action = Action.allCases.randomElement()!

  switch action {
  case .swap:
    dna.replaceSubrange(i..<i, with: [bases.randomElement()!])
  case .delete:
    dna.remove(at: i)
  case .insert:
    dna.insert(bases.randomElement()!, at: i)
  }

  return action
}

var d = ""

for _ in 0..<200 {
  d.append(bases.randomElement()!)
}

func printSeq(_ dna: String) {
  for startI in stride(from: 0, to: dna.count, by: 50) {
    print("\(startI): \(dna.dropFirst(startI).prefix(50))")
  }

  print()
  print("Size: \(dna.count)")
  print()

  let counts = dna.reduce(into: [:], { $0[$1, default: 0] += 1 })

  for (char, count) in counts.sorted(by: { $0.key < $1.key }) {
    print("\(char): \(count)")
  }
}

printSeq(d)

print()

for _ in 0..<20 {
  mutate(dna: &d)
}

printSeq(d)


DEFINE PTR="CARD"

PROC PrettyPrint(PTR ARRAY data INT count,gsize,gcount)
  INT index,item,i,ingroup,group,a,t,c,g
  CHAR ARRAY s
  CHAR ch

  index=0 item=0 i=1 ingroup=0 group=0
  a=0 t=0 g=0 c=0
  s=data(0)
  DO
    WHILE i>s(0)
    DO
      i=1 item==+1
      IF item>=count THEN EXIT FI
      s=data(item)
    OD
    IF item>=count THEN EXIT FI

    index==+1
    IF group=0 AND ingroup=0 THEN
      IF index<10 THEN Put(32) FI
      IF index<100 THEN Put(32) FI
      PrintI(index) Print(":")
    FI
    IF ingroup=0 THEN Put(32) FI
    ch=s(i) i==+1
    Put(ch)
    IF ch='A THEN a==+1
    ELSEIF ch='T THEN t==+1
    ELSEIF ch='C THEN c==+1
    ELSEIF ch='G THEN g==+1 FI
    ingroup==+1
    IF ingroup>=gsize THEN
      ingroup=0 group==+1
      IF group>=gcount THEN
        group=0
      FI
    FI
  OD
  PrintF("%E%EBases: A:%I, T:%I, C:%I, G:%I%E",a,t,c,g)
  PrintF("%ETotal: %I",a+t+g+c)
RETURN

PROC Main()
  PTR ARRAY data(10)
  BYTE LMARGIN=$52,oldLMARGIN

  oldLMARGIN=LMARGIN
  LMARGIN=0 ;remove left margin on the screen
  Put(125) PutE() ;clear the screen

  data(0)="CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG"
  data(1)="CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG"
  data(2)="AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT"
  data(3)="GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT"
  data(4)="CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG"
  data(5)="TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA"
  data(6)="TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT"
  data(7)="CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG"
  data(8)="TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC"
  data(9)="GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"

  PrettyPrint(data,10,5,6)

  LMARGIN=oldLMARGIN ;restore left margin on the screen
RETURN


with Ada.Text_Io;

procedure Base_Count is

   type Sequence is new String;
   Test : constant Sequence :=
     "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG" &
     "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG" &
     "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT" &
     "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT" &
     "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG" &
     "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA" &
     "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT" &
     "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG" &
     "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC" &
     "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT";

   Line_Width : constant := 70;

   procedure Put (Seq : Sequence) is
      use Ada.Text_Io;
      package Position_Io is new Ada.Text_Io.Integer_Io (Natural);
      First : Natural := Seq'First;
      Last  : Natural;
   begin
      loop
         Last := Natural'Min (Seq'Last, First + Line_Width - 1);
         Position_Io.Put (First, Width => 3);
         Put (String'(".."));
         Position_Io.Put (Last, Width => 3);
         Put (String'("  "));
         Put (String (Seq (First .. Last)));
         New_Line;
         exit when Last = Seq'Last;
         First := First + Line_Width;
      end loop;
   end Put;

   procedure Count (Seq : Sequence) is
      use Ada.Text_Io;
      A_Count, C_Count : Natural := 0;
      G_Count, T_Count : Natural := 0;
   begin
      for B of Seq loop
         case B is
            when 'A' =>  A_Count := A_Count + 1;
            when 'C' =>  C_Count := C_Count + 1;
            when 'G' =>  G_Count := G_Count + 1;
            when 'T' =>  T_Count := T_Count + 1;
            when others =>
               raise Constraint_Error;
         end case;
      end loop;
      Put_Line ("A: " & A_Count'Image);
      Put_Line ("C: " & C_Count'Image);
      Put_Line ("G: " & G_Count'Image);
      Put_Line ("T: " & T_Count'Image);
      Put_Line ("Total: " & Seq'Length'Image);
   end Count;

begin
   Put (Test);
   Count (Test);
end Base_Count;


BEGIN # count DNA bases in a sequence                                        #
    # returns an array of counts of the characters in s that are in c        #
    #         an extra final element holds the count of characters not in c  #
    PRIO COUNT = 9;
    OP   COUNT = ( STRING s, STRING c )[]INT:
         BEGIN
            [ LWB c : UPB c + 1 ]INT results; # extra element for "other"    #
            [ 0     : 255       ]INT counts;  # only counts ASCII characters #
            FOR i FROM LWB counts  TO UPB counts  DO counts[  i ] := 0 OD;
            FOR i FROM LWB results TO UPB results DO results[ i ] := 0 OD;
            # count the occurrences of each ASCII character in s              #
            FOR i FROM LWB s TO UPB s DO
                IF INT ch pos = ABS s[ i ];
                   ch pos >= LWB counts AND ch pos <= UPB counts
                THEN
                    # have a character we can count                          #
                    counts[ ch pos ] +:= 1
                ELSE
                    # not an ASCII character ?                               #
                    results[ UPB results ] +:= 1
                FI
            OD;
            # return the counts of the required characters                   #
            # set the results for the expected characters and clear their    #
            # counts so we can count the "other" characters                  #
            FOR i FROM LWB results TO UPB results - 1 DO
                IF INT ch pos = ABS c[ i ];
                   ch pos >= LWB counts AND ch pos <= UPB counts
                THEN
                    results[ i ]     := counts[ ch pos ];
                    counts[ ch pos ] := 0
                FI
            OD;
            # count the "other" characters                                   #
            FOR i FROM LWB counts TO UPB counts DO
                IF counts[ i ] /= 0 THEN
                    results[ UPB results ] +:= counts[ i ]
                FI
            OD;
            results
         END; # COUNT #
    # returns the combined counts of the characters in the elements of s     #
    #         that are in c                                                  #
    #         an extra final element holds the count of characters not in c  #
    OP   COUNT = ( []STRING s, STRING c )[]INT:
         BEGIN
            [ LWB c : UPB c + 1 ]INT results;
            FOR i FROM LWB results TO UPB results DO results[ i ] := 0 OD;
            FOR i FROM LWB s TO UPB s DO
                []INT counts = s[ i ] COUNT c;
                FOR i FROM LWB results TO UPB results DO
                   results[ i ] +:= counts[ i ]
                OD
            OD;
            results
         END; # COUNT #
    # returns the length of s                                                #
    OP   LEN = ( STRING s )INT: ( UPB s - LWB s ) + 1;
    # count the bases in the required sequence                               #
    []STRING seq = ( "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG"
                   , "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG"
                   , "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT"
                   , "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT"
                   , "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG"
                   , "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA"
                   , "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT"
                   , "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG"
                   , "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC"
                   , "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"
                   );
    STRING bases  = "ATCG";
    []INT  counts = seq COUNT bases;
    # print the sequence with leading character positions                    #
    # find the overall length of the sequence                                #
    INT   seq len := 0;
    FOR i FROM LWB seq TO UPB seq DO
        seq len +:= LEN seq[ i ]
    OD;
    # compute the minimum field width required for the positions             #
    INT   s len   := seq len;
    INT   width   := 1;
    WHILE  s len >= 10 DO
        width +:= 1;
        s len OVERAB 10
    OD;
    # show the sequence                                                      #
    print( ( "Sequence:", newline, newline ) );
    INT start := 0;
    FOR i FROM LWB seq TO UPB seq DO
        print( ( " ", whole( start, - width ), " :", seq[ i ], newline ) );
        start +:= LEN seq[ i ]
    OD;
    # show the base counts                                                   #
    print( ( newline, "Bases: ", newline, newline ) );
    INT total := 0;
    FOR i FROM LWB bases TO UPB bases DO
        print( ( "  ", bases[ i ], "  : ", whole( counts[ i ], - width ), newline ) );
        total +:= counts[ i ]
    OD;
    # show the count of other characters (invalid bases) - if there are any  #
    IF INT others = UPB counts;
       counts[ others ] /= 0
    THEN
        # there were characters other than the bases                         #
        print( ( newline, "Other: ", whole( counts[ others ], - width ), newline, newline ) );
        total +:= counts[ UPB counts ]
    FI;
    # totals                                                                 #
    print( ( newline, "Total: ", whole( total, - width ), newline ) )
END


import rand
import rand.seed

const bases = "ACGT"

// 'w' contains the weights out of 300 for each
// of swap, delete or insert in that order.
fn mutate(dna string, w [3]int) string {
    le := dna.len
    // get a random position in the dna to mutate
    p := rand.intn(le) or {0}
    // get a random number between 0 and 299 inclusive
    r := rand.intn(300) or {0}
    mut bytes := dna.bytes()
    match true {
		r < w[0] { // swap
			base := bases[rand.intn(4) or {0}]
			println("  Change @${p:3} ${[bytes[p]].bytestr()} to ${[base].bytestr()}")
			bytes[p] = base
		}
		r < w[0]+w[1] { // delete
			println("  Delete @${p:3} ${bytes[p]}")
			bytes.delete(p)
			//copy(bytes[p:], bytes[p+1:])
			bytes = bytes[0..le-1]
		}
		else { // insert
			base := bases[rand.intn(4) or {0}]
			bytes << 0
			bytes.insert(p,bytes[p])
			//copy(bytes[p+1:], bytes[p:])
			println("  Insert @${p:3} $base")
			bytes[p] = base
		}
    }
    return bytes.bytestr()
}

// Generate a random dna sequence of given length.
fn generate(le int) string {
    mut bytes := []u8{len:le}
    for i := 0; i < le; i++ {
        bytes[i] = bases[rand.intn(4) or {0}]
    }
    return bytes.bytestr()
}

// Pretty print dna and stats.
fn pretty_print(dna string, rowLen int) {
    println("SEQUENCE:")
    le := dna.len
    for i := 0; i < le; i += rowLen {
        mut k := i + rowLen
        if k > le {
            k = le
        }
        println("${i:5}: ${dna[i..k]}")
    }
    mut base_map := map[byte]int{} // allows for 'any' base
    for i in 0..le {
        base_map[dna[i]]++
    }
    mut bb := base_map.keys()
	bb.sort()

    println("\nBASE COUNT:")
    for base in bb {
        println("    $base: ${base_map[base]:3}")
    }
    println("    ------")
    println("    Σ: $le")
    println("    ======\n")
}

// Express weights as a string.
fn wstring(w [3]int) string {
    return "  Change: ${w[0]}\n  Delete: ${w[1]}\n  Insert: ${w[2]}\n"
}

fn main() {
    rand.seed(seed.time_seed_array(2))
    mut dna := generate(250)
    pretty_print(dna, 50)
    muts := 10
    w := [100, 100, 100]! // use e.g. {0, 300, 0} to choose only deletions
    println("WEIGHTS (ex 300):\n${wstring(w)}")
    println("MUTATIONS ($muts):")
    for _ in 0..muts {
        dna = mutate(dna, w)
    }
    println('')
    pretty_print(dna, 50)
}


import "random" for Random
import "/fmt" for Fmt
import "/sort" for Sort

var rand = Random.new()
var bases = "ACGT"

// 'w' contains the weights out of 300 for each
// of swap, delete or insert in that order.
var mutate = Fn.new { |dna, w|
    var le = dna.count
    // get a random position in the dna to mutate
    var p = rand.int(le)
    // get a random number between 0 and 299 inclusive
    var r = rand.int(300)
    var chars = dna.toList
    if (r < w[0]) {   // swap
        var base = bases[rand.int(4)]
        Fmt.print("  Change @$3d $q to $q", p, chars[p], base)
        chars[p] = base
    } else if (r < w[0] + w[1]) {  // delete
        Fmt.print("  Delete @$3d $q", p, chars[p])
        chars.removeAt(p)
    } else { // insert
        var base = bases[rand.int(4)]
        Fmt.print("  Insert @$3d $q", p, base)
        chars.insert(p, base)
    }
    return chars.join()
}

// Generate a random dna sequence of given length.
var generate = Fn.new { |le|
    var chars = [""] * le
    for (i in 0...le) chars[i] = bases[rand.int(4)]
    return chars.join()
}

// Pretty print dna and stats.
var prettyPrint = Fn.new { |dna, rowLen|
    System.print("SEQUENCE:")
    var le = dna.count
    var i = 0
    while (i < le) {
        var k = i + rowLen
        if (k > le) k = le
        Fmt.print("$5d: $s", i, dna[i...k])
        i =  i + rowLen
    }
    var baseMap = {}
    for (i in 0...le) {
        var v = baseMap[dna[i]]
        baseMap[dna[i]] = (v) ? v + 1 : 1
    }
    var bases = []
    for (k in baseMap.keys) bases.add(k)
    Sort.insertion(bases) // get bases into alphabetic order
    System.print("\nBASE COUNT:")
    for (base in bases) Fmt.print("    $s: $3d", base, baseMap[base])
    System.print("    ------")
    System.print("    Σ: %(le)")
    System.print("    ======\n")
}

// Express weights as a string.
var wstring = Fn.new { |w|
    return Fmt.swrite("  Change: $d\n  Delete: $d\n  Insert: $d\n", w[0], w[1], w[2])
}

var dna = generate.call(250)
prettyPrint.call(dna, 50)
var muts = 10
var w = [100, 100, 100] // use e.g. {0, 300, 0} to choose only deletions
Fmt.print("WEIGHTS (ex 300):\n$s", wstring.call(w))
Fmt.print("MUTATIONS ($d):", muts)
for (i in 0...muts) dna = mutate.call(dna, w)
System.print()
prettyPrint.call(dna, 50)


dna: {
    CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
    CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
    AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
    GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
    CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
    TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
    TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
    CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
    TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
    GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT
}

prettyPrint: function [in][
    count: #[ A: 0, T: 0, G: 0, C: 0 ]

    loop.with:'i split.lines in 'line [
        prints [pad to :string i*50 3 ":"]
        print split.every:10 line

        loop split line 'ch [
            case [ch=]
                when? -> "A" -> count\A: count\A + 1
                when? -> "T" -> count\T: count\T + 1
                when? -> "G" -> count\G: count\G + 1
                when? -> "C" -> count\C: count\C + 1
                else []
        ]
    ]
    print ["Total count => A:" count\A, "T:" count\T "G:" count\G "C:" count\C]
]

prettyPrint dna


# syntax: GAWK -f BIOINFORMATICS_BASE_COUNT.AWK
# converted from FreeBASIC
#
# sorting:
#   PROCINFO["sorted_in"] is used by GAWK
#   SORTTYPE is used by Thompson Automation's TAWK
#
BEGIN {
    dna = "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG" \
          "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG" \
          "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT" \
          "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT" \
          "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG" \
          "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA" \
          "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT" \
          "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG" \
          "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC" \
          "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"
    curr = first = 1
    while (curr <= length(dna)) {
      curr_base = substr(dna,curr,1)
      base_arr[curr_base]++
      rec = sprintf("%s%s",rec,curr_base)
      curr++
      if (curr % 10 == 1) {
        rec = sprintf("%s ",rec)
      }
      if (curr % 50 == 1) {
        printf("%3d-%3d: %s\n",first,curr-1,rec)
        rec = ""
        first = curr
      }
    }
    PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
    printf("\nBase count\n")
    for (i in base_arr) {
      printf("%s %8d\n",i,base_arr[i])
      total += base_arr[i]
    }
    printf("%10d total\n",total)
    exit(0)
}


var [const] bases="ACGT", lbases=bases.toLower();
dna:=(190).pump(Data().howza(3),(0).random.fp(0,4),bases.get); // bucket of bytes

foreach s,m in (T("Original","Mutated").zip(T(True,False))){
   println("\n",s," DNA strand:"); dnaPP(dna);
   println("Base Counts: ", dna.len()," : ",
      dna.text.toUpper().counts()	// ("A",5, "C",10, ...)
      .pump(String,Void.Read,"%s(%d) ".fmt));
   if(m) mutate(dna,10,True);
}

fcn mutate(dna,count=1,verbose=False){
   if(verbose) println("Mutating:");
   do(count){
      n,rb := (0).random(dna.len()), lbases[(0).random(4)];
      switch( (0).random(3) ){
	 case(0){ if(verbose) println("Change[%d] '%s' to '%s'".fmt(n,dna.charAt(n),rb));
	    dna[n]=rb;
	 }
	 case(1){ if(verbose) println("Delete[%d] '%s'".fmt(n,dna.charAt(n)));
	    dna.del(n);
	 }
	 else{	  if(verbose) println("Insert[%d] '%s'".fmt(n,rb));
	    dna.insert(n,rb);
	 }
      }
   }
}

fcn dnaPP(dna,N=50){
   [0..*,50].zipWith(fcn(n,bases){ println("%6d: %s".fmt(n,bases.concat())) },
      dna.walker().walk.fp(50)).pump(Void);  // .pump forces the iterator
}


use rand::prelude::*;
use std::collections::HashMap;
use std::fmt::{Display, Formatter, Error};

pub struct Seq<'a> {
    alphabet: Vec<&'a str>,
    distr: rand::distributions::Uniform<usize>,
    pos_distr: rand::distributions::Uniform<usize>,
    seq: Vec<&'a str>,
}

impl Display for Seq<'_> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {

        let pretty: String = self.seq
            .iter()
            .enumerate()
            .map(|(i, nt)| if (i + 1) % 60 == 0 { format!("{}\n", nt) } else { nt.to_string() })
            .collect();

        let counts_hm = self.seq
            .iter()
            .fold(HashMap::<&str, usize>::new(), |mut m, nt| {
                *m.entry(nt).or_default() += 1;
                m
            });

        let mut counts_vec: Vec<(&str, usize)> = counts_hm.into_iter().collect();
        counts_vec.sort_by(|a, b| a.0.cmp(&b.0));
        let counts_string = counts_vec
            .iter()
            .fold(String::new(), |mut counts_string, (nt, count)| {
                counts_string += &format!("{} = {}\n", nt, count);
                counts_string
            });

        write!(f, "Seq:\n{}\n\nLength: {}\n\nCounts:\n{}", pretty, self.seq.len(), counts_string)
    }
}

impl Seq<'_> {
    pub fn new(alphabet: Vec<&str>, len: usize) -> Seq {
        let distr = rand::distributions::Uniform::new_inclusive(0, alphabet.len() - 1);
        let pos_distr = rand::distributions::Uniform::new_inclusive(0, len - 1);

        let seq: Vec<&str> = (0..len)
            .map(|_| {
                alphabet[thread_rng().sample(distr)]
            })
            .collect();
        Seq { alphabet, distr, pos_distr, seq }
    }

    pub fn insert(&mut self) {
        let pos = thread_rng().sample(self.pos_distr);
        let nt = self.alphabet[thread_rng().sample(self.distr)];
        println!("Inserting {} at position {}", nt, pos);
        self.seq.insert(pos, nt);
    }

    pub fn delete(&mut self) {
        let pos = thread_rng().sample(self.pos_distr);
        println!("Deleting {} at position {}", self.seq[pos], pos);
        self.seq.remove(pos);
    }

    pub fn swap(&mut self) {
        let pos = thread_rng().sample(self.pos_distr);
        let cur_nt = self.seq[pos];
        let new_nt = self.alphabet[thread_rng().sample(self.distr)];
        println!("Replacing {} at position {} with {}", cur_nt, pos, new_nt);
        self.seq[pos] = new_nt;
    }
}

fn main() {

    let mut seq = Seq::new(vec!["A", "C", "T", "G"], 200);
    println!("Initial sequnce:\n{}", seq);

    let mut_distr = rand::distributions::Uniform::new_inclusive(0, 2);

    for _ in 0..10 {
        let mutation = thread_rng().sample(mut_distr);

        if mutation == 0 {
            seq.insert()
        } else if mutation == 1 {
            seq.delete()
        } else {
            seq.swap()
        }
    }

    println!("\nMutated sequence:\n{}", seq);
}


#include<string.h>
#include<stdlib.h>
#include<stdio.h>

typedef struct genome{
    char* strand;
    int length;
    struct genome* next;
}genome;

genome* genomeData;
int totalLength = 0, Adenine = 0, Cytosine = 0, Guanine = 0, Thymine = 0;

int numDigits(int num){
    int len = 1;

    while(num>10){
        num = num/10;
        len++;
    }

    return len;
}

void buildGenome(char str[100]){
    int len = strlen(str),i;
    genome *genomeIterator, *newGenome;

    totalLength += len;

    for(i=0;i<len;i++){
        switch(str[i]){
            case 'A': Adenine++;
                break;
            case 'T': Thymine++;
                break;
            case 'C': Cytosine++;
                break;
            case 'G': Guanine++;
                break;
        };
    }

    if(genomeData==NULL){
        genomeData = (genome*)malloc(sizeof(genome));

        genomeData->strand = (char*)malloc(len*sizeof(char));
        strcpy(genomeData->strand,str);
        genomeData->length = len;

        genomeData->next = NULL;
    }

    else{
        genomeIterator = genomeData;

        while(genomeIterator->next!=NULL)
            genomeIterator = genomeIterator->next;

        newGenome = (genome*)malloc(sizeof(genome));

        newGenome->strand = (char*)malloc(len*sizeof(char));
        strcpy(newGenome->strand,str);
        newGenome->length = len;

        newGenome->next = NULL;
        genomeIterator->next = newGenome;
    }
}

void printGenome(){
    genome* genomeIterator = genomeData;

    int width = numDigits(totalLength), len = 0;

    printf("Sequence:\n");

    while(genomeIterator!=NULL){
        printf("\n%*d%3s%3s",width+1,len,":",genomeIterator->strand);
        len += genomeIterator->length;

        genomeIterator = genomeIterator->next;
    }

    printf("\n\nBase Count\n----------\n\n");

    printf("%3c%3s%*d\n",'A',":",width+1,Adenine);
    printf("%3c%3s%*d\n",'T',":",width+1,Thymine);
    printf("%3c%3s%*d\n",'C',":",width+1,Cytosine);
    printf("%3c%3s%*d\n",'G',":",width+1,Guanine);
    printf("\n%3s%*d\n","Total:",width+1,Adenine + Thymine + Cytosine + Guanine);

    free(genomeData);
}

int main(int argc,char** argv)
{
    char str[100];
    int counter = 0, len;

    if(argc!=2){
        printf("Usage : %s <Gene file name>\n",argv[0]);
        return 0;
    }

    FILE *fp = fopen(argv[1],"r");

    while(fscanf(fp,"%s",str)!=EOF)
        buildGenome(str);
    fclose(fp);

    printGenome();

    return 0;
}


#include <map>
#include <string>
#include <iostream>
#include <iomanip>

const std::string DEFAULT_DNA = "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG"
                                "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG"
                                "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT"
                                "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT"
                                "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG"
                                "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA"
                                "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT"
                                "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG"
                                "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC"
                                "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT";

class DnaBase {
public:
    DnaBase(const std::string& dna = DEFAULT_DNA, int width = 50) : genome(dna), displayWidth(width) {
        // Map each character to a counter
        for (auto elm : dna) {
            if (count.find(elm) == count.end())
                count[elm] = 0;
            ++count[elm];
        }
    }

    void viewGenome() {
        std::cout << "Sequence:" << std::endl;
        std::cout << std::endl;
        int limit = genome.size() / displayWidth;
        if (genome.size() % displayWidth != 0)
            ++limit;

        for (int i = 0; i < limit; ++i) {
            int beginPos = i * displayWidth;
            std::cout << std::setw(4) << beginPos << "  :" << std::setw(4) << genome.substr(beginPos, displayWidth) << std::endl;
        }
        std::cout << std::endl;
        std::cout << "Base Count" << std::endl;
        std::cout << "----------" << std::endl;
        std::cout << std::endl;
        int total = 0;
        for (auto elm : count) {
            std::cout << std::setw(4) << elm.first << " : " << elm.second << std::endl;
            total += elm.second;
        }
        std::cout << std::endl;
        std::cout << "Total: " << total << std::endl;
    }

private:
    std::string genome;
    std::map<char, int> count;
    int displayWidth;
};

int main(void) {
    auto d = new DnaBase();
    d->viewGenome();
    delete d;
    return 0;
}


program base_count;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  Generics.Collections,
  System.Console;

const
  DNA = 'CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG' +
    'CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG' +
    'AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT' +
    'GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT' +
    'CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG' +
    'TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA' +
    'TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT' +
    'CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG' +
    'TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC' +
    'GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT';

procedure Println(code: ansistring);
var
  c: ansichar;
begin
  console.ForegroundColor := TConsoleColor.Black;
  for c in code do
  begin
    case c of
      'A':
        console.BackgroundColor := TConsoleColor.Red;
      'C':
        console.BackgroundColor := TConsoleColor.Blue;
      'T':
        console.BackgroundColor := TConsoleColor.Green;
      'G':
        console.BackgroundColor := TConsoleColor.Yellow;
    else
      console.BackgroundColor := TConsoleColor.Black;
    end;
    console.Write(c);
  end;
  console.ForegroundColor := TConsoleColor.White;
  console.BackgroundColor := TConsoleColor.Black;
  console.WriteLine;
end;

begin
  console.WriteLine('SEQUENCE:');
  var le := Length(DNA);
  var index := 0;
  while index < le do
  begin
    Write(index: 5, ': ');
    Println(dna.Substring(index, 50));

    inc(index, 50);
  end;

  var baseMap := TDictionary<byte, integer>.Create;

  for var i := 1 to le do
  begin
    var key := ord(dna[i]);
    if baseMap.ContainsKey(key) then
      baseMap[key] := baseMap[key] + 1
    else
      baseMap.Add(key, 1);
  end;

  var bases: TArray<byte>;
  for var k in baseMap.Keys do
  begin
    SetLength(bases, Length(bases) + 1);
    bases[High(bases)] := k;
  end;
  TArray.Sort<Byte>(bases);

  console.WriteLine(#10'BASE COUNT:');

  for var base in bases do
    console.WriteLine('    {0}: {1}', [ansichar(base), baseMap[base]]);

  console.WriteLine('    ------');
  console.WriteLine('    S: {0}', [le]);
  console.WriteLine('    ======');

  readln;
end.


USING: assocs formatting grouping io kernel literals math
math.statistics prettyprint qw sequences sorting ;

CONSTANT: dna
$[
    qw{
        CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
        CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
        AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
        GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
        CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
        TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
        TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
        CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
        TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
        GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT
    } concat
]

: .dna ( seq n -- )
    "SEQUENCE:" print [ group ] keep
    [ * swap "  %3d: %s\n" printf ] curry each-index ;

: show-counts ( seq -- )
    "BASE COUNTS:" print histogram >alist [ first ] sort-with
    [ [ "    %c: %3d\n" printf ] assoc-each ]
    [ "TOTAL: " write [ second ] [ + ] map-reduce . ] bi ;

dna [ 50 .dna nl ] [ show-counts ] bi


( Gforth 0.7.3 )

: dnacode s" CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATGCTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTGAGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGATGGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTTCGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGGTCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATATTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTATCGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTGTCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGACGACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT" ;

variable #A \ Gforth initialises variables to 0
variable #C
variable #G
variable #T
variable #ch
50 constant pplength

: basecount ( adr u -- )
    ." Sequence:"
    swap dup rot + swap ?do  \ count while pretty-printing
        #ch @ pplength mod 0= if cr #ch @ 10 .r 2 spaces then
        i [email protected] dup emit
        dup 'A = if drop #A @ 1+ #A ! else
        dup 'C = if drop #C @ 1+ #C ! else
        dup 'G = if drop #G @ 1+ #G ! else
        dup 'T = if drop #T @ 1+ #T ! else drop then then then then
        #ch @ 1+ #ch !
    loop
    cr cr ." Base counts:"
    cr 4 spaces 'A emit ': emit #A @ 5 .r
    cr 4 spaces 'C emit ': emit #C @ 5 .r
    cr 4 spaces 'G emit ': emit #G @ 5 .r
    cr 4 spaces 'T emit ': emit #T @ 5 .r
    cr ."  ----------"
    cr ."   Sum:"  #ch @ 5 .r
    cr ."  ==========" cr cr
;

( demo run: )

dnacode basecount


#define SCW 36
#define GRP 3

function padto( n as integer, w as integer ) as string
    dim as string r = str(n)
    while len(r)<w
       r = " "+r
    wend
    return r
end function

dim as string dna = "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG"+_
                    "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG"+_
                    "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT"+_
                    "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT"+_
                    "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG"+_
                    "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA"+_
                    "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT"+_
                    "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG"+_
                    "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC"+_
                    "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"

dim as string outstr = "", currb
dim as integer bases(0 to 3), curr = 1, first = 1
while curr <= len(dna)
    currb = mid(dna, curr, 1)
    if currb = "A" then bases(0) += 1
    if currb = "C" then bases(1) += 1
    if currb = "G" then bases(2) += 1
    if currb = "T" then bases(3) += 1
    outstr += currb
    curr += 1
    if curr mod GRP = 1 then outstr += " "
    if curr mod SCW = 1 or curr=len(dna)+1 then
        outstr = padto(first,3) + "--" + padto(curr-1,3) + ":   " + outstr
        print outstr
        outstr = ""
        first = curr
    end if
wend
print
print "Base counts"
print "-----------"
print "     A:  " + str(bases(0))
print "     C:  " + str(bases(1))
print "     G:  " + str(bases(2))
print "     T:  " + str(bases(3))
print
print " total:  " + str(bases(0)+bases(1)+bases(2)+bases(3))


package main

import (
    "fmt"
    "sort"
)

func main() {
    dna := "" +
        "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG" +
        "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG" +
        "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT" +
        "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT" +
        "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG" +
        "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA" +
        "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT" +
        "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG" +
        "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC" +
        "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"

    fmt.Println("SEQUENCE:")
    le := len(dna)
    for i := 0; i < le; i += 50 {
        k := i + 50
        if k > le {
            k = le
        }
        fmt.Printf("%5d: %s\n", i, dna[i:k])
    }
    baseMap := make(map[byte]int) // allows for 'any' base
    for i := 0; i < le; i++ {
        baseMap[dna[i]]++
    }
    var bases []byte
    for k := range baseMap {
        bases = append(bases, k)
    }
    sort.Slice(bases, func(i, j int) bool { // get bases into alphabetic order
        return bases[i] < bases[j]
    })

    fmt.Println("\nBASE COUNT:")
    for _, base := range bases {
        fmt.Printf("    %c: %3d\n", base, baseMap[base])
    }
    fmt.Println("    ------")
    fmt.Println("    Σ:", le)
    fmt.Println("    ======")
}


import Data.List       (group, sort)
import Data.List.Split (chunksOf)
import Text.Printf     (printf, IsChar(..), PrintfArg(..), fmtChar, fmtPrecision, formatString)

data DNABase = A | C | G | T deriving (Show, Read, Eq, Ord)
type DNASequence = [DNABase]

instance IsChar DNABase where
  toChar = head . show
  fromChar = read . pure

instance PrintfArg DNABase where
  formatArg x fmt = formatString (show x) (fmt { fmtChar = 's', fmtPrecision = Nothing })

test :: DNASequence
test = read . pure <$> concat
  [ "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG"
  , "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG"
  , "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT"
  , "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT"
  , "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG"
  , "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA"
  , "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT"
  , "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG"
  , "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC"
  , "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT" ]

chunkedDNASequence :: DNASequence -> [(Int, [DNABase])]
chunkedDNASequence = zip [50,100..] . chunksOf 50

baseCounts :: DNASequence -> [(DNABase, Int)]
baseCounts = fmap ((,) . head <*> length) . group . sort

main :: IO ()
main = do
  putStrLn "Sequence:"
  mapM_ (uncurry (printf "%3d: %s\n")) $ chunkedDNASequence test
  putStrLn "\nBase Counts:"
  mapM_ (uncurry (printf "%2s: %2d\n")) $ baseCounts test
  putStrLn (replicate 8 '-') >> printf " Σ: %d\n\n" (length test)


countBases=: (({.;#)/.~)@,
totalBases=: #@,

require 'format/printf'

printSequence=: verb define
'Sequence:' printf ''
'%4d: %s' printf ((- {.)@(+/\)@:(#"1) ,.&<"_1 ]) y
'\n Base Count\n-----------' printf ''
'%5s: %4d' printf countBases y
'-----------\nTotal = %3d' printf totalBases y
)


import java.util.HashMap;
import java.util.Map;

public class orderedSequence {
    public static void main(String[] args) {
        Sequence gene = new Sequence("CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATGCTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTGAGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGATGGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTTCGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGGTCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATATTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTATCGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTGTCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGACGACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT");
        gene.runSequence();
    }
}

/** Separate class for defining behaviors */
public class Sequence {

    private final String seq;

    public Sequence(String sq) {
        this.seq = sq;
    }

    /** print the organized structure of the sequence */
    public void prettyPrint() {
        System.out.println("Sequence:");
        int i = 0;
        for ( ; i < seq.length() - 50 ; i += 50) {
            System.out.printf("%5s : %s\n", i + 50, seq.substring(i, i + 50));
        }
        System.out.printf("%5s : %s\n", seq.length(), seq.substring(i));
    }

    /** display a base vs. frequency chart */
    public void displayCount() {
        Map<Character, Integer> counter = new HashMap<>();
        for (int i = 0 ; i < seq.length() ; ++i) {
            counter.merge(seq.charAt(i), 1, Integer::sum);
        }

        System.out.println("Base vs. Count:");
        counter.forEach(
            key, value -> System.out.printf("%5s : %s\n", key, value));
        System.out.printf("%5s: %s\n", "SUM", seq.length());
    }

    public void runSequence() {
        this.prettyPrint();
        this.displayCount();
    }
}


const rowLength = 50;

const bases = ['A', 'C', 'G', 'T'];

// Create the starting sequence
const seq = `CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT`
    .split('')
    .filter(e => bases.includes(e))

/**
 * Convert the given array into an array of smaller arrays each with the length
 * given by n.
 * @param {number} n
 * @returns {function(!Array<*>): !Array<!Array<*>>}
 */
const chunk = n => a => a.reduce(
    (p, c, i) => (!(i % n)) ? p.push([c]) && p : p[p.length - 1].push(c) && p,
    []);
const toRows = chunk(rowLength);

/**
 * Given a number, return function that takes a string and left pads it to n
 * @param {number} n
 * @returns {function(string): string}
 */
const padTo = n => v => ('' + v).padStart(n, ' ');
const pad = padTo(5);

/**
 * Count the number of elements that match the given value in an array
 * @param {Array<string>} arr
 * @returns {function(string): number}
 */
const countIn = arr => s => arr.filter(e => e === s).length;

/**
 * Utility logging function
 * @param {string|number} v
 * @param {string|number} n
 */
const print = (v, n) => console.log(`${pad(v)}:\t${n}`)

const prettyPrint = seq => {
  const chunks = toRows(seq);
  console.log('SEQUENCE:')
  chunks.forEach((e, i) => print(i * rowLength, e.join('')))
}

const printBases = (seq, bases) => {
  const filterSeq = countIn(seq);
  const counts = bases.map(filterSeq);
  console.log('\nBASE COUNTS:')
  counts.forEach((e, i) => print(bases[i], e));
  print('Total', counts.reduce((p,c) => p + c, 0));
}

prettyPrint(seq);
printBases(seq, bases);


def lpad($len; $fill): tostring | ($len - length) as $l | ($fill * $l)[:$l] + .;

# Create a bag of words, i.e. a JSON object with counts of the items in the stream
def bow(stream):
  reduce stream as $word ({}; .[($word|tostring)] += 1);


const sequence =
"CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG" *
"CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG" *
"AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT" *
"GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT" *
"CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG" *
"TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA" *
"TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT" *
"CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG" *
"TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC" *
"GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"

function dnasequenceprettyprint(seq, colsize=50)
    println(length(seq), "nt DNA sequence:\n")
    rows = [seq[i:min(length(seq), i + colsize - 1)] for i in 1:colsize:length(seq)]
    for (i, r) in enumerate(rows)
        println(lpad(colsize * (i - 1), 5), "   ", r)
    end
end

dnasequenceprettyprint(sequence)

function printcounts(seq)
    bases = [['A', 0], ['C', 0], ['G', 0], ['T', 0]]
    for c in seq, base in bases
        if c == base[1]
            base[2] += 1
        end
    end
    println("\nNucleotide counts:\n")
    for base in bases
        println(lpad(base[1], 10), lpad(string(base[2]), 12))
    end
    println(lpad("Other", 10), lpad(string(length(seq) - sum(x[2] for x in bases)), 12))
    println("     _________________\n", lpad("Total", 10), lpad(string(length(seq)), 12))

end

printcounts(sequence)


fun printSequence(sequence: String, width: Int = 50) {
    fun <K, V> printWithLabel(k: K, v: V) {
        val label = k.toString().padStart(5)
        println("$label: $v")
    }

    println("SEQUENCE:")
    sequence.chunked(width).withIndex().forEach { (i, line) ->
        printWithLabel(i*width + line.length, line)
    }
    println("BASE:")
    sequence.groupingBy { it }.eachCount().forEach { (k, v) ->
        printWithLabel(k, v)
    }
    printWithLabel("TOTALS", sequence.length)
}

const val BASE_SEQUENCE = "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATGCTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTGAGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGATGGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTTCGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGGTCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATATTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTATCGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTGTCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGACGACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"

fun main() {
    printSequence(BASE_SEQUENCE)
}


{def DNA CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATGCTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTGAGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGATGGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTTCGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGGTCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATATTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTATCGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTGTCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGACGACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT}
-> DNA

{def base_count
 {def base_count.r
  {lambda {:dna :b :n :i :count}
   {if {> :i :n}
    then :count
    else {base_count.r :dna :b :n {+ :i 1}
                       {if {W.equal? {W.get :i :dna} :b}
                        then {+ :count 1}
                        else :count}} }}}
 {lambda {:dna :b}
  {base_count.r :dna :b {- {W.length :dna} 1} 0 0} }}
-> base_count

{def S {S.map {base_count {DNA}}} A C G T}}
-> S
[A C G T] = (129 97 119 155)

A+C+G+T = {+ {S}}
-> A+C+G+T = 500


function prettyprint(seq) -- approx DDBJ format
  seq = seq:gsub("%A",""):lower()
  local sums, n = { a=0, c=0, g=0, t=0 }, 1
  seq:gsub("(%a)", function(c) sums[c]=sums[c]+1 end)
  local function printf(s,...) io.write(s:format(...)) end
  printf("LOCUS       AB000000     %12d bp    mRNA    linear   HUM 01-JAN-2001\n", #seq)
  printf(" BASE COUNT %12d a %12d c %12d g %12d t\n", sums.a, sums.c, sums.g, sums.t)
  printf("ORIGIN\n")
  while n < #seq do
    local sub60 = seq:sub(n,n+59)
    printf("%9d %s\n", n, sub60:gsub("(..........)","%1 "))
    n = n + #sub60
  end
end

prettyprint[[
CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT
]]


seq = "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATGCTCGTGCTTTCCA\
ATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTGAGGACAAAGGTCAAGATGGAGCGCATCGAACGC\
AATAAGGATCATTTGATGGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTTCGA\
TTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGGTCAGTCCCAATGTGCGGGGTTTC\
TTTTAGTACGTCGGGAGTGGTATTATATTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTT\
AGGTTATCGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTGTCCTAAATTTGAA\
TGGCAAACACAAATAAGATTTAGCAATTCGTGTAGACGACCGGGGACTTGCATGATGGGAGCAGCTTTGT\
TAAACTACGAACGTAAT";
size = 70;
parts = StringPartition[seq, UpTo[size]];
begins = Most[Accumulate[Prepend[StringLength /@ parts, 1]]];
ends = Rest[Accumulate[Prepend[StringLength /@ parts, 0]]];
StringRiffle[MapThread[ToString[#1] <> "-" <> ToString[#2] <> ": " <> #3 &, {begins, ends, parts}], "\n"]
StringRiffle[#1 <> ": " <> ToString[#2] & @@@ Tally[Characters[seq]], "\n"]


function r = base_count(f)
    fid = fopen(f,'r');
    nn=[0,0,0,0];
    while ~feof(fid)
	s = fgetl(fid);
	fprintf(1,'%5d :%s\n', sum(nn), s(s=='A'|s=='C'|s=='G'|s=='T'));
	nn = nn+[sum(s=='A'),sum(s=='C'),sum(s=='G'),sum(s=='T')];
    end
    fclose(fid);

    fprintf(1, '\nBases:\n\n  A  : %d\n  C  : %d\n  G  : %d\n  T  : %d\n', nn);
    fprintf(1, '\nTotal: %d\n\n', sum(nn));
end;


import strformat
import strutils

const Source = "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG" &
               "CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG" &
               "AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT" &
               "GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT" &
               "CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG" &
               "TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA" &
               "TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT" &
               "CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG" &
               "TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC" &
               "GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT"

# Enumeration type for bases.
type Base* {.pure.} = enum A, C, G, T, Other = "other"

proc display*(dnaSeq: string) =
  ## Display a DNA sequence using EMBL format.

  var counts: array[Base, Natural]    # Count of bases.
  for c in dnaSeq:
    inc counts[parseEnum[Base]($c, Other)]  # Use Other as default value.

  # Display the SQ line.
  var sqline = fmt"SQ   {dnaSeq.len} BP; "
  for (base, count) in counts.pairs:
    sqline &= fmt"{count} {base}; "
  echo sqline

  # Display the sequence.
  var idx = 0
  var row = newStringOfCap(80)
  var remaining = dnaSeq.len

  while remaining > 0:
    row.setLen(0)
    row.add("     ")

    # Add groups of 10 bases.
    for group in 1..6:
      let nextIdx = idx + min(10, remaining)
      row.add(dnaSeq[idx..<nextIdx] & ' ')
      dec remaining, nextIdx - idx
      idx = nextIdx
      if remaining == 0:
        break

    # Append the number of the last base in the row.
    row.add(spaces(72 - row.len))
    row.add(fmt"{idx:>8}")
    echo row

  # Add termination.
  echo "//"

when isMainModule:
  Source.display()


program DNA_Base_Count;
{$IFDEF FPC}
  {$MODE DELPHI}//String = AnsiString
{$ELSE}
  {$APPTYPE CONSOLE}
{$ENDIF}
const
    dna =
        'CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG' +
        'CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG' +
        'AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT' +
        'GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT' +
        'CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG' +
        'TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA' +
        'TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT' +
        'CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG' +
        'TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC' +
        'GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT';
var
  CntIdx : array of NativeUint;
  DNABases : String;
  SumBaseTotal : NativeInt;

procedure OutFormatBase(var DNA: String;colWidth:NativeInt);
var
  j: NativeInt;
Begin
  j := 0;
  Writeln(' DNA base sequence');
  While j<Length(DNA) do
  Begin
    writeln(j:5,copy(DNA,j+1,colWidth):colWidth+2);
    inc(j,colWidth);
  end;
  writeln;
end;

procedure Cnt(const DNA: String);
var
  i,p :NativeInt;
Begin
  SetLength(CntIdx,Length(DNABases));
  i := 1;
  while i <= Length(DNA) do
  Begin
    p := Pos(DNA[i],DNABases);
    //found new base so extend list
    if p = 0 then
    Begin
      DNABases := DNABases+DNA[i];
      p := length(DNABases);
      Setlength(CntIdx,p+1);
    end;
    inc(CntIdx[p]);
    inc(i);
  end;

  Writeln('Base     Count');
  SumBaseTotal := 0;
  For i := 1 to Length(DNABases) do
  Begin
    p := CntIdx[i];
    inc(SumBaseTotal,p);
    writeln(DNABases[i]:4,p:10);
  end;
  Writeln('Total base count ',SumBaseTotal);
  writeln;
end;

var
  TestDNA: String;
Begin
  DNABases :='ACGT';// predefined
  TestDNA := DNA;
  OutFormatBase(TestDNA,50);
  Cnt(TestDNA);
end.


use strict;
use warnings;
use feature 'say';

my %cnt;
my $total = 0;

while ($_ = <DATA>) {
    chomp;
    printf "%4d: %s\n", $total+1, s/(.{10})/$1 /gr;
    $total += length;
    $cnt{$_}++ for split //
}

say "\nTotal bases: $total";
say "$_: " . ($cnt{$_}//0) for <A C G T>;

__DATA__
CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT


constant dna = substitute("""
CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT
""","\n","")
sequence acgt = repeat(0,5)
for i=1 to length(dna) do
    acgt[find(dna[i],"ACGT")] += 1
end for
acgt[$] = sum(acgt)
sequence s = split(trim(join_by(split(join_by(dna,1,10,""),"\n"),1,5," ")),"\n")
for i=1 to length(s) do
    printf(1,"%3d: %s\n",{(i-1)*50+1,s[i]})
end for
printf(1,"\nBase counts: A:%d, C:%d, G:%d, T:%d, total:%d\n",acgt)


main =>
  dna(DNA, ChunkSize),
  Count = 0,
  println("Sequence:"),
  Map = new_map(['A'=0,'C'=0,'G'=0,'T'=0]),
  foreach(Chunk in DNA.chunks_of(ChunkSize))
    printf("%4d: %s\n", Count, Chunk),
    Count := Count + Chunk.len,
    foreach(C in Chunk)
      Map.put(C,Map.get(C)+1)
    end
  end,
  println("\nBase count:"),
  foreach(C in "ACGT")
    printf("%5c: %3d\n", C, Map.get(C))
  end,
  printf("Total: %d\n", Count),
  nl.

dna(DNA,ChunkSize) =>
  DNA = "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT".delete_all('\n'),
  ChunkSize = 50.


(let
   (S (chop "CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG\
      CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG\
      AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT\
      GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT\
      CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG\
      TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA\
      TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT\
      CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG\
      TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC\
      GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT" )
      R )
   (for I S (accu 'R I 1))
   (for I R (println I))
   (println 'Total: (sum cdr R)) )