diff --git a/lib/period-length.js b/lib/period-length.js
new file mode 100644
index 0000000000000000000000000000000000000000..e62f076ef09aa0a555c62219fbd3a33c5e90baba
--- /dev/null
+++ b/lib/period-length.js
@@ -0,0 +1,49 @@
+import assert from 'assert'
+
+export default function getPeriodLengthStats(cycleLengths) {
+  throwIfArgsAreNotInRequiredFormat(cycleLengths)
+  const periodLengthStats = {}
+  const sortedCycleLengths = cycleLengths.sort((a, b) => {
+    return a - b
+  })
+  periodLengthStats.minimum = sortedCycleLengths[0]
+  periodLengthStats.maximum = sortedCycleLengths[cycleLengths.length - 1]
+  periodLengthStats.mean = Math.round(
+    cycleLengths.reduce(getSum) / cycleLengths.length * 100
+  ) / 100
+  // median
+  if (cycleLengths.length % 2 == 1) {
+    periodLengthStats.median = sortedCycleLengths[
+      (cycleLengths.length + 1) / 2 - 1
+    ]
+  } else {
+    const middle = cycleLengths.length / 2
+    periodLengthStats.median = (sortedCycleLengths[middle - 1] +
+       sortedCycleLengths[middle]) / 2
+  }
+  // corrected standard deviation (based on unbiased sample variance)
+  if (cycleLengths.length > 1) {
+    const sumOfSquares = cycleLengths.map(cycleLength => {
+      return Math.pow(cycleLength - periodLengthStats.mean, 2)
+    }).reduce(getSum)
+    periodLengthStats.stdDeviation = Math.round(
+      Math.sqrt(sumOfSquares / (cycleLengths.length - 1 )) * 100
+    ) / 100
+  } else {
+    periodLengthStats.stdDeviation = null
+  }
+  return periodLengthStats
+}
+
+function getSum(total, num) {
+  return total + num
+}
+
+function throwIfArgsAreNotInRequiredFormat(cycleLengths) {
+  assert.ok(Array.isArray(cycleLengths), 'Input should be an array.')
+  assert.ok(cycleLengths.length > 0, 'Input array should not be empty.')
+  cycleLengths.forEach(cycleLength => {
+    assert.equal(typeof cycleLength, 'number', 'Elements in the array should be of type number.')
+    assert.ok(!isNaN(cycleLength), 'Elements of array should not be NaN.')
+  })
+}
\ No newline at end of file
diff --git a/test/periode-length.spec.js b/test/periode-length.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..0b9b200b479b828e526295682b756c87065208a9
--- /dev/null
+++ b/test/periode-length.spec.js
@@ -0,0 +1,64 @@
+import chai from 'chai'
+import { AssertionError } from 'assert'
+
+import periodInfo from '../lib/period-length'
+
+const expect = chai.expect
+
+describe.only('getPeriodLengthStats', () => {
+  it('works for a simple odd-numbered array', () => {
+    const periodLengths = [99, 5, 1, 2, 100]
+    const result = periodInfo(periodLengths)
+    const expectedResult = {
+      minimum: 1,
+      maximum: 100,
+      mean: 41.4,
+      median: 5,
+      stdDeviation: 53.06
+    }
+    expect(result).to.eql(expectedResult)
+  })
+
+  it('works for a simple even-numbered array', () => {
+    const periodLengths = [4, 1, 15, 2, 20, 5]
+    const result = periodInfo(periodLengths)
+    const expectedResult = {
+      minimum: 1,
+      maximum: 20,
+      mean: 7.83,
+      median: 4.5,
+      stdDeviation: 7.78
+    }
+    expect(result).to.eql(expectedResult)
+  })
+  it('works for an one-element array', () => {
+    const periodLengths = [42]
+    const result = periodInfo(periodLengths)
+    const expectedResult = {
+      minimum: 42,
+      maximum: 42,
+      mean: 42,
+      median: 42,
+      stdDeviation: null
+    }
+    expect(result).to.eql(expectedResult)
+  })
+  describe('when args are wrong', () => {
+    it('throws when arg object is an empty array', () => {
+      const periodLengths = []
+      expect(() => periodInfo(periodLengths).to.throw(AssertionError))
+    })
+    it('throws when arg object is not in right format', () => {
+      const wrongObject = { hello: 'world' }
+      expect(() => periodInfo(wrongObject).to.throw(AssertionError))
+    })
+    it('throws when arg array contains a string', () => {
+      const wrongElement = [4, 1, 15, '2', 20, 5]
+      expect(() => periodInfo(wrongElement).to.throw(AssertionError))
+    })
+    it('throws when arg array contains a NaN', () => {
+      const wrongElement = [4, 1, 15, NaN, 20, 5]
+      expect(() => periodInfo(wrongElement).to.throw(AssertionError))
+    })
+  })
+})
\ No newline at end of file