Skip to content

Commit

Permalink
feat: Add Manchester code
Browse files Browse the repository at this point in the history
  • Loading branch information
manniL committed Mar 31, 2018
1 parent 4114b88 commit 377f838
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ console.log(rot('Hello world!'))
- Fractionated Morse
- Pollux
- Multi-Tap
- Manchester code


## Contributing
Expand Down
102 changes: 102 additions & 0 deletions docs/ciphers/manchester.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Manchester code

> Invented by G.E. Thomas in 1949, the Manchester code (also known as PE / Phase Encoding)
is a binary line code mainly used in telecommunication and data storage. The
ciphertext has twice the amount of characters after encoding.

## Cipher behavior information

* Case sensitive? ❌
* Deterministic? ✓
* Alphabet: `01`
* Characters not in alphabet will be: **omitted** or **throwing an error (default)**

## Default options object

The options are the same for both methods, `encode` and `decode`

```
const options = {
inverted: false, // Invert the result / treat ciphertext as inverted.
failOnUnknownCharacter: true, // Should an error be thrown when a character is not included in the alphabet
}
```

`inverted` can be used to switch between two implementations. G.E. Thomas' (false) and IEEE 802.3 (true)

## Usage

### Encoding

#### Default

```
import { manchester } from 'cipher-collection'
console.log(manchester.encode('01')) // 0110
```

#### Inverted


```
import { manchester } from 'cipher-collection'
const invertedOptions = { inverted: true }
console.log(manchester.encode('01'), invertedOptions) // 1001
```

#### Without error throwing


```
import { manchester } from 'cipher-collection'
const silentFailOptions = {
failOnUnknownCharacter: false,
}
console.log(fractionatedMorse.encode('€€€01', silentFailOptions)) // 0110
```


### Decoding


#### Default

```
import { manchester } from 'cipher-collection'
console.log(manchester.decode('0110')) // 01
```

#### Inverted


```
import { manchester } from 'cipher-collection'
const invertedOptions = { inverted: true }
console.log(manchester.encode('1001'), invertedOptions) // 01
```

#### Without error throwing


```
import { manchester } from 'cipher-collection'
const silentFailOptions = {
failOnUnknownCharacter: false,
}
console.log(fractionatedMorse.decode('0', silentFailOptions)) // (empty string)
console.log(fractionatedMorse.decode('€0110', silentFailOptions)) // 01
```
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
* [Fractionated Morse](./ciphers/fractionated-morse.md)
* [Pollux](./ciphers/pollux.md)
* [Multi-Tap](./ciphers/multi-tap.md)
* [Manchester code](./ciphers/manchester.md)

78 changes: 78 additions & 0 deletions src/manchester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const decode = (input, options = {}) => {
options = { ...DEFAULT_OPTIONS, ...options }

if (input.match(/[^01]/g)) {
if (options.failOnUnknownCharacter) {
throw Error('Invalid Input')
}
input = [...input].filter(c => '01'.includes(c)).join('')
}

if (input.length % 2) {
if (options.failOnUnknownCharacter) {
throw Error('Invalid Input')
}
return ''
}
const splitUp = splitInput(input)

if (!splitUp || !splitUp.length) {
if (options.failOnUnknownCharacter) {
throw Error('Invalid Input after splitting')
}
return ''
}

const result = splitUp.map(xorWithClock).join('').split('').filter((_, k) => k % 2 === 0).join('')

return options.inverted ? invertResult(result) : result
}

const encode = (input, options = {}) => {
options = { ...DEFAULT_OPTIONS, ...options }

if (input.match(/[^01]/g)) {
if (options.failOnUnknownCharacter) {
throw Error('Invalid Input')
}
input = [...input].filter(c => '01'.includes(c)).join('')
}

// Map input to clock
const doubledInput = [...input].map(c => `${c}${c}`).join('')

const splitUp = splitInput(doubledInput)

if (!splitUp || !splitUp.length) {
if (options.failOnUnknownCharacter) {
throw Error('Invalid Input after splitting')
}
return ''
}

const result = splitUp.map(xorWithClock).join('')

return options.inverted ? invertResult(result) : result
}

const xorWithClock = i => (Number.parseInt(i, 2) ^ getClock(i.length)).toString(2).padStart(i.length, '0')

const getClock = len => {
const clock = '01'.repeat(len / 2)

return Number.parseInt(clock, 2)
}

const invertResult = r => r.replace(/[01]/g, n => 1 - n)

const splitInput = i => i.match(/[01]{1,6}/g)

const DEFAULT_OPTIONS = {
inverted: false,
failOnUnknownCharacter: true
}

export default {
decode,
encode
}
71 changes: 71 additions & 0 deletions test/manchester.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import manchester from 'manchester'

const silentFailOptions = {
failOnUnknownCharacter: false
}

const invertedOptions = {
inverted: true
}

describe('encoding', () => {
test('default', () => {
expect(manchester.encode('0')).toBe('01')
expect(manchester.encode('01')).toBe('0110')
expect(manchester.encode('010')).toBe('011001')
expect(manchester.encode('0010110101000101')).toBe('01011001101001100110010101100110')
})

test('inverted', () => {
expect(manchester.encode('0', invertedOptions)).toBe('10')
expect(manchester.encode('01', invertedOptions)).toBe('1001')
expect(manchester.encode('010', invertedOptions)).toBe('100110')
expect(manchester.encode('0010110101000101', invertedOptions)).toBe('10100110010110011001101010011001')
})

test('empty', () => {
expect(manchester.encode('', silentFailOptions)).toBe('')
expect(() => { manchester.encode('') }).toThrowError('Invalid Input after splitting')
})

test('invalid', () => {
expect(manchester.encode('A', silentFailOptions)).toBe('')
expect(manchester.encode('€', silentFailOptions)).toBe('')
expect(manchester.encode('€0', silentFailOptions)).toBe('01')

expect(() => { manchester.encode('A') }).toThrowError('Invalid Input')
expect(() => { manchester.encode('€') }).toThrowError('Invalid Input')
})
})

describe('decoding', () => {
test('default', () => {
expect(manchester.decode('01')).toBe('0')
expect(manchester.decode('0110')).toBe('01')
expect(manchester.decode('011001')).toBe('010')
expect(manchester.decode('01011001101001100110010101100110')).toBe('0010110101000101')
})

test('inverted', () => {
expect(manchester.decode('10', invertedOptions)).toBe('0')
expect(manchester.decode('1001', invertedOptions)).toBe('01')
expect(manchester.decode('100110', invertedOptions)).toBe('010')
expect(manchester.decode('10100110010110011001101010011001', invertedOptions)).toBe('0010110101000101')
})

test('empty', () => {
expect(manchester.decode('', silentFailOptions)).toBe('')
expect(() => { manchester.decode('') }).toThrowError('Invalid Input after splitting')
})

test('invalid', () => {
expect(manchester.decode('0', silentFailOptions)).toBe('')
expect(manchester.decode('A', silentFailOptions)).toBe('')
expect(manchester.decode('€', silentFailOptions)).toBe('')
expect(manchester.decode('€01', silentFailOptions)).toBe('0')

expect(() => { manchester.decode('0') }).toThrowError('Invalid Input')
expect(() => { manchester.decode('A') }).toThrowError('Invalid Input')
expect(() => { manchester.decode('€') }).toThrowError('Invalid Input')
})
})

0 comments on commit 377f838

Please sign in to comment.