Command Line Fu: surveillance with Raspberry Pi

Today we are building the a primitive surveillance system using a Raspberry Pi. This is our goal:

Take a picture every hour -> send it as encrypted email

These are the basic prerequisites:

  • A Raspberry Pi, no matter which model. OS: Raspbian Jessie. Others might also work.
  • A Raspberry Pi camera module, no matter which model.

Required software

One needs the following software, all available through the standard repositories:

  • raspistill (usually installed by default in raspbian)
  • gpg (usually installed by default in raspbian)
  • curl
  • tmux

All in all this is a relatively lightweight setup, all tooling is standard in the linux world. Use apt-get install to get any missing programs.

Mail

I used yandex.com as mail provider and created a dedicated account, let us assume that it is raspberry_surveillance_1111@yandex.com. Any other mail provider may most likely work as well. One simply needs to substitute some parameters in the snippets below. Let us further assume that we send mails to one address john.doe@yandex.com. Let us also assume that john.doe@yandex.com has a PGP key pair and uploaded the public key to a public keyserver. Then import the key at the Pi

pi$> gpg --recv-keys 999AAA1

where 999AAA1 is the key id. Then trust the key after checking the fingerprint

pi$> gpg --edit-key "john.doe@yandex.com"
trust
5

bash script

Sending mail using curl will require that we build up a text block containing the raw email contents. We can script that in a straightforward, although slightly hacky way:

#!/usr/bin/env bash

echo 'From: "Raspberry Pi" <raspberry_surveillance_1111@yandex.com>' \
     | (cat && echo 'To: "John Doe" <john.doe@yandex.com>') \
     | (cat && echo 'Subject: Encrypted Message Title') \
     | (cat && echo 'MIME-Version: 1.0') \
     | (cat && echo 'Content-Type: multipart/encrypted;') \
     | (cat && echo ' protocol="application/pgp-encrypted";') \
     | (cat && echo ' boundary="gc0p4Jq0M2Yt08jU534c0p"') \
     | (cat && echo '') \
     | (cat && echo 'This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)') \
     | (cat && echo '--gc0p4Jq0M2Yt08jU534c0p') \
     | (cat && echo 'Content-Type: application/pgp-encrypted') \
     | (cat && echo 'Content-Description: PGP/MIME version identification') \
     | (cat && echo '') \
     | (cat && echo 'Version: 1') \
     | (cat && echo '') \
     | (cat && echo '--gc0p4Jq0M2Yt08jU534c0p') \
     | (cat && echo 'Content-Type: application/octet-stream; name="encrypted.asc"') \
     | (cat && echo 'Content-Description: OpenPGP encrypted message') \
     | (cat && echo 'Content-Disposition: inline; filename="encrypted.asc"') \
     | (cat && echo '') \
     | (cat && \
         ( \
               (echo 'Content-Type: multipart/mixed; boundary="wnfqnfqwjnwjqnfjohBbBHk9124b"') \
               | (cat && echo 'From: raspberry_surveillance_1111@yandex.com') \
               | (cat && echo 'Subject: Encrypted Message Title') \
               | (cat && echo '') \
               | (cat && echo '--wnfqnfqwjnwjqnfjohBbBHk9124b') \
               | (cat && echo 'Content-Type: multipart/mixed;') \
               | (cat && echo ' boundary="------------561248FHHE61H523"') \
               | (cat && echo '') \
               | (cat && echo 'This is a multi-part message in MIME format.') \
               | (cat && echo '--------------561248FHHE61H523') \
               | (cat && echo 'Content-Type: text/plain; charset=windows-1252') \
               | (cat && echo 'Content-Transfer-Encoding: quoted-printable') \
               | (cat && echo '') \
               | (cat && echo '') \
               | (cat && echo '') \
               | (cat && echo '') \
               | (cat && echo '') \
               | (cat && echo '--------------561248FHHE61H523') \
               | (cat && echo 'Content-Type: image/jpeg;') \
               | (cat && echo ' name="raspistill.jpg"') \
               | (cat && echo 'Content-Transfer-Encoding: base64') \
               | (cat && echo 'Content-Disposition: attachment;') \
               | (cat && echo ' filename="raspistill.jpg"') \
               | (cat && echo '') \
               | (cat && (raspistill -o - | base64)) \
               | (cat && echo '') \
               | (cat && echo '--------------561248FHHE61H523--') \
               | (cat && echo '') \
               | (cat && echo '--wnfqnfqwjnwjqnfjohBbBHk9124b--') \
               | gpg --encrypt --quiet --armor --output - --recipient john.doe@yandex.com) \
         ) \
     | (cat && echo '') \
     | (cat && echo -n '--gc0p4Jq0M2Yt08jU534c0p--') \
     | curl --url 'smtps://smtp.yandex.com:465' --ssl-reqd --mail-from 'raspberry_surveillance_1111@yandex.com' --mail-rcpt 'john.doe@yandex.com' --user "raspberry_surveillance_1111@yandex.com:password" -T  -

Schedule

We will use a tmux session and loop infinitely:

pi$> tmux new -s picsandmails
pi$> while true; do ./sendpic.sh; sleep 3600; done
pi$> CTRL+B d

Remarks

  • At no point in the bash script any file is written to the filesystem. Everything works with pipes and streams, any data is kept in memory. This avoids the problem of a potentially filling the Pis memory card. Of course your mail inbox fills up, but there are ways around this.
  • On first sight it seems that using curl is way inferior to mail. That is partially true, but in real systems I want to avoid putting my mail credentials in any config files. I extended the script to work with environment variables, which I am exporting inside the tmux sessions. Also I am not sure if the pipe/stream approach here can be used as straightforward.
  • The sent mail contain no message body and one attachment. Leveraging this limitation is trivial.
  • A nice extension idea: sign the mails by the Pi with a dedicated PGP key.
Posted in Encryption, Security/Encryption | Tagged , , , , , , , , | Leave a comment

Let gooby do teh hoemwerk: Riemann zeta at two

Based on what I read in a very old book, we show

\zeta(2) = \sum\limits_{n=1}^\infty \frac{1}{n^2} = \frac{\pi^2}{6}

We can divide the sum into even and odd contributions

\sum\limits_{n=1}^\infty \frac{1}{n^2} = \sum\limits_{n=1}^\infty \frac{1}{(2n-1)^2} + \sum\limits_{n=1}^\infty \frac{1}{(2n)^2}

One sees that

\zeta(2) =\frac{4}{3}\sum\limits_{n=1}^\infty \frac{1}{(2n-1)^2}

In order to evaluate the right hand side of this equation we consider the function

C(x) = \sum\limits_{n=1}^\infty \frac{\cos (2n-1)x}{(2n-1)^2}, such that \zeta(2) =\frac{4}{3}C(0)

We now take a small detour and consider the finite sum C_N(x) and its derivatives

C_N(x) = \sum\limits_{n=1}^N \frac{\cos (2n-1)x}{(2n-1)^2}

C_N'(x) = -\sum\limits_{n=1}^N \frac{\sin (2n-1)x}{2n-1}

C_N''(x) = -\sum\limits_{n=1}^N \cos(2n-1)x

Multiplying the last equation by 2\sin x and using

2\sin x \cos k x = \sin(k+1)x-\sin(k-1)x

We get

2\sin x \,C_N''(x) = -\sin 2Nx

Now one can solve for C_N'(x)

C_N'(x) = C_N'(\pi/2) - \int\limits_{\pi/2}^{x}dy\,\frac{\sin 2Ny}{\sin y}

By writing

C_N'(x) = C_N'(\pi/2) + \frac{1}{2N} \int\limits_{\pi/2}^{x}dy\,\frac{\frac{d}{dy}\cos 2Ny}{\sin y}

One can think of integration by parts and notice that the second term vanishes when N\to\infty. Now

C'(\pi/2) = -\lim\limits_{N\to\infty}\sum\limits_{n=1}^N \frac{(-1)^n}{2n-1} =-\arctan(1) = -\frac{\pi}{4}

In other words,

C'(x) =-\frac{\pi}{4}

from which it is immediate

C(x) = C(0) - \frac{\pi}{4}x

Now note C(\pi/2) = 0 and therefore

C(0) = \frac{\pi}{4}\times\frac{\pi}{2} = \frac{\pi^2}{8}

Finally, the identity \zeta(2) =\frac{4}{3}C(0) establishes the result \zeta(2) =\frac{\pi^2}{6}.

Happy new year to evri1!

Posted in Math | Tagged , , , | Leave a comment

Mathematical insult gone awry

Seen on average image board:

math_fail

Let’s try to solve it. The inner limit can be written as

\lim\limits_{x\rightarrow 0} e^{\frac{\ln(1+\sin^2x + \sin^2 2x + \dots+ \sin^2nx)}{x^2}}

The limit in the exponent can be taken using the l’Hostpital rule

\lim\limits_{x\rightarrow 0} \frac{\ln(1+\sin^2x + \sin^2 2x + \dots+ \sin^2nx)}{x^2} = \lim\limits_{x\rightarrow 0} \frac{\sin x \cos x+2\sin 2x \cos 2x + \dots + n\sin nx \cos nx}{x(1+\sin^2x + \sin^2 2x + \dots+ \sin^2nx)} = \dots

The in the sense of the limit the denominator can be replaced by x. In the same sense we can put any \cos \alpha x \rightarrow 1 in the numerator. Any expression of the form \sin\alpha x/x \rightarrow \alpha as x\rightarrow 0, thus

\dots = 1 +2\cdot 2 + \dots + n\cdot n = \frac{n^3}{3} + \mathcal{O}(n^2)

The rest is straightforward and one immediately sees that the limit is fact

e^{\frac{1}{3}}

Posted in Math | Tagged , | Leave a comment

How to get a job with 70 lines of code

After my recent post How to get a job with 250 lines of code there is a new challenge available, cf. followthewhiterabbit:

challengemd5

They kindly provide a list of words containing the building blocks of the solution as well as an “anagram hint”. One can use this hint at the very beginning by preprocessing the file with some grep acrobatics:

dos2unix wordlist
grep -Ei '^[poultry outwits ants]+$' wordlist | grep -Eiv '*[y].*[y]' | grep -Eiv '*[p].*[p]'  | grep -Eiv '*[o].*[o].*[o]' |  grep -Eiv '*[l].*[l]' |  grep -Eiv '*[u].*[u].*[u]' | grep -Eiv '*[r].*[r]' | grep -Eiv '*[w].*[w]' | grep -Eiv '*[i].*[i]' | grep -Eiv '*[a].*[a]'  | grep -Eiv '*[n].*[n]' | grep -Eiv '*[s].*[s].*[s]' | sort --unique > wordlist_clean

This step is important since it reduces the number of candidate words to 1659 (compared to 99175 in its untouched form).

Without further ado, using the reduced file let me spoil the solution written in go:

package main

import (
  "crypto/md5"
  "errors"
  "fmt"
  "io/ioutil"
  "log"
  "strings"
)

const (
  targetMd5        = "4624d200580677270a54ccff86b9610e"
  anagramLetters   = "poultry outwits ants"
  wordlistFilename = "wordlist_clean"
)

func main() {
  log.Printf("Target md5 is                    : %s", targetMd5)
  log.Printf("Anagram letters are              : %s", anagramLetters)
  content, err := ioutil.ReadFile(wordlistFilename)
  if err != nil {
    log.Fatalln("Cannot read wordlist "+wordlistFilename, err)
  }
  lines := strings.Split(string(content), "\n")
  numWords := uint64(len(lines))
  log.Printf("Number of words read             : %d", numWords)
  log.Printf("Number of permutations to process: %d", numWords*numWords*numWords)

  phrase, err := findMd5(lines, targetMd5)
  if err != nil {
    log.Fatalln("No matching md5 found!", err)
  }
  log.Printf("Found correct phrase: '%s'", phrase)
}

func findMd5(lines []string, expectedMd5 string) (string, error) {
  for c1, word1 := range lines {
    for _, word2 := range lines {
      for _, word3 := range lines {
        phrase := strings.Join([]string{word1, word2, word3}, " ")
        if isAnagram(anagramLetters, phrase) {
          phraseAsBytes := []byte(phrase)
          md5 := fmt.Sprintf("%x", md5.Sum(phraseAsBytes))
          if md5 == expectedMd5 {
            fmt.Println(" ...done")
            return phrase, nil
          }
        }
      }
    }
    pctage := fmt.Sprintf("%.3f", 100.0*float64(c1)/float64(len(lines))) + "%"
    fmt.Printf("\r%s of permutations processed", pctage)
  }
  fmt.Println(" ...done")
  return "", errors.New("No phrase found for md5 " + expectedMd5)
}

func isAnagram(one string, another string) bool {
  if len(one) != len(another) {
    return false
  }
  for _, character := range one {
    if strings.Count(one, string(character)) != strings.Count(another, string(character)) {
      return false
    }
  }
  return true
}

The output of the program will look something like

2016/10/17 22:24:28 Target md5 is                    : 4624d200580677270a54ccff86b9610e
2016/10/17 22:24:28 Anagram letters are              : poultry outwits ants
2016/10/17 22:24:28 Number of words read             : 1660
2016/10/17 22:24:28 Number of permutations to process: 4574296000
27.470% of permutations processed ...done
2016/10/17 22:26:55 Found correct phrase: 'pastils turnout towy'
Posted in Hash functions, Programming | Tagged , , , , , | 3 Comments

Quick tip: Veracrypt on a Raspberry Pi 2 [update: Veracrypt 1.18]

My recent blog post seems to be somewhat outdated. Here is an update to install Veracrypt 1.18 on a raspberry 2 with OS
Raspbian GNU/Linux 8 kernel 4.4.13.
First one needs to install

sudo apt-get install makeself libfuse-dev

We assume that you downloaded VeraCrypt_1.18_Source.tar.bz2 into /home/pi/vc/VeraCrypt_1.18_Source.tar.bz2 and wxWidgets-3.0.2.tar.bz2 into /home/pi/wx/wxWidgets-3.0.2.tar.bz2. Now unpack the sources ĺike

cd /home/pi/vc/
bzip2 -d VeraCrypt_1.18_Source.tar.bz2
tar xfv VeraCrypt_1.18_Source.tar
cd /home/pi/wx/
bzip2 -d wxWidgets-3.0.2.tar.bz2
tar xfv wxWidgets-3.0.2.tar

Then it is time for compiling

cd /home/pi/vc/src
make NOGUI=1 WXSTATIC=1 WX_ROOT=/home/pi/wx/wxWidgets-3.0.2/ wxbuild

Then it is time to grab some coffee. Compiling takes a while… and it is not very verbose.
Once it is done one needs to modify the /home/pi/vc/src/Makefile and delete the four lines

...
CFLAGS += -msse2
CXXFLAGS += -msse2
...
CFLAGS += -maes
CXXFLAGS += -maes
...

And then

make NOGUI=1 WXSTATIC=1

Then it is coffee time again.
Once finished, proceed to copy the veracrypt binary

sudo cp Main/veracrypt /usr/local/bin/

Mounting can be done by e.g.

sudo veracrypt -t -k "" volume.vc /media/veracrypt1/ -m nokernelcrypto

Again be aware the the key derivation in native Veracrypt volumes is real computation-intense, it is not really fun on a RPi. The old Truecrypt containers will work fine, provided the option -tc is passed to veracrypt.
Last remark: the build and source folders can be deleted

rm -rf /home/pi/vc/
rm -rf /home/pi/wx/
Posted in Encryption, Security, Security/Encryption | Tagged , | 4 Comments

Java bean differences

From time to time your application needs to work on certain data objects or entities. Depending on the situation and the complexity, you want to have logging that will help analyzing what the application was doing during a past time interval. Typically you want to log errors, user input, decisions that the application makes, results of whatever the application was computing.

In every step one is dealing with data. Today I would like to give a quick tip on generic logging of data containers, roughly speaking beans: logging the difference of two objects. There are many important use-cases where one wants to have a comparison of two objects. Typically logging both of them via toString() is not so helpful, since the reader of the log file will have to figure out the differences.

The general solution I propose makes use of two powerful libraries, commons-beanutils and guava. The former will figure out the properties of a bean, the later is responsible for determining the differences. Let’s have a look at some code:

/* (C) 2016 Gooby. All rights reserved. */
package plz.gooby;

import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;

import org.apache.commons.beanutils.BeanMap;

import java.util.Map;

/**
 * Differences of beans.
 */
public final class BeanDifferences {

    private BeanDifferences() {
    }

    public static BeanDifference differenceOf(Object o1, Object o2) {
        return new BeanDifference(o1, o2);
    }

    /**
     * Wrapper class for object difference. Describes differences in its {@link BeanDifference#toString()} method.
     */
    private static final class BeanDifference {
        final Object o1;
        final Object o2;

        private BeanDifference(Object o1, Object o2) {
            this.o1 = o1;
            this.o2 = o2;
        }

        @Override
        public String toString() {
            try {
                return describeDifferences();
            } catch (Throwable t) {
                return "ERROR: cannot determine difference " + t;
            }
        }

        private String describeDifferences() {
            BeanMap beanMap1 = new BeanMap(o1);
            BeanMap beanMap2 = new BeanMap(o2);
            MapDifference&lt;Object, Object&amp;gt; difference = Maps.difference(beanMap1, beanMap2);
            StringBuilder sb = new StringBuilder(&amp;quot;[&amp;quot;);

            for (Map.Entry&amp;lt;Object, Object&amp;gt; differenceEntry : difference.entriesOnlyOnLeft().entrySet()) {
                Object key = differenceEntry.getKey();
                Object value = differenceEntry.getValue();
                sb.append(key).append(&amp;quot;: &amp;quot;).append(value).append(&amp;quot;&amp;lt;&amp;lt;&amp;quot;).append(&amp;quot; &amp;quot;).append(&amp;quot;;&amp;quot;);
            }
            for (Map.Entry&amp;lt;Object, MapDifference.ValueDifference&amp;lt;Object&amp;gt;&amp;gt; differenceEntry : difference.entriesDiffering().entrySet()) {
                Object key = differenceEntry.getKey();
                MapDifference.ValueDifference&amp;lt;Object&amp;gt; value = differenceEntry.getValue();
                sb.append(key).append(&amp;quot;: &amp;quot;).append(value.leftValue()).append(&amp;quot;&amp;lt;-&amp;gt;&amp;quot;).append(value.rightValue()).append(&amp;quot;;&amp;quot;);
            }
            for (Map.Entry&amp;lt;Object, Object&amp;gt; differenceEntry : difference.entriesOnlyOnRight().entrySet()) {
                Object key = differenceEntry.getKey();
                Object value = differenceEntry.getValue();
                sb.append(key).append(&amp;quot;: &amp;quot;).append(value).append(&amp;quot;&amp;gt;&amp;gt;&amp;quot;).append(&amp;quot; &amp;quot;).append(&amp;quot;;&amp;quot;);
            }
            sb.append(&amp;quot;]&amp;quot;);
            return sb.toString();
        }
    }
}

Seeing this code in action can be easily demonstrated with a couple of JUnit tests

/* (C) 2016 Gooby. All rights reserved. */
package plz.gooby;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.Point;
import java.net.URL;
import java.time.LocalDate;
import java.util.UUID;

/**
 * Some tests.
 */
public class BeanDifferencesTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(BeanDifferencesTest.class);

    @Test
    public void testDifferenceOfPoints() throws Exception {
        Point p1 = new Point(42, 24);
        Point p2 = new Point(42, 3);
        LOGGER.info(&amp;quot;Difference of {} and {} : {}&amp;quot;, p1, p2, BeanDifferences.differenceOf(p1, p2));
        LOGGER.info(&amp;quot;Difference of {} and {} : {}&amp;quot;, p1, p1, BeanDifferences.differenceOf(p1, p1));
    }

    @Test
    public void testDifferenceOfDates() throws Exception {
        LocalDate d1 = LocalDate.ofYearDay(2016, 15);
        LocalDate d2 = LocalDate.ofYearDay(2016, 16);
        LOGGER.info(&amp;quot;Difference of {} and {} : {}&amp;quot;, d1, d2, BeanDifferences.differenceOf(d1, d2));
    }

    @Test
    public void testDifferenceOfDateAndPoint() throws Exception {
        LocalDate d = LocalDate.ofYearDay(2016, 15);
        Point p = new Point(1, -1);
        LOGGER.info(&amp;quot;Difference of {} and {} : {}&amp;quot;, d, p, BeanDifferences.differenceOf(d, p));
    }

    @Test
    public void testDifferenceOfUUIDs() throws Exception {
        UUID u1 = UUID.randomUUID();
        UUID u2 = UUID.randomUUID();
        LOGGER.info(&amp;quot;Difference of {} and {} : {}&amp;quot;, u1, u1, BeanDifferences.differenceOf(u1, u2));
    }

    @Test
    public void testDifferenceOfULS() throws Exception {
        URL u1 = new URL(&amp;quot;https://google.com&amp;quot;);
        URL u2 = new URL(&amp;quot;http://google.ca&amp;quot;);
        LOGGER.info(&amp;quot;Difference of {} and {} : {}&amp;quot;, u1, u1, BeanDifferences.differenceOf(u1, u2));
    }
}

The output:

Difference of java.awt.Point[x=42,y=24] and java.awt.Point[x=42,y=3] : [y: 24.0&amp;lt;-&amp;gt;3.0;location: java.awt.Point[x=42,y=24]&amp;lt;-&amp;gt;java.awt.Point[x=42,y=3];]
Difference of 2016-01-15 and 2016-01-16 : [dayOfWeek: FRIDAY&amp;lt;-&amp;gt;SATURDAY;dayOfMonth: 15&amp;lt;-&amp;gt;16;dayOfYear: 15&amp;lt;-&amp;gt;16;]
Difference of java.awt.Point[x=42,y=24] and java.awt.Point[x=42,y=24] : []
Difference of 2016-01-15 and java.awt.Point[x=1,y=-1] : [dayOfWeek: FRIDAY&amp;lt;&amp;lt; ;month: JANUARY&amp;lt;&amp;lt; ;dayOfMonth: 15&amp;lt;&amp;lt; ;dayOfYear: 15&amp;lt;&amp;lt; ;era: CE&amp;lt;&amp;lt; ;year: 2016&amp;lt;&amp;lt; ;monthValue: 1&amp;lt;&amp;lt; ;chronology: ISO&amp;lt;&amp;lt; ;leapYear: true&amp;lt;&amp;lt; ;class: class java.time.LocalDate&amp;lt;-&amp;gt;class java.awt.Point;x: 1.0&amp;gt;&amp;gt; ;y: -1.0&amp;gt;&amp;gt; ;location: java.awt.Point[x=1,y=-1]&amp;gt;&amp;gt; ;]
Difference of e6b785e1-1ffd-4877-b8f0-03d72a931689 and e6b785e1-1ffd-4877-b8f0-03d72a931689 : [mostSignificantBits: -1821840322297247625&amp;lt;-&amp;gt;4456614852182232643;leastSignificantBits: -5120588553653119351&amp;lt;-&amp;gt;-7111626394932247653;]
Difference of https://google.com and https://google.com : [defaultPort: 443&amp;lt;-&amp;gt;80;protocol: https&amp;lt;-&amp;gt;http;authority: google.com&amp;lt;-&amp;gt;google.ca;host: google.com&amp;lt;-&amp;gt;google.ca;content: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@7364985f&amp;lt;-&amp;gt;sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@5d20e46;]

That’s it. feel free to try it out.

Posted in Programming | Tagged , | Leave a comment

Pade approximants: a case study (iii)

We are now going to explore the analytic structure of the Padé approximants for our example [1], [2].

The study (iii)

The limit n\to\infty for the Padé approximants P^n_n(z) and P^n_{n+1}(z) exist for all z\in\mathbb{C}\setminus[1,\infty). This is much stronger than the Taylor series, which only converges inside the unit disk and diverges outside.

On the line [1,\infty) the limit does not exist for P^n_{n}(z), P^n_{n+1}(z), where the original function f has a logarithmic branch cut. This fact is “captured” by the Padé rational functions by a funny effect: P^n_{n}(z) and P^n_{n+1}(z) have poles for z>1 which become dense in z\in(1,\infty). E.g. P^{10}_{10}(z):

pade10_C

Although divergent, in some instances the Padé’s still exhibit a rich structure. E.g. P^n_n(2) has two limit points at \pm\frac{\pi}{2}:

pade_2The difference is the same as the discontinuity of f across the branch cut.

The esoteric conclusion: the Padé’s are much more efficient than the Taylor approximations and “somehow know” about the analytic properties of the original function.

 

 

 

Posted in Math, Mathematica | Tagged , , , | 1 Comment