Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • hlgr/drawing-app
  • sweng-group-15/drawing-app
2 results
Show changes
Commits on Source (325)
Showing
with 8504 additions and 778 deletions
{
"env": {
"test": {
"plugins": ["@babel/plugin-transform-modules-commonjs"]
}
}
}
......@@ -5,14 +5,19 @@
"node": true,
"jest": true
},
"extends": "eslint:recommended",
"plugins": ["testcafe"],
"extends": ["eslint:recommended", "prettier", "plugin:testcafe/recommended"],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {}
"rules": {
"no-var": "error",
"prefer-const": "error"
}
}
# Created by https://www.gitignore.io/api/vim,node,macos,visualstudiocode
# Edit at https://www.gitignore.io/?templates=vim,node,macos,visualstudiocode
......@@ -10,6 +9,54 @@ src/liowebrtc
src/rtcpeerconnection
src/signalbuddy
src/yjs
src/drawing-crdt
# Temporary benchmark dump files
.dot-seq-add-packets.json
.dot-seq-erase-packets.json
.dot-seq-sync-packets.json
.dot-seq-add-events.json
.dot-seq-erase-events.json
.dot-seq-sync-events.json
.dot-par-add-packets.json
.dot-par-erase-packets.json
.dot-par-sync-packets.json
.dot-par-add-events.json
.dot-par-erase-events.json
.dot-par-sync-events.json
.path-seq-add-packets.json
.path-seq-erase-packets.json
.path-seq-sync-packets.json
.path-seq-add-events.json
.path-seq-erase-events.json
.path-seq-sync-events.json
.path-par-add-packets.json
.path-par-erase-packets.json
.path-par-sync-packets.json
.path-par-add-events.json
.path-par-erase-events.json
.path-par-sync-events.json
.dot-ver-add-packets.json
.dot-ver-erase-packets.json
.dot-ver-sync-packets.json
.dot-ver-add-events.json
.dot-ver-erase-events.json
.dot-ver-sync-events.json
.path-ver-add-packets.json
.path-ver-erase-packets.json
.path-ver-sync-packets.json
.path-ver-add-events.json
.path-ver-erase-events.json
.path-ver-sync-events.json
# Benchmark output files
plots/*.tsv
plots/*.pdf
### macOS ###
# General
......@@ -118,7 +165,10 @@ typings/
.nuxt
# react / gatsby (customised)
public/js/app.js
public/service-worker.js
public/js
public/benchmarks.html
public/assets/fonts/font-awesome
# vuepress build output
.vuepress/dist
......@@ -154,13 +204,12 @@ tags
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
# Jupyter checkpoints
**/.ipynb_checkpoints
# End of https://www.gitignore.io/api/vim,node,macos,visualstudiocode
default:
image: node:12
image: amio/node-chrome
stages:
- fetch
......@@ -8,21 +8,23 @@ stages:
- build
- test
- deploy
- benchmark
submodule_fetch:
stage: fetch
script:
- chmod 600 .drawing-app-deploy.rsa
- git submodule sync --recursive
- GIT_SSH_COMMAND='ssh -i .drawing-app-deploy.rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' git submodule update --init
- GIT_SSH_COMMAND='ssh -i '`pwd`'/.drawing-app-deploy.rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' git submodule update --init --recursive
artifacts:
paths:
- src/liowebrtc
- src/rtcpeerconnection
- src/signalbuddy
- src/yjs
- src/drawing-crdt/pkg
node_install:
npm_install_prod:
stage: deps
dependencies:
- submodule_fetch
......@@ -31,12 +33,9 @@ node_install:
artifacts:
paths:
- node_modules
- src/liowebrtc
- src/rtcpeerconnection
- src/signalbuddy
- src/yjs
dev_node_install:
npm_install:
stage: deps
dependencies:
- submodule_fetch
......@@ -47,48 +46,76 @@ dev_node_install:
- node_modules
- src/liowebrtc
- src/rtcpeerconnection
- src/signalbuddy
- src/yjs
- src/drawing-crdt/pkg
- src/signalbuddy
check_format:
format_check:
stage: check
dependencies:
- dev_node_install
- npm_install
script:
- npx prettier --ignore-path .gitignore --check "**/*.{html,js,json,md}"
- npm run format-check
lint:
stage: check
dependencies:
- dev_node_install
- npm_install
script:
- npx eslint --ignore-path .gitignore "**/*.js"
- npm run lint
backend_build:
build:
stage: build
dependencies:
- dev_node_install
- npm_install
script:
- npm run build
- gcc -E -P -traditional-cpp -o /dev/stdout -DFILES_TO_CACHE_LIST=`find public/ -type f "!" -iname service-worker.js -and "!" -name '.*' | cut -c6- | sed 's_._"_' | sed 's/$/",/' | sort | tr -d '\n'` - < src/service-worker.js | npx prettier --parser babel > public/service-worker.js
artifacts:
paths:
- public/
backend_test:
test:
stage: test
dependencies:
- npm_install
script:
- npm run test
chrome_test:
stage: test
dependencies:
- dev_node_install
- npm_install
- build
script:
- npm test
- npm run start-bg
- npm run test-e2e
deploy:
stage: deploy
dependencies:
- node_install
- backend_build
- npm_install_prod
- build
only:
- master
script:
- apt-get update -qq && apt-get install -qq zip
- zip -r application.zip *
- curl -X POST -u "$DEPLOYMENT_USERNAME:$DEPLOYMENT_PASSWORD" $DEPLOYMENT_ENDPOINT -T application.zip
benchmark:
stage: benchmark
dependencies:
- npm_install
only:
- master
script:
- apt-get -y install gnuplot
- npm run build:bench
- cp __benchmarks__/benchmarks.html public/benchmarks.html
- npm run start-bg
- npm run benchmarks
- npm run plot
artifacts:
paths:
- plots/
......@@ -10,3 +10,6 @@
[submodule "src/yjs"]
path = src/yjs
url = git@gitlab.doc.ic.ac.uk:sweng-group-15/yjs.git
[submodule "src/drawing-crdt"]
path = src/drawing-crdt
url = git@gitlab.doc.ic.ac.uk:sweng-group-15/drawing-crdt.git
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<script src="js/benchmarks.js"></script>
</body>
</html>
import { test, equal, ok, notOk } from "zora"
import chalk from "chalk"
import { connect } from "../src/room.js"
import CRDT, { benchmark } from "../src/wasm-crdt.js"
//import CRDT, { benchmark } from "../src/y-crdt.js"
const { blocksize, eventsGC, syncStep1 } = benchmark
import MockConnection, {
userID,
sendListener,
broadcastListener,
getEventListener,
} from "../src/connection/MockConnection.js"
import { handshake, dotDraw, dotErase, pathDraw, pathErase } from "./data.js"
const remoteUserID = "392bf960-1d18-4482-bbbf-1c85e0132c9a"
function createMessageReceivedEvent(
message,
channel = "crdt",
uid = remoteUserID,
) {
return {
detail: {
uid,
channel,
message,
},
}
}
// Start: Adapted from https://github.com/jprichardson/buffer-json (MIT license)
function stringify(value, space) {
return JSON.stringify(value, replacer, space)
}
function parse(text) {
return JSON.parse(text, reviver)
}
function replacer(key, value) {
if (value instanceof Uint8Array) {
return "base64:" + Buffer.from(value).toString("base64")
}
return value
}
function reviver(key, value) {
if (typeof value == "string" && value.startsWith("base64:")) {
return Uint8Array.from(Buffer.from(value.slice("base64:".length), "base64"))
}
return value
}
// End: Adapted from https://github.com/jprichardson/buffer-json (MIT license)
let fs
async function initFileSystem() {
fs = await new Promise((resolve, reject) =>
window.webkitRequestFileSystem(
window.TEMPORARY,
1024 * 1024 * 100,
resolve,
reject,
),
)
}
async function dumpBSON(filename, data) {
await new Promise((resolve) => {
fs.root.getFile(
filename,
{ create: false },
(fileEntry) => {
fileEntry.remove(resolve, console.error)
},
resolve,
)
})
await new Promise((resolve) => {
fs.root.getFile(
filename,
{ create: true, exclusive: true },
(fileEntry) => {
fileEntry.createWriter((fileWriter) => {
fileWriter.onwriteend = resolve
fileWriter.onerror = console.error
const blob = new Blob([stringify(data)], { type: "text/plain" })
fileWriter.write(blob)
}, console.error)
},
console.error,
)
})
}
async function loadBSON(filename) {
return await new Promise((resolve) => {
fs.root.getFile(
filename,
{},
(fileEntry) => {
fileEntry.file((file) => {
const reader = new FileReader()
reader.onloadend = () => resolve(parse(reader.result))
reader.readAsText(file)
}, console.error)
},
console.error,
)
})
}
function printBenchmark(title, iterations, results) {
console.debug(`\n ${title} (${iterations} iterations):\n`)
for (const title in results) {
const {
timeLoc,
encodeRAM,
packets,
size,
timeRem,
decodeRAM,
events,
} = results[title]
const synchronisation = title == "synchronisation"
console.debug(
chalk` {yellow ⧗} {dim ${title}:} {yellow.inverse ${(
timeLoc /
(1e3 * (synchronisation ? 1 : iterations))
).toFixed(3)}ms ${synchronisation ? "total" : "/ it"}} + {red.inverse ${(
encodeRAM /
(1024 * 1024)
).toFixed(
3,
)}MB} => {dim ${packets} packet(s)} => {magenta.inverse ${size}B} => {yellow.inverse ${(
timeRem /
(1e3 * (synchronisation ? 1 : iterations))
).toFixed(3)}ms ${synchronisation ? "total" : "/ it"}} + {red.inverse ${(
decodeRAM /
(1024 * 1024)
).toFixed(3)}MB} => {dim ${events} event(s)}\n`,
)
}
console.debug(`\n`)
}
function writeBenchmarkHeader(filename, title, results) {
console.info(JSON.stringify({ filename, title, results }))
}
function appendBenchmark(filename, iterations, results) {
console.info(JSON.stringify({ filename, iterations, results }))
}
function captureHeapUsage() {
window.gc()
return window.performance.memory.usedJSHeapSize
}
function runBidirectionalBenchmark(
BENCHMARK,
FILENAME,
ITERATIONSLIST,
BLOCKSIZE,
addData,
eraseData,
addOnInitFrontend,
addBroadcastGroupTimeout,
addOnBroadcastGroup,
addPacketsFilename,
eraseOnInitFrontend,
eraseBroadcastGroupTimeout,
eraseOnBroadcastGroup,
erasePacketsFilename,
syncSendGroupTimeout,
syncOnSendGroup,
syncPacketsFilename,
addOnInitBackend,
addEventGroupTimeout,
addOnEventGroup,
addEventsCache,
addEventsFilename,
eraseOnInitBackend,
eraseEventGroupTimeout,
eraseOnEventGroup,
eraseEventsCache,
eraseEventsFilename,
syncEventGroupTimeout,
syncOnEventGroup,
syncEventsCache,
syncEventsFilename,
) {
if (BENCHMARK && FILENAME) {
writeBenchmarkHeader(FILENAME, BENCHMARK, [
"addPath",
"extendErasureIntervals",
"synchronisation",
])
}
return ITERATIONSLIST.reduce(
(promise, ITERATIONS) =>
promise.then(() => {
const pathIDs = []
let prevTime
let currTime
let connectRAM // eslint-disable-line no-unused-vars
let addLocTime = 0
let addPackets = []
let addSize = 0
let addRAM
let eraseLocTime = 0
let erasePackets = []
let eraseSize = 0
let eraseRAM
let syncLocTime
let syncPackets = []
let syncSize = 0
let syncRAM
let disconnectRAM // eslint-disable-line no-unused-vars
let connectUpdRAM // eslint-disable-line no-unused-vars
let addRemTime = 0
let addEvents = addEventsCache ? [] : 0
let addRemRAM
let eraseRemTime = 0
let eraseEvents = eraseEventsCache ? [] : 0
let eraseRemRAM
let disconnectUpdRAM // eslint-disable-line no-unused-vars
let connectSyncRAM // eslint-disable-line no-unused-vars
let syncRemTime = 0
let syncEvents = syncEventsCache ? [] : 0
let syncRemRAM
let disconnectSyncRAM // eslint-disable-line no-unused-vars
let timeout
let addEventListener = null
let eraseEventListener = null
let room = null
let updateRoom = null
let syncRoom = null
return (
// eslint-disable-next-line no-async-promise-executor
new Promise(async (resolve) => {
userID.uuid = "61a540d6-4522-48c7-a660-1ed501503cb7"
room = await connect("room", CRDT, MockConnection)
getEventListener(
"room",
"messageReceived",
)(createMessageReceivedEvent(handshake, "tw-ml"))
connectRAM = captureHeapUsage()
return resolve()
})
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
broadcastListener.callback = (channel, message) => {
currTime = window.performance.now()
equal(channel, "crdt")
ok(message instanceof Uint8Array)
addPackets[addPackets.length - 1].push(message)
addSize += message.length
clearTimeout(timeout)
timeout = setTimeout(() => {
broadcasts += 1
addLocTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
addOnBroadcastGroup(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
broadcasts,
resolve,
)
}, addBroadcastGroupTimeout)
}
prevTime = window.performance.now()
addOnInitFrontend(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
BLOCKSIZE,
)
}),
)
.then(async () => {
broadcastListener.callback = null
await dumpBSON(addPacketsFilename, addPackets)
addPackets = null
addRAM = captureHeapUsage()
})
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
broadcastListener.callback = (channel, message) => {
currTime = window.performance.now()
equal(channel, "crdt")
ok(message instanceof Uint8Array)
erasePackets[erasePackets.length - 1].push(message)
eraseSize += message.length
clearTimeout(timeout)
timeout = setTimeout(() => {
broadcasts += 1
eraseLocTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
eraseOnBroadcastGroup(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
broadcasts,
resolve,
)
}, eraseBroadcastGroupTimeout)
}
prevTime = window.performance.now()
eraseOnInitFrontend(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
BLOCKSIZE,
)
}),
)
.then(async () => {
broadcastListener.callback = null
await dumpBSON(erasePacketsFilename, erasePackets)
erasePackets = null
eraseRAM = captureHeapUsage()
})
.then(
() =>
new Promise((resolve) => {
sendListener.callback = (uid, channel, message) => {
const currTime = window.performance.now()
equal(uid, remoteUserID)
equal(channel, "crdt")
ok(message instanceof Uint8Array)
syncLocTime = (currTime - prevTime) * 1e3
syncPackets.push(message)
syncSize += message.length
clearTimeout(timeout)
timeout = setTimeout(
() => syncOnSendGroup(syncPackets, resolve),
syncSendGroupTimeout,
)
}
prevTime = window.performance.now()
getEventListener(
"room",
"messageReceived",
)(createMessageReceivedEvent(syncStep1))
}),
)
.then(async () => {
sendListener.callback = null
await dumpBSON(syncPacketsFilename, syncPackets)
syncPackets = null
syncRAM = captureHeapUsage()
room.disconnect()
room = null
disconnectRAM = captureHeapUsage()
})
.then(
() =>
// eslint-disable-next-line no-async-promise-executor
new Promise(async (resolve) => {
userID.uuid = "5c9e550b-3de8-4a32-80e1-80c08c19891a"
updateRoom = await connect("update", CRDT, MockConnection)
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(handshake, "tw-ml"))
connectUpdRAM = captureHeapUsage()
addPackets = await loadBSON(addPacketsFilename)
return resolve()
}),
)
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
let currTime
const timeoutCallback = () => {
broadcasts += 1
addRemTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
addOnEventGroup(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents,
pathIDs,
addData,
)
}
addEventListener = (event) => {
currTime = window.performance.now()
if (addEventsCache) {
addEvents.push({ add: event })
} else {
addEvents += 1
}
clearTimeout(timeout)
timeout = setTimeout(timeoutCallback, addEventGroupTimeout)
}
eraseEventListener = (event) => {
currTime = window.performance.now()
if (addEventsCache) {
addEvents.push({ erase: event })
} else {
addEvents += 1
}
clearTimeout(timeout)
timeout = setTimeout(timeoutCallback, addEventGroupTimeout)
}
updateRoom.addEventListener(
"addOrUpdatePath",
addEventListener,
)
updateRoom.addEventListener(
"removedIntervalsChange",
eraseEventListener,
)
prevTime = window.performance.now()
addOnInitBackend(addPackets, BLOCKSIZE)
}),
)
.then(async () => {
updateRoom.removeEventListener(
"addOrUpdatePath",
addEventListener,
)
addEventListener = null
updateRoom.removeEventListener(
"removedIntervalsChange",
eraseEventListener,
)
eraseEventListener = null
addPackets = null
await dumpBSON(addEventsFilename, addEvents)
addEvents = null
addRemRAM = captureHeapUsage()
erasePackets = await loadBSON(erasePacketsFilename)
})
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
let currTime
const timeoutCallback = () => {
broadcasts += 1
eraseRemTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
eraseOnEventGroup(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents,
pathIDs,
addData,
eraseData,
)
}
eraseEventListener = (event) => {
currTime = window.performance.now()
if (eraseEventsCache) {
eraseEvents.push({ erase: event })
} else {
eraseEvents += 1
}
clearTimeout(timeout)
timeout = setTimeout(
timeoutCallback,
eraseEventGroupTimeout,
)
}
updateRoom.addEventListener(
"removedIntervalsChange",
eraseEventListener,
)
prevTime = window.performance.now()
eraseOnInitBackend(erasePackets, BLOCKSIZE)
}),
)
.then(async () => {
updateRoom.removeEventListener(
"removedIntervalsChange",
eraseEventListener,
)
eraseEventListener = null
erasePackets = null
await dumpBSON(eraseEventsFilename, eraseEvents)
eraseEvents = null
eraseRemRAM = captureHeapUsage()
updateRoom.disconnect()
updateRoom = null
disconnectUpdRAM = captureHeapUsage()
})
.then(
() =>
// eslint-disable-next-line no-async-promise-executor
new Promise(async (resolve) => {
userID.uuid = "a2108f84-3785-4696-8dd5-fb89b38d4f7f"
syncRoom = await connect("sync", CRDT, MockConnection)
getEventListener(
"sync",
"messageReceived",
)(createMessageReceivedEvent(handshake, "tw-ml"))
connectSyncRAM = captureHeapUsage()
syncPackets = await loadBSON(syncPacketsFilename)
return resolve()
}),
)
.then(
() =>
new Promise((resolve) => {
addEventListener = (event) => {
const currTime = window.performance.now()
syncRemTime = (currTime - prevTime) * 1e3
if (syncEventsCache) {
syncEvents.push({ add: event })
} else {
syncEvents += 1
if (syncEvents % eventsGC == 1) {
captureHeapUsage()
}
}
clearTimeout(timeout)
timeout = setTimeout(
() =>
syncOnEventGroup(
resolve,
syncEvents,
pathIDs,
addData,
eraseData,
),
syncEventGroupTimeout,
)
}
eraseEventListener = (event) => {
const currTime = window.performance.now()
syncRemTime = (currTime - prevTime) * 1e3
if (syncEventsCache) {
syncEvents.push({ erase: event })
} else {
syncEvents += 1
if (syncEvents % eventsGC == 1) {
captureHeapUsage()
}
}
clearTimeout(timeout)
timeout = setTimeout(
() =>
syncOnEventGroup(
resolve,
syncEvents,
pathIDs,
addData,
eraseData,
),
syncEventGroupTimeout,
)
}
syncRoom.addEventListener("addOrUpdatePath", addEventListener)
syncRoom.addEventListener(
"removedIntervalsChange",
eraseEventListener,
)
prevTime = window.performance.now()
for (const syncPacket of syncPackets) {
getEventListener(
"sync",
"messageReceived",
)(createMessageReceivedEvent(syncPacket))
}
syncPackets = null
captureHeapUsage()
}),
)
.then(async () => {
syncRoom.removeEventListener("addOrUpdatePath", addEventListener)
addEventListener = null
syncRoom.removeEventListener(
"removedIntervalsChange",
eraseEventListener,
)
eraseEventListener = null
syncPackets = null
await dumpBSON(syncEventsFilename, syncEvents)
syncEvents = null
syncRemRAM = captureHeapUsage()
syncRoom.disconnect()
syncRoom = null
disconnectSyncRAM = captureHeapUsage()
})
.then(async () => {
if (!BENCHMARK) {
return
}
addPackets = (await loadBSON(addPacketsFilename)).reduce(
(sum, packets) => sum + packets.length,
0,
)
erasePackets = (await loadBSON(erasePacketsFilename)).reduce(
(sum, packets) => sum + packets.length,
0,
)
syncPackets = (await loadBSON(syncPacketsFilename)).length
addEvents = await loadBSON(addEventsFilename)
addEvents = addEventsCache ? addEvents.length : addEvents
eraseEvents = await loadBSON(eraseEventsFilename)
eraseEvents = eraseEventsCache ? eraseEvents.length : eraseEvents
syncEvents = await loadBSON(syncEventsFilename)
syncEvents = syncEventsCache ? syncEvents.length : syncEvents
const results = {
addPath: {
timeLoc: addLocTime,
encodeRAM: addRAM,
packets: addPackets,
size: addSize,
timeRem: addRemTime,
decodeRAM: addRemRAM,
events: addEvents,
},
extendErasureIntervals: {
timeLoc: eraseLocTime,
encodeRAM: eraseRAM,
packets: erasePackets,
size: eraseSize,
timeRem: eraseRemTime,
decodeRAM: eraseRemRAM,
events: eraseEvents,
},
synchronisation: {
timeLoc: syncLocTime,
encodeRAM: syncRAM,
packets: syncPackets,
size: syncSize,
timeRem: syncRemTime,
decodeRAM: syncRemRAM,
events: syncEvents,
},
}
printBenchmark(BENCHMARK, ITERATIONS, results)
appendBenchmark(FILENAME, ITERATIONS, results)
})
)
}),
Promise.resolve(),
)
}
function addOnInitFrontendSequential(
room,
addPackets,
pathIDs,
addData,
ITERATIONS, // eslint-disable-line no-unused-vars
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
addPackets.push([])
const drawPathID = room.addPath(addData[0])
pathIDs.push(drawPathID)
for (let i = 1; i < addData.length; i++) {
room.extendPath(drawPathID, addData[i])
}
room.endPath(drawPathID)
}
function addOnBroadcastGroupSequential(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
broadcasts,
resolve,
) {
if (broadcasts < ITERATIONS) {
addPackets.push([])
const drawPathID = room.addPath(addData[0])
pathIDs.push(drawPathID)
for (let i = 1; i < addData.length; i++) {
room.extendPath(drawPathID, addData[i])
}
room.endPath(drawPathID)
} else {
resolve()
}
}
function eraseOnInitFrontendSequential(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS, // eslint-disable-line no-unused-vars
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
erasePackets.push([])
const erasePathID = pathIDs[0]
for (let i = 0; i < eraseData.length; i++) {
room.extendErasureIntervals(erasePathID, eraseData[i][0], eraseData[i][1])
}
}
function eraseOnBroadcastGroupSequential(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
broadcasts,
resolve,
) {
if (broadcasts < ITERATIONS) {
erasePackets.push([])
const erasePathID = pathIDs[broadcasts]
for (let i = 0; i < eraseData.length; i++) {
room.extendErasureIntervals(erasePathID, eraseData[i][0], eraseData[i][1])
}
} else {
resolve()
}
}
function syncOnSendGroup(syncPackets, resolve) {
resolve()
}
function addOnInitBackendSequential(
addPackets,
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
for (const packet of addPackets[0]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function addOnEventGroupSequential(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
) {
if (broadcasts >= ITERATIONS) {
return resolve()
}
for (const packet of addPackets[broadcasts]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function eraseOnInitBackendSequential(
erasePackets,
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
for (const packet of erasePackets[0]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function eraseOnEventGroupSequential(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
eraseData, // eslint-disable-line no-unused-vars
) {
if (broadcasts >= ITERATIONS) {
return resolve()
}
for (const packet of erasePackets[broadcasts]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function syncOnEventGroup(
resolve,
syncEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
eraseData, // eslint-disable-line no-unused-vars
) {
resolve()
}
function addOnInitFrontendParallel(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
BLOCKSIZE,
) {
addPackets.push([])
// Necessary to allow yjs to execute transactions (majority of processing time)
function addPath(sj) {
if (sj >= ITERATIONS) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, ITERATIONS); j++) {
const drawPathID = room.addPath(addData[0])
pathIDs.push(drawPathID)
for (let i = 1; i < addData.length; i++) {
room.extendPath(drawPathID, addData[i])
}
room.endPath(drawPathID)
}
captureHeapUsage()
setTimeout(addPath, 0, sj + BLOCKSIZE)
}
addPath(0)
}
function addOnBroadcastGroupParallel(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
broadcasts,
resolve,
) {
resolve()
}
function eraseOnInitFrontendParallel(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
BLOCKSIZE,
) {
erasePackets.push([])
// Necessary to allow yjs to execute transactions (majority of processing time)
function erasePath(sj) {
if (sj >= ITERATIONS) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, ITERATIONS); j++) {
const erasePathID = pathIDs[j]
for (let i = 0; i < eraseData.length; i++) {
room.extendErasureIntervals(
erasePathID,
eraseData[i][0],
eraseData[i][1],
)
}
}
captureHeapUsage()
setTimeout(erasePath, 0, sj + BLOCKSIZE)
}
erasePath(0)
}
function eraseOnBroadcastGroupParallel(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
broadcasts,
resolve,
) {
resolve()
}
function addOnInitBackendParallel(addPackets, BLOCKSIZE) {
const packets = addPackets[0]
function addPath(sj) {
if (sj >= packets.length) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, packets.length); j++) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packets[j]))
}
captureHeapUsage()
setTimeout(addPath, 0, sj + BLOCKSIZE)
}
addPath(0)
}
function addOnEventGroupParallel(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
) {
resolve()
}
function eraseOnInitBackendParallel(erasePackets, BLOCKSIZE) {
const packets = erasePackets[0]
function erasePath(sj) {
if (sj >= packets.length) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, packets.length); j++) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packets[j]))
}
captureHeapUsage()
setTimeout(erasePath, 0, sj + BLOCKSIZE)
}
erasePath(0)
}
function eraseOnEventGroupParallel(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
eraseData, // eslint-disable-line no-unused-vars
) {
resolve()
}
function addOnEventGroupVerify(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents,
pathIDs,
addData,
) {
const updatePaths = {}
const updateIntervals = {}
for (const event of addEvents) {
equal(!event.add + !event.erase, 1)
if (event.add) {
const {
detail: { id, points },
} = event.add
updatePaths[id] = points
} else if (event.erase) {
const {
detail: { id, intervals },
} = event.erase
updateIntervals[id] = intervals
}
}
equal(updatePaths, { [pathIDs[0]]: addData })
equal(updateIntervals, { [pathIDs[0]]: {} })
resolve()
}
function eraseOnEventGroupVerify(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents,
pathIDs,
addData,
eraseData, // eslint-disable-line no-unused-vars
) {
const updateIntervals = {}
for (const event of eraseEvents) {
ok(!event.add)
notOk(!event.erase)
const {
detail: { id, intervals },
} = event.erase
updateIntervals[id] = intervals
}
equal(updateIntervals, {
[pathIDs[0]]: Object.assign(
{},
new Array(addData.length).fill([[0, 0 + (addData.length > 1)]]),
),
})
resolve()
}
function syncOnEventGroupVerify(
resolve,
syncEvents,
pathIDs,
addData,
eraseData, // eslint-disable-line no-unused-vars
) {
const syncPaths = {}
const syncIntervals = {}
for (const event of syncEvents) {
equal(!event.add + !event.erase, 1)
if (event.add) {
const {
detail: { id, points },
} = event.add
syncPaths[id] = points
} else if (event.erase) {
const {
detail: { id, intervals },
} = event.erase
syncIntervals[id] = intervals
}
}
equal(syncPaths, { [pathIDs[0]]: addData })
equal(syncIntervals, {
[pathIDs[0]]: Object.assign(
{},
new Array(addData.length).fill([[0, 0 + (addData.length > 1)]]),
),
})
resolve()
}
test("benchmark", async (t) => {
await initFileSystem()
const ITERATIONSLIST = [10, 25, 50, 75, 100, 250, 500]
const BLOCKSIZE = blocksize
await t.test("communicates a dot draw and erase update", async (/*t*/) => {
return runBidirectionalBenchmark(
null /* BENCHMARK */,
null /* FILENAME */,
[1] /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
dotDraw /* addData */,
dotErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
1000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".dot-ver-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
1000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".dot-ver-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".dot-ver-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
1000 /* addEventGroupTimeout */,
addOnEventGroupVerify /* addOnEventGroup */,
true /* addEventsCache */,
".dot-ver-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
1000 /* eraseEventGroupTimeout */,
eraseOnEventGroupVerify /* eraseOnEventGroupTimeout */,
true /* eraseEventsCache */,
".dot-ver-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroupVerify /* syncOnEventGroup */,
true /* syncEventsCache */,
".dot-ver-sync-events.json" /* syncEventsFilename */,
)
})
await t.test(
"benchmarks a dot draw and erase update sequentially",
async (/*t*/) => {
return runBidirectionalBenchmark(
"dot draw and erase [sequential]" /* BENCHMARK */,
"plots/dot-seq-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
dotDraw /* addData */,
dotErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
100 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupSequential /* addOnBroadcastGroup */,
".dot-seq-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
100 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupSequential /* eraseOnBroadcastGroup */,
".dot-seq-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".dot-seq-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
100 /* addEventGroupTimeout */,
addOnEventGroupSequential /* addOnEventGroup */,
false /* addEventsCache */,
".dot-seq-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
100 /* eraseEventGroupTimeout */,
eraseOnEventGroupSequential /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".dot-seq-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".dot-seq-sync-events.json" /* syncEventsFilename */,
)
},
)
await t.test(
"benchmarks a dot draw and erase update in parallel",
async (/*t*/) => {
return runBidirectionalBenchmark(
"dot draw and erase [parallel]" /* BENCHMARK */,
"plots/dot-par-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
dotDraw /* addData */,
dotErase /* eraseData */,
addOnInitFrontendParallel /* addOnInitFrontend */,
1000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".dot-par-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendParallel /* eraseOnInitFrontend */,
1000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".dot-par-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".dot-par-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendParallel /* addOnInitBackend */,
1000 /* addEventGroupTimeout */,
addOnEventGroupParallel /* addOnEventGroup */,
false /* addEventsCache */,
".dot-par-add-events.json" /* addEventsFilename */,
eraseOnInitBackendParallel /* eraseOnInitBackend */,
1000 /* eraseEventGroupTimeout */,
eraseOnEventGroupParallel /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".dot-par-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".dot-par-sync-events.json" /* syncEventsFilename */,
)
},
)
await t.test("communicates a path draw and erase update", async (/*t*/) => {
return runBidirectionalBenchmark(
null /* BENCHMARK */,
null /* FILENAME */,
[1] /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
pathDraw /* addData */,
pathErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
1000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".path-ver-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
1000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".path-ver-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".path-ver-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
1000 /* addEventGroupTimeout */,
addOnEventGroupVerify /* addOnEventGroup */,
true /* addEventsCache */,
".path-ver-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
1000 /* eraseEventGroupTimeout */,
eraseOnEventGroupVerify /* eraseOnEventGroupTimeout */,
true /* eraseEventsCache */,
".path-ver-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroupVerify /* syncOnEventGroup */,
true /* syncEventsCache */,
".path-ver-sync-events.json" /* syncEventsFilename */,
)
})
await t.test(
"benchmarks a path draw and erase update sequentially",
async (/*t*/) => {
return runBidirectionalBenchmark(
"path draw and erase [sequential]" /* BENCHMARK */,
"plots/path-seq-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
pathDraw /* addData */,
pathErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
100 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupSequential /* addOnBroadcastGroup */,
".path-seq-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
100 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupSequential /* eraseOnBroadcastGroup */,
".path-seq-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".path-seq-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
100 /* addEventGroupTimeout */,
addOnEventGroupSequential /* addOnEventGroup */,
false /* addEventsCache */,
".path-seq-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
100 /* eraseEventGroupTimeout */,
eraseOnEventGroupSequential /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".path-seq-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".path-seq-sync-events.json" /* syncEventsFilename */,
)
},
)
await t.test(
"benchmarks a path draw and erase update in parallel",
async (/*t*/) => {
return runBidirectionalBenchmark(
"path draw and erase [parallel]" /* BENCHMARK */,
"plots/path-par-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
pathDraw /* addData */,
pathErase /* eraseData */,
addOnInitFrontendParallel /* addOnInitFrontend */,
5000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".path-par-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendParallel /* eraseOnInitFrontend */,
5000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".path-par-erase-packets.json" /* erasePacketsFilename */,
5000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".path-par-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendParallel /* addOnInitBackend */,
5000 /* addEventGroupTimeout */,
addOnEventGroupParallel /* addOnEventGroup */,
false /* addEventsCache */,
".path-par-add-events.json" /* addEventsFilename */,
eraseOnInitBackendParallel /* eraseOnInitBackend */,
5000 /* eraseEventGroupTimeout */,
eraseOnEventGroupParallel /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".path-par-erase-events.json" /* eraseEventsFilename */,
5000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".path-par-sync-events.json" /* syncEventsFilename */,
)
},
)
})
export const handshake = Uint8Array.of(
133,
164,
117,
117,
105,
100,
217,
36,
97,
54,
100,
57,
50,
55,
97,
56,
45,
53,
50,
57,
101,
45,
52,
55,
50,
54,
45,
97,
102,
48,
98,
45,
56,
48,
57,
55,
98,
48,
48,
102,
55,
99,
49,
55,
167,
109,
101,
115,
115,
97,
103,
101,
196,
3,
162,
109,
108,
165,
115,
108,
105,
99,
101,
0,
166,
108,
101,
110,
103,
116,
104,
3,
170,
99,
111,
109,
112,
114,
101,
115,
115,
101,
100,
194,
)
export const dotDraw = [[209, 88, 5.0, "#0000ff"]]
export const dotErase = [[0, [[0, 0]]]]
export const pathDraw = [
[229, 147, 5.0, "#0000ff"],
[239, 149, 5.0, "#0000ff"],
[265, 154, 5.0, "#0000ff"],
[329, 158, 5.0, "#0000ff"],
[428, 168, 5.0, "#0000ff"],
[559, 172, 5.0, "#0000ff"],
[689, 176, 5.0, "#0000ff"],
[789, 176, 5.0, "#0000ff"],
[871, 178, 5.0, "#0000ff"],
[915, 179, 5.0, "#0000ff"],
[937, 179, 5.0, "#0000ff"],
[942, 179, 5.0, "#0000ff"],
]
export const pathErase = [
[0, [[0, 0.030367582231477598]]],
[0, [[0, 0.21377102974828613]]],
[0, [[0, 0.2537463508623564]]],
[0, [[0, 0.34615384615384587]]],
[0, [[0, 0.4383613354449363]]],
[0, [[0, 0.5303675822314777]]],
[0, [[0, 0.6221712759429692]]],
[0, [[0, 0.7137710297482863]]],
[0, [[0, 0.8051653782004998]]],
[0, [[0, 0.9383613354449363]]],
[0, [[0, 1]]],
[1, [[0, 0.011716311379342382]]],
[1, [[0, 0.11776520948773353]]],
[1, [[0, 0.24006055181058228]]],
[1, [[0, 0.3107159148890084]]],
[1, [[0, 0.3810746303975673]]],
[1, [[0, 0.4329788671985464]]],
[1, [[0, 0.46835801309405006]]],
[1, [[0, 0.5036636541863274]]],
[1, [[0, 0.5740522382479196]]],
[1, [[0, 0.6441399664465288]]],
[1, [[0, 0.731854882371686]]],
[1, [[0, 0.8021237085400632]]],
[1, [[0, 0.8371448744989799]]],
[1, [[0, 0.88955025220044]]],
[1, [[0, 0.9599984343180302]]],
[1, [[0, 1]]],
[2, [[0, 0.028177027652500065]]],
[2, [[0, 0.03543420592413073]]],
[2, [[0, 0.06582991759489924]]],
[2, [[0, 0.10275381734879]]],
[2, [[0, 0.11799692411252251]]],
[2, [[0, 0.13323648198760243]]],
[2, [[0, 0.16370490408198182]]],
[2, [[0, 0.2154080009026301]]],
[2, [[0, 0.24595364724469834]]],
[2, [[0, 0.27648544742767633]]],
[2, [[0, 0.31247481764398494]]],
[2, [[0, 0.3478072470007028]]],
[2, [[0, 0.3785134252130592]]],
[2, [[0, 0.3938616312677431]]],
[2, [[0, 0.42869266182685783]]],
[2, [[0, 0.44408271983476183]]],
[2, [[0, 0.4594695941376009]]],
[2, [[0, 0.4902337694168022]]],
[2, [[0, 0.505611059005356]]],
[2, [[0, 0.5246181146997148]]],
[2, [[0, 0.5400397300348875]]],
[2, [[0, 0.6047746406708809]]],
[2, [[0, 0.6202304708442001]]],
[2, [[0, 0.6356832087546725]]],
[2, [[0, 0.6535687967630837]]],
[2, [[0, 0.7000469301249926]]],
[2, [[0, 0.7155335204468358]]],
[2, [[0, 0.7484433368793898]]],
[2, [[0, 0.8415551828545537]]],
[2, [[0, 0.8896745156013701]]],
[2, [[0, 0.9207665061568425]]],
[2, [[0, 0.9518463520584276]]],
[2, [[0, 0.9840324922550672]]],
[2, [[0, 1]]],
[3, [[0, 0.01983903263791853]]],
[3, [[0, 0.029876622283678104]]],
[3, [[0, 0.03990912701215377]]],
[3, [[0, 0.0806437862168895]]],
[3, [[0, 0.09070123866218649]]],
[3, [[0, 0.10101007524615979]]],
[3, [[0, 0.11110799523965204]]],
[3, [[0, 0.12120077057322706]]],
[3, [[0, 0.13128840847448436]]],
[3, [[0, 0.15144829885129574]]],
[3, [[0, 0.16152056375068494]]],
[3, [[0, 0.1818098260284825]]],
[3, [[0, 0.20202009896967427]]],
[3, [[0, 0.21211750416023725]]],
[3, [[0, 0.2727084630640253]]],
[3, [[0, 0.29292802997685985]]],
[3, [[0, 0.3030300711783685]]],
[3, [[0, 0.33330532017010783]]],
[3, [[0, 0.3636029076253441]]],
[3, [[0, 0.37371993253130364]]],
[3, [[0, 0.3838317824361028]]],
[3, [[0, 0.40403999188002426]]],
[3, [[0, 0.41413636769223433]]],
[3, [[0, 0.42422760105585006]]],
[3, [[0, 0.43431369907567324]]],
[3, [[0, 0.4443662699271226]]],
[3, [[0, 0.4544931529993546]]],
[3, [[0, 0.46461484324950236]]],
[3, [[0, 0.4747313502489194]]],
[3, [[0, 0.5050498610823838]]],
[3, [[0, 0.5151457223183467]]],
[3, [[0, 0.5353220266951074]]],
[3, [[0, 0.5454024835564785]]],
[3, [[0, 0.5858535321955943]]],
[3, [[0, 0.5959591836309699]]],
[3, [[0, 0.616155025527601]]],
[3, [[0, 0.6463920624411829]]],
[3, [[0, 0.6666385590106888]]],
[3, [[0, 0.6767540304709369]]],
[3, [[0, 0.6969694650467834]]],
[3, [[0, 0.7474179601261248]]],
[3, [[0, 0.7675614683096057]]],
[3, [[0, 0.7776255556078067]]],
[3, [[0, 0.8282626561208872]]],
[3, [[0, 0.8484256216002729]]],
[3, [[0, 0.8584994213492495]]],
[3, [[0, 0.8685681068775302]]],
[3, [[0, 0.9087918074364576]]],
[3, [[0, 0.9188349938933553]]],
[3, [[0, 0.9288730921857218]]],
[3, [[0, 0.9889949697600137]]],
[3, [[0, 0.99974858647489]]],
[3, [[0, 1]]],
[4, [[0, 0.015052512188419602]]],
[4, [[0, 0.03029426962088413]]],
[4, [[0, 0.06839243749770468]]],
[4, [[0, 0.0836292133608997]]],
[4, [[0, 0.0912470672285808]]],
[4, [[0, 0.09886456497394153]]],
[4, [[0, 0.13734696870310906]]],
[4, [[0, 0.14497397901135212]]],
[4, [[0, 0.16022693354357556]]],
[4, [[0, 0.16785287776755597]]],
[4, [[0, 0.2061009857691912]]],
[4, [[0, 0.2213715432205954]]],
[4, [[0, 0.2290062878232259]]],
[4, [[0, 0.23664067642184725]]],
[4, [[0, 0.31287013784360607]]],
[4, [[0, 0.33579551026422]]],
[4, [[0, 0.3434365859114192]]],
[4, [[0, 0.37399731542592607]]],
[4, [[0, 0.3892755376794567]]],
[4, [[0, 0.3969141134976991]]],
[4, [[0, 0.41219019494203474]]],
[4, [[0, 0.47328026274975304]]],
[4, [[0, 0.48854921795242295]]],
[4, [[0, 0.5267153779648435]]],
[4, [[0, 0.5343475426943275]]],
[4, [[0, 0.5419793517647892]]],
[4, [[0, 0.5496108052135158]]],
[4, [[0, 0.5801330634496403]]],
[4, [[0, 0.5877627392618375]]],
[4, [[0, 0.5953920596237392]]],
[4, [[0, 0.610649634073643]]],
[4, [[0, 0.6487873509046499]]],
[4, [[0, 0.6564138281707615]]],
[4, [[0, 0.6640399500759696]]],
[4, [[0, 0.7174128527857359]]],
[4, [[0, 0.7326590547531385]]],
[4, [[0, 0.7402816225030254]]],
[4, [[0, 0.7555256913985305]]],
[4, [[0, 0.8469602239274668]]],
[4, [[0, 0.8621943289183245]]],
[4, [[0, 0.8698108469953234]]],
[4, [[0, 0.8774270087010998]]],
[4, [[0, 0.9078880903271799]]],
[4, [[0, 0.9155024690411608]]],
[4, [[0, 0.9231164909119645]]],
[4, [[0, 0.9307301558520772]]],
[4, [[0, 0.9769418956440951]]],
[4, [[0, 0.9845647300195252]]],
[4, [[0, 0.992187208890867]]],
[4, [[0, 0.9998093322301707]]],
[4, [[0, 1]]],
[5, [[0, 0.03820574873127833]]],
[5, [[0, 0.045884233047770166]]],
[5, [[0, 0.061240109456200745]]],
[5, [[0, 0.1226490440345489]]],
[5, [[0, 0.13032352057732857]]],
[5, [[0, 0.14567137935938093]]],
[5, [[0, 0.15334476144043718]]],
[5, [[0, 0.19170619534531316]]],
[5, [[0, 0.19937738618962128]]],
[5, [[0, 0.2070482114971963]]],
[5, [[0, 0.2453968513383652]]],
[5, [[0, 0.26073374476033434]]],
[5, [[0, 0.26840164189649224]]],
[5, [[0, 0.3067356268350702]]],
[5, [[0, 0.322066651341349]]],
[5, [[0, 0.32973161244332394]]],
[5, [[0, 0.34506043162274563]]],
[5, [[0, 0.39869970096661816]]],
[5, [[0, 0.4063609788597506]]],
[5, [[0, 0.41402188753199426]]],
[5, [[0, 0.4676378950251564]]],
[5, [[0, 0.47529584340574377]]],
[5, [[0, 0.5518548940788806]]],
[5, [[0, 0.5595087511478618]]],
[5, [[0, 0.56716223496407]]],
[5, [[0, 0.5824680819078778]]],
[5, [[0, 0.6686904305673842]]],
[5, [[0, 0.6917061953453132]]],
[5, [[0, 0.7070482114971963]]],
[5, [[0, 0.7766873397479431]]],
[5, [[0, 0.7923021861323849]]],
[5, [[0, 0.7999923098661218]]],
[5, [[0, 0.8076820697480561]]],
[5, [[0, 0.8614794103478556]]],
[5, [[0, 0.8768764254973112]]],
[5, [[0, 0.8845743850827851]]],
[5, [[0, 0.8922719794659787]]],
[5, [[0, 0.9458689803376649]]],
[5, [[0, 0.9612817605421127]]],
[5, [[0, 0.9689875986837584]]],
[5, [[0, 1]]],
[6, [[0, 0.029749843554381775]]],
[6, [[0, 0.059749843554381774]]],
[6, [[0, 0.06974984355438178]]],
[6, [[0, 0.13974984355438178]]],
[6, [[0, 0.14974984355438178]]],
[6, [[0, 0.15974984355438177]]],
[6, [[0, 0.16974984355438177]]],
[6, [[0, 0.17974984355438178]]],
[6, [[0, 0.21974984355438176]]],
[6, [[0, 0.22974984355438177]]],
[6, [[0, 0.23974984355438178]]],
[6, [[0, 0.2497498435543818]]],
[6, [[0, 0.3197498435543818]]],
[6, [[0, 0.3397498435543818]]],
[6, [[0, 0.3497498435543818]]],
[6, [[0, 0.35974984355438183]]],
[6, [[0, 0.468997487421324]]],
[6, [[0, 0.48899748742132404]]],
[6, [[0, 0.49899748742132405]]],
[6, [[0, 0.518997487421324]]],
[6, [[0, 0.6277371993328519]]],
[6, [[0, 0.6377371993328519]]],
[6, [[0, 0.6477371993328519]]],
[6, [[0, 0.6677371993328519]]],
[6, [[0, 0.7777371993328519]]],
[6, [[0, 0.7877371993328519]]],
[6, [[0, 0.8077371993328519]]],
[6, [[0, 0.9377371993328519]]],
[6, [[0, 0.957737199332852]]],
[6, [[0, 0.977737199332852]]],
[6, [[0, 1]]],
[7, [[0, 0.08434122461529223]]],
[7, [[0, 0.09736319249220814]]],
[7, [[0, 0.12177670236609432]]],
[7, [[0, 0.13398291047706393]]],
[7, [[0, 0.2560176149231152]]],
[7, [[0, 0.26820494288749264]]],
[7, [[0, 0.2803919084377965]]],
[7, [[0, 0.30476475227840155]]],
[7, [[0, 0.42434250381943905]]],
[7, [[0, 0.4364947880419225]]],
[7, [[0, 0.44864670195280104]]],
[7, [[0, 0.4607982453870106]]],
[7, [[0, 0.5822932659066377]]],
[7, [[0, 0.5944407226857343]]],
[7, [[0, 0.6065878068658583]]],
[7, [[0, 0.7298557216513387]]],
[7, [[0, 0.7420139098657976]]],
[7, [[0, 0.7541717301678039]]],
[7, [[0, 0.7663291824206042]]],
[7, [[0, 0.8284299052782537]]],
[7, [[0, 0.8406001691299525]]],
[7, [[0, 0.8649396036740605]]],
[7, [[0, 0.9753340606845997]]],
[7, [[0, 0.9875148632580261]]],
[7, [[0, 0.9996953030971739]]],
[7, [[0, 1]]],
[8, [[0, 0.022133273001338587]]],
[8, [[0, 0.20418547065439502]]],
[8, [[0, 0.22693303640624127]]],
[8, [[0, 0.24968001232099726]]],
[8, [[0, 0.272426398521291]]],
[8, [[0, 0.47595107564942674]]],
[8, [[0, 0.5214835194176608]]],
[8, [[0, 0.5442488489860375]]],
[8, [[0, 0.5670135839399206]]],
[8, [[0, 0.5897777244732638]]],
[8, [[0, 0.749110080215568]]],
[8, [[0, 0.7718694720767475]]],
[8, [[0, 0.7946282711542776]]],
[8, [[0, 0.8173864776180217]]],
[8, [[0, 1]]],
[9, [[0, 0.04431747070173535]]],
[9, [[0, 0.0897720161562808]]],
[9, [[0, 0.18068110706537172]]],
[9, [[0, 0.4988629252471899]]],
[9, [[0, 0.5897720161562808]]],
[9, [[0, 0.6352265616108262]]],
[9, [[0, 0.7261356525199172]]],
[9, [[0, 1]]],
[10, [[0, 1]]],
[11, [[0, 1]]],
]
import puppeteer from "puppeteer-core"
import fs from "fs"
;(async () => {
const browser = await puppeteer.launch({
executablePath: "google-chrome",
headless: true,
args: [
"--js-flags=--expose-gc",
"--no-sandbox",
"--enable-precise-memory-info",
],
})
const page = await browser.newPage()
const done = new Promise((resolve) => {
page.on("console", (msg) => {
if (msg.type() == "debug") {
process.stderr.write(msg.text())
} else if (msg.type() == "info") {
const { filename, title, iterations, results } = JSON.parse(msg.text())
if (title) {
const columns = ["iterations"]
for (const title of results) {
columns.push(`${title}_timeLoc`)
columns.push(`${title}_encodeRAM`)
columns.push(`${title}_packets`)
columns.push(`${title}_size`)
columns.push(`${title}_timeRem`)
columns.push(`${title}_decodeRAM`)
columns.push(`${title}_events`)
}
fs.writeFileSync(
filename,
`# Benchmark: ${title}\n# ${columns.join("\t")}\n`,
)
} else {
const columns = [iterations]
for (const title in results) {
const {
timeLoc,
encodeRAM,
packets,
size,
timeRem,
decodeRAM,
events,
} = results[title]
columns.push(timeLoc)
columns.push(encodeRAM)
columns.push(packets)
columns.push(size)
columns.push(timeRem)
columns.push(decodeRAM)
columns.push(events)
}
fs.appendFileSync(filename, `${columns.join("\t")}\n`)
}
} else if (msg.type() == "log") {
if (msg.text().startsWith("# failure")) {
resolve()
}
process.stdout.write(msg.text() + "\n")
}
})
})
await page.goto("http://localhost:3000/benchmarks.html").catch(console.error)
await done
await browser.close()
})()
import { Selector } from "testcafe"
const host = "127.0.0.1"
const port = process.env.PORT || 3000
fixture`Peer 1`.page`${host}:${port}`
const idAppearTimeout = 1000
const syncTimeout = 5000
const selectorOptions = { timeout: 1000 }
test("Connection id appears", async (t) => {
const userId = async () =>
await Selector("#user-avatar")
.child(1)
.addCustomDOMProperties({ innerHTML: (el) => el.innerHTML })
.with(selectorOptions).innerHTML
await t
.wait(idAppearTimeout)
.expect((await userId()).length)
.gt(0)
})
test("Clicking and dragging on canvas creates a single child element", async (t) => {
const canvas = Selector("#canvas").with(selectorOptions)
await t
.drag(canvas, 10, 10)
.wait(syncTimeout)
.expect(canvas.childElementCount)
// first draw also creates last recognised path placeholder
.eql(2)
})
import { Selector } from "testcafe"
const host = "127.0.0.1"
const port = 3000
fixture`Peer 2`.page`${host}:${port}`
const idAppearTimeout = 1000
const syncTimeout = 15000
const selectorOptions = { timeout: 1000 }
test("Connection id appears", async (t) => {
const userId = async () =>
await Selector("#user-avatar")
.child(1)
.addCustomDOMProperties({ innerHTML: (el) => el.innerHTML })
.with(selectorOptions).innerHTML
await t
.wait(idAppearTimeout)
.expect((await userId()).length)
.gt(0)
})
test("Canvas eventually gets a child element", async (t) => {
const canvas = Selector("#canvas").with(selectorOptions)
await t.expect(canvas.childElementCount).eql(1, { timeout: syncTimeout })
})
import {
computeErasureIntervals,
combineErasureIntervals,
spreadErasureIntervals,
flattenErasureIntervals,
} from "../src/erasure.js"
describe("erasure intervals", () => {
it("computes simple erasure intervals", () => {
const points = [
[0, 0],
[100, 100],
]
const erasureCenter = [50, 50]
const erasureRadius = 25 * Math.SQRT2
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({ 0: [[0.25, 0.75]] })
})
it("computes complex erasure intervals", () => {
const points = [
[0, 0],
[100, 100],
[0, 200],
]
const erasureCenter = [100, 100]
const erasureRadius = 25 * Math.SQRT2
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({ 0: [[0.75, 1]], 1: [[0, 0.25]] })
})
it("computes erasure intervals when point projection is not on the segment", () => {
const points = [
[800, 400],
[800, 450],
[800, 500],
]
const erasureCenter = [800, 432]
const erasureRadius = 20 //* Math.SQRT2
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({ 0: [[0.24, 1]], 1: [[0, 0.04]] })
})
it("computes erasure intervals ???", () => {
const points = [
[100, 100],
[1100, 100],
]
const erasureCenter = [448, 86]
const erasureRadius = 100
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({
0: [[0.2489848496441075, 0.4470151503558925]],
})
})
it("combines distinct intervals", () => {
const i1 = { 0: [[0.1, 0.6]] }
const i2 = { 0: [[0.7, 0.8]] }
const combined = combineErasureIntervals(i1, i2)
const expected = {
0: [
[0.1, 0.6],
[0.7, 0.8],
],
}
expect(combined).toStrictEqual(expected)
})
it("combines overlapping intervals", () => {
const i1 = { 0: [[0.1, 0.6]] }
const i2 = { 0: [[0.5, 0.8]] }
const combined = combineErasureIntervals(i1, i2)
const expected = { 0: [[0.1, 0.8]] }
expect(combined).toStrictEqual(expected)
})
it("combines overlapping inside intervals", () => {
const i1 = { 0: [[0.1, 0.6]] }
const i2 = { 0: [[0.2, 0.3]] }
const combined = combineErasureIntervals(i1, i2)
const expected = { 0: [[0.1, 0.6]] }
expect(combined).toStrictEqual(expected)
})
it("spreads flattened intervals", () => {
const il = [
[0.1, 1.25],
[1.5, 2.0],
[7.5, 7.75],
]
const spread = spreadErasureIntervals(il)
const expected = {
0: [[0.1, 1.0]],
1: [
[0.0, 0.25],
[0.5, 1.0],
],
7: [[0.5, 0.75]],
}
expect(spread).toStrictEqual(expected)
})
it("spreads singulatity intervals", () => {
const il = [
[0.0, 0.0],
[3.5, 3.5],
[99.0, 99.0],
]
const spread = spreadErasureIntervals(il)
const expected = { 0: [[0.0, 0.0]], 3: [[0.5, 0.5]], 99: [[0.0, 0.0]] }
expect(spread).toStrictEqual(expected)
})
it("flattens spread intervals", () => {
const is = {
0: [[0.1, 1.0]],
1: [
[0.0, 0.3],
[0.4, 1.0],
],
7: [[0.35, 0.75]],
}
const flattened = flattenErasureIntervals(is)
const expected = [
[0.1, 1.0],
[1.0, 1.3],
[1.4, 2.0],
[7.35, 7.75],
]
expect(flattened).toStrictEqual(expected)
})
})
const sum = (a, b) => a + b
describe("number adding", () => {
describe("number summation", () => {
it("adds 1 + 2 to equal 3", () => {
expect(sum(1, 2)).toBe(3)
})
})
})
describe("server", () => {
it("should work", () => {})
})
import recognizeFromPoints, { Shapes } from "../src/shapes"
describe("shape recognition", () => {
describe("general", () => {
test("should return a shape description object", () => {
const points = [[0, 0]]
const result = recognizeFromPoints(points)
expect(result).not.toBeNull()
})
})
describe("lines", () => {
test("should recognize a simple horizontal line", () => {
const points = [
[0, 0],
[100, 0],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
expect(result.firstPoint).toStrictEqual([0, 0])
expect(result.lastPoint).toStrictEqual([100, 0])
})
test("should recognize a simple vertical line", () => {
const points = [
[0, 50],
[0, -100],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
expect(result.firstPoint).toStrictEqual([0, 50])
expect(result.lastPoint).toStrictEqual([0, -100])
})
test("should recognize a slightly curve horizontal line", () => {
const points = [
[0, 0],
[30, 5],
[100, 0],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should not recognize a heavily curved horizontal line", () => {
const points = [
[0, 0],
[30, 30],
[100, -4],
]
const result = recognizeFromPoints(points)
expect(result.shape).not.toBe(Shapes.line)
})
test("should recognize a long horizontal line", () => {
const points = [
[48.675496688741724, 197.8062913907285],
[50.66225165562914, 197.8062913907285],
[53.64238410596026, 197.8062913907285],
[57.6158940397351, 197.8062913907285],
[61.58940397350993, 197.8062913907285],
[66.55629139072848, 197.8062913907285],
[71.52317880794702, 197.8062913907285],
[76.49006622516558, 197.8062913907285],
[77.48344370860927, 197.8062913907285],
[82.45033112582782, 197.8062913907285],
[84.43708609271523, 197.8062913907285],
[87.41721854304636, 197.8062913907285],
[88.41059602649007, 197.8062913907285],
[88.41059602649007, 197.8062913907285],
[89.40397350993378, 197.8062913907285],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize a long vertical line", () => {
const points = [
[218.54304635761588, 84.56125827814569],
[218.54304635761588, 86.54801324503312],
[218.54304635761588, 87.54139072847681],
[218.54304635761588, 88.53476821192052],
[218.54304635761588, 90.52152317880794],
[218.54304635761588, 91.51490066225165],
[218.54304635761588, 92.50827814569537],
[218.54304635761588, 93.50165562913908],
[218.54304635761588, 94.49503311258277],
[218.54304635761588, 94.49503311258277],
[218.54304635761588, 95.4884105960265],
[218.54304635761588, 96.4817880794702],
[218.54304635761588, 97.4751655629139],
[218.54304635761588, 98.46854304635761],
[218.54304635761588, 100.45529801324503],
[218.54304635761588, 101.44867549668874],
[218.54304635761588, 103.43543046357617],
[218.54304635761588, 105.42218543046357],
[218.54304635761588, 108.4023178807947],
[218.54304635761588, 110.38907284768212],
[218.54304635761588, 112.37582781456953],
[218.54304635761588, 114.36258278145695],
[218.54304635761588, 116.34933774834438],
[218.54304635761588, 118.33609271523179],
[218.54304635761588, 119.32947019867551],
[218.54304635761588, 120.3228476821192],
[218.54304635761588, 122.30960264900662],
[218.54304635761588, 123.30298013245033],
[218.54304635761588, 124.29635761589404],
[218.54304635761588, 125.28973509933775],
[218.54304635761588, 125.28973509933775],
[218.54304635761588, 125.28973509933775],
[218.54304635761588, 126.28311258278147],
[218.54304635761588, 126.28311258278147],
[218.54304635761588, 127.27649006622516],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize a line at 20 degrees", () => {
const points = [
[34.7682119205298, 268.3360927152318],
[34.7682119205298, 268.3360927152318],
[36.75496688741722, 268.3360927152318],
[37.74834437086093, 268.3360927152318],
[38.741721854304636, 268.3360927152318],
[39.735099337748345, 268.3360927152318],
[40.728476821192054, 268.3360927152318],
[41.721854304635755, 268.3360927152318],
[42.71523178807947, 268.3360927152318],
[43.70860927152318, 268.3360927152318],
[43.70860927152318, 268.3360927152318],
[44.70198675496689, 268.3360927152318],
[44.70198675496689, 268.3360927152318],
[45.6953642384106, 267.3427152317881],
[45.6953642384106, 267.3427152317881],
[46.6887417218543, 267.3427152317881],
[47.682119205298015, 267.3427152317881],
[48.675496688741724, 266.34933774834434],
[48.675496688741724, 266.34933774834434],
[49.66887417218543, 266.34933774834434],
[50.66225165562914, 266.34933774834434],
[51.65562913907284, 265.35596026490066],
[52.64900662251656, 265.35596026490066],
[52.64900662251656, 265.35596026490066],
[53.64238410596026, 265.35596026490066],
[54.63576158940397, 264.362582781457],
[54.63576158940397, 264.362582781457],
[55.629139072847686, 264.362582781457],
[55.629139072847686, 264.362582781457],
[57.6158940397351, 263.36920529801324],
[57.6158940397351, 263.36920529801324],
[58.609271523178805, 263.36920529801324],
[58.609271523178805, 263.36920529801324],
[59.602649006622514, 262.37582781456956],
[60.59602649006623, 262.37582781456956],
[61.58940397350993, 262.37582781456956],
[62.58278145695365, 261.3824503311258],
[63.57615894039735, 261.3824503311258],
[64.56953642384106, 260.3890728476821],
[66.55629139072848, 259.3956953642384],
[67.54966887417218, 259.3956953642384],
[68.54304635761589, 259.3956953642384],
[69.5364238410596, 258.4023178807947],
[69.5364238410596, 258.4023178807947],
[70.52980132450331, 258.4023178807947],
[71.52317880794702, 258.4023178807947],
[71.52317880794702, 257.408940397351],
[71.52317880794702, 257.408940397351],
[72.51655629139073, 257.408940397351],
[72.51655629139073, 257.408940397351],
[72.51655629139073, 257.408940397351],
[73.50993377483444, 257.408940397351],
[73.50993377483444, 257.408940397351],
[73.50993377483444, 257.408940397351],
[74.50331125827815, 257.408940397351],
[76.49006622516558, 257.408940397351],
[77.48344370860927, 257.408940397351],
[79.47019867549669, 256.4155629139073],
[80.4635761589404, 256.4155629139073],
[82.45033112582782, 255.42218543046357],
[83.44370860927151, 255.42218543046357],
[84.43708609271523, 255.42218543046357],
[85.43046357615894, 255.42218543046357],
[85.43046357615894, 254.42880794701986],
[86.42384105960264, 254.42880794701986],
[86.42384105960264, 254.42880794701986],
[87.41721854304636, 254.42880794701986],
[87.41721854304636, 254.42880794701986],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize line 1", () => {
const points = [
[380, 355],
[380, 356],
[381, 355],
[383, 354],
[386, 352],
[391, 349],
[395, 346],
[400, 343],
[405, 339],
[411, 335],
[416, 332],
[425, 325],
[433, 318],
[440, 312],
[447, 306],
[454, 300],
[462, 293],
[471, 283],
[479, 274],
[487, 266],
[498, 255],
[509, 244],
[520, 235],
[531, 226],
[539, 218],
[543, 215],
[550, 208],
[558, 203],
[563, 199],
[567, 196],
[571, 193],
[575, 190],
[577, 189],
[578, 188],
[579, 187],
[580, 187],
[580, 187],
[581, 187],
[581, 186],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize line 2", () => {
const points = [
[648, 509],
[648, 509],
[648, 509],
[646, 509],
[641, 509],
[634, 509],
[628, 509],
[608, 509],
[591, 509],
[571, 509],
[554, 509],
[534, 509],
[513, 505],
[486, 499],
[462, 495],
[454, 494],
[433, 490],
[413, 488],
[396, 485],
[382, 485],
[368, 482],
[360, 482],
[356, 482],
[348, 479],
[341, 479],
[336, 478],
[331, 478],
[329, 478],
[327, 477],
[326, 476],
[326, 476],
[325, 476],
[325, 476],
[325, 476],
[325, 476],
[323, 476],
[322, 476],
[320, 476],
[319, 476],
[318, 476],
[316, 476],
[315, 475],
[315, 475],
[314, 475],
[314, 475],
[313, 475],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize line 3", () => {
const points = [
[204, 590],
[205, 590],
[208, 584],
[219, 574],
[254, 534],
[276, 500],
[305, 456],
[334, 410],
[346, 388],
[376, 336],
[404, 284],
[430, 238],
[454, 197],
[458, 190],
[474, 160],
[483, 142],
[485, 138],
[492, 126],
[495, 121],
[498, 117],
[499, 115],
[500, 114],
[500, 114],
[500, 113],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
})
describe("rectangles", () => {
test("should recognize simple rectangle", () => {
const points = [
[0, 0],
[5, 0],
[10, 0],
[10, 5],
[10, 10],
[5, 10],
[0, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should recognize rectangle with varying points", () => {
const points = [
[0, 0],
[5, 1],
[10, 0],
[10, 6],
[10, 10],
[6, 10],
[0, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should not recognize rectangle with non-rectangular points", () => {
const points = [
[5, 1],
[23, 0],
[10, 54],
[0, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).not.toBe(Shapes.rectangle)
})
test("should not recognize unclosed rectangle", () => {
const points = [
[-10, -10],
[10, -10],
[10, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).not.toBe(Shapes.rectangle)
})
test("should recognize almost-closed rectangle", () => {
const points = [
[0, 0],
[5, 0],
[10, 0],
[10, 5],
[10, 10],
[5, 10],
[0, 8],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should recognize half-closed rectangle", () => {
const points = [
[380, 503],
[379, 503],
[379, 503],
[379, 498],
[379, 491],
[379, 471],
[379, 468],
[379, 457],
[379, 440],
[379, 428],
[379, 412],
[379, 398],
[379, 384],
[379, 373],
[379, 369],
[379, 361],
[379, 354],
[379, 349],
[379, 346],
[379, 344],
[379, 343],
[379, 342],
[379, 341],
[381, 340],
[385, 340],
[389, 340],
[398, 340],
[407, 340],
[418, 340],
[429, 340],
[440, 340],
[451, 340],
[463, 340],
[476, 342],
[488, 343],
[502, 345],
[515, 348],
[520, 349],
[531, 350],
[540, 351],
[549, 352],
[552, 353],
[557, 354],
[558, 355],
[560, 355],
[560, 355],
[560, 355],
[561, 355],
[561, 356],
[561, 356],
[561, 356],
[561, 357],
[561, 360],
[561, 363],
[561, 368],
[561, 377],
[561, 388],
[561, 399],
[561, 406],
[561, 414],
[561, 423],
[561, 432],
[561, 441],
[561, 447],
[561, 452],
[561, 459],
[561, 463],
[561, 468],
[561, 471],
[561, 475],
[561, 478],
[561, 479],
[561, 480],
[561, 480],
[561, 481],
[561, 482],
[561, 482],
[561, 483],
[560, 483],
[560, 483],
[558, 484],
[556, 485],
[551, 487],
[546, 488],
[540, 490],
[533, 492],
[522, 493],
[513, 494],
[502, 496],
[491, 497],
[480, 497],
[466, 499],
[452, 500],
[447, 500],
[436, 500],
[427, 501],
[418, 501],
[412, 503],
[407, 503],
[402, 504],
[400, 504],
[398, 504],
[398, 504],
[397, 504],
[396, 504],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should recognize half-closed rectangle 2", () => {
const points = [
[294, 477],
[293, 477],
[293, 477],
[293, 473],
[293, 469],
[293, 465],
[293, 459],
[293, 455],
[293, 451],
[293, 444],
[293, 437],
[293, 431],
[293, 423],
[293, 414],
[293, 403],
[293, 394],
[293, 391],
[293, 384],
[293, 379],
[293, 376],
[293, 373],
[293, 372],
[293, 371],
[293, 370],
[293, 370],
[293, 369],
[294, 369],
[295, 368],
[298, 368],
[303, 367],
[308, 366],
[317, 364],
[328, 362],
[341, 360],
[358, 357],
[375, 355],
[392, 352],
[412, 350],
[436, 348],
[457, 346],
[480, 344],
[501, 342],
[518, 342],
[535, 340],
[539, 339],
[551, 339],
[559, 338],
[568, 337],
[573, 337],
[576, 337],
[578, 337],
[579, 337],
[579, 337],
[580, 337],
[580, 337],
[581, 337],
[581, 338],
[581, 341],
[581, 346],
[581, 349],
[581, 358],
[579, 366],
[578, 373],
[576, 382],
[575, 386],
[571, 403],
[571, 407],
[568, 420],
[567, 424],
[565, 430],
[562, 437],
[561, 442],
[559, 447],
[558, 450],
[557, 452],
[556, 454],
[556, 456],
[555, 458],
[555, 459],
[554, 460],
[554, 461],
[553, 462],
[553, 462],
[553, 463],
[552, 463],
[552, 463],
[552, 464],
[552, 464],
[551, 464],
[550, 465],
[549, 465],
[544, 466],
[541, 467],
[534, 468],
[527, 469],
[521, 470],
[512, 472],
[509, 472],
[500, 474],
[493, 475],
[485, 476],
[478, 477],
[471, 478],
[465, 479],
[460, 480],
[454, 481],
[450, 482],
[446, 483],
[442, 483],
[439, 483],
[437, 484],
[436, 484],
[435, 484],
[434, 484],
[433, 484],
[433, 484],
[433, 484],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
})
})
Source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -13,36 +13,69 @@
"build": "webpack --config webpack.prod.js",
"build:analyze": "webpack --env.analyze --config webpack.prod.js",
"build:dev": "webpack --config webpack.dev.js",
"build:bench": "webpack --config webpack.bench.js",
"watch": "webpack --watch --config webpack.dev.js",
"start": "node --experimental-modules src/server.js",
"test": "jest --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs",
"test-changed": "jest --only-changed --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs",
"test-coverage": "jest --coverage --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs",
"lint": "jshint .",
"validate": "npm ls"
"test": "jest --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs src/drawing-crdt",
"test-changed": "jest --only-changed --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs src/drawing-crdt",
"test-coverage": "jest --coverage --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs src/drawing-crdt",
"benchmarks": "node --experimental-modules __benchmarks__/puppeteer.js | npx tap-summary --no-progress",
"test-e2e:peer1": "testcafe chrome:headless __e2e_tests__/peer1.e2e.js",
"test-e2e:peer2": "testcafe chrome:headless __e2e_tests__/peer2.e2e.js",
"test-e2e": "run-p test-e2e:*",
"test-e2e-start": "run-s start-bg test-e2e",
"start-bg": "npm run start > /dev/null 2>&1 &",
"format": "prettier --ignore-path .gitignore --check --write '**/*.{html,js,json,md}'",
"format-check": "prettier --ignore-path .gitignore --check '**/*.{html,js,json,md}'",
"lint": "eslint --ignore-path .gitignore '**/*.js'",
"validate": "npm ls",
"plot": "find plot-scripts/ -maxdepth 1 -type f -name '*.p' -exec gnuplot {} \\;"
},
"dependencies": {
"@ungap/event-target": "^0.1.0",
"d3-shape": "^1.3.5",
"@xmpp/client": "^0.9.2",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"liowebrtc": "file:src/liowebrtc",
"signalbuddy": "file:src/signalbuddy",
"uuid": "^3.3.3",
"webrtc-adapter": "^7.3.0",
"y-array": "^10.1.4",
"y-map": "^10.1.3",
"y-memory": "^8.0.9",
"yjs": "file:src/yjs"
"uuid": "^3.3.3"
},
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.6.0",
"@fortawesome/fontawesome-free": "^5.12.0",
"@ungap/event-target": "^0.1.0",
"array-flat-polyfill": "^1.0.1",
"babel-eslint": "^10.0.3",
"chalk": "^3.0.0",
"css-loader": "^3.4.1",
"d3-shape": "^1.3.5",
"drawing-crdt": "file:src/drawing-crdt/pkg",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-testcafe": "^0.2.1",
"fastbitset": "^0.2.8",
"file-loader": "^5.0.2",
"humanhash": "^1.0.4",
"jdenticon": "^2.2.0",
"jest": "^24.9.0",
"liowebrtc": "file:src/liowebrtc",
"npm-run-all": "^4.1.5",
"pako": "^1.0.10",
"prettier": "^1.18.2",
"puppeteer-core": "^2.0.0",
"rtcpeerconnection": "file:src/rtcpeerconnection",
"style-loader": "^1.1.2",
"tap-summary": "^4.0.0",
"testcafe": "^1.5.0",
"webpack": "^4.41.0",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.9",
"webpack-merge": "^4.2.2"
"webpack-merge": "^4.2.2",
"webrtc-adapter": "^7.3.0",
"what-the-pack": "^2.0.3",
"y-array": "^10.1.4",
"y-map": "^10.1.3",
"y-memory": "^8.0.9",
"yjs": "file:src/yjs",
"zora": "^3.1.8"
},
"pre-commit": [
"lint",
......
set xlabel "Iterations"
set ylabel "Time [ms]"
set title "addPath() performance scalability"
set key inside bottom right
set terminal dumb size 120, 30
set autoscale
plot "plots/dot-seq-benchmark.tsv" using 1:($2/(1e6*$1)) with lines title "dot [sequential]"
set terminal pdf
set output "plots/dot-seq-benchmark.pdf"
plot "plots/dot-seq-benchmark.tsv" using 1:($2/(1e6*$1)) with lines title "dot [sequential]"
File added
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<defs >
<font id="Martel" horiz-adv-x="609" ><font-face
font-family="Martel"
units-per-em="1000"
panose-1="0 0 5 0 0 0 0 0 0 0"
ascent="1125"
descent="-562"
alphabetic="0" />
<glyph unicode=" " glyph-name="space" horiz-adv-x="231" />
<glyph unicode="!" glyph-name="exclam" horiz-adv-x="346" d="M193 234H139L110 869H226L193 234ZM207 112T221 96T236 55Q236 26 215 8T167 -11Q144 -11 127 4T109 45Q109 73 130 92T178 112Q207 112 221 96Z" />
<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="615" d="M250 870L228 472H170L148 870H250ZM466 870L444 472H386L364 870H466Z" />
<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="700" d="M73 178V237H184L202 442H84V500H205L218 662H294L281 500H444L458 662H533L520 500H626V442H517L499 237H609L610 178H497L481 0H408L421 178H258L242 0H167L182 178H73ZM440 442H277L260 237H423L440
442Z" />
<glyph unicode="$" glyph-name="dollar" horiz-adv-x="621" d="M126 184L155 86Q182 58 226 47T320 35Q384 35 430 68T477 169Q477 209 453 240T392 296T295 353Q230 387 188 415T118 484T90 576Q90 659 149 706T290 762L292 845H364L361 764Q415 761 454 751T516
727V593H476L441 688Q423 704 391 712T319 720Q286 720 253 707T196 665T172 591Q172 539 215 502T341 422Q413 384 456 356T530 288T561 194Q561 97 498 46T333 -10L329 -113H258L261 -9Q206 -5 156 6T85 30V184H126Z" />
<glyph unicode="%" glyph-name="percent" horiz-adv-x="867" d="M331 -2H259L531 784H604L331 -2ZM285 697T320 647T356 519Q356 462 333 421T272 359T189 338Q146 338 113 362T63 426T45 514Q45 572 68 613T130 676T215 697Q285 697 320 647ZM162 650T140 615T118
518Q118 459 139 422T199 385Q239 385 261 419T283 515Q283 577 262 613T202 650Q162 650 140 615ZM751 439T786 389T821 261Q821 205 799 164T739 102T657 80Q613 80 580 104T529 168T511 256Q511 314 534 355T596 418T681 439Q751 439 786 389ZM629 392T607 357T584
260Q584 202 606 165T666 127Q706 127 728 161T750 257Q750 319 729 355T668 392Q629 392 607 357Z" />
<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="766" d="M427 763T469 717T511 602Q512 533 466 476T351 376Q390 325 454 252T560 141Q570 164 573 188T576 243Q576 287 569 319T556 360H641Q648 334 652 312T657 262Q657 220 646 177T610 99Q671
53 724 47V8Q705 2 683 2Q665 2 655 4Q606 10 554 46Q519 16 466 0T362 -16Q300 -16 239 1T134 61T90 178Q90 245 132 291T249 376Q206 436 183 480T159 575Q159 633 183 675T250 740T345 763Q427 763 469 717ZM297 717T267 690T236 605Q237 545 260 501T321 412Q375
440 405 484T436 586Q436 636 413 676T335 717Q297 717 267 690ZM221 304T198 273T174 189Q174 143 200 109T270 56T367 38Q468 38 513 79Q458 127 390 201T276 338Q221 304 198 273Z" />
<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="399" d="M250 870L228 472H170L148 870H250Z" />
<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="399" d="M252 -129T198 -68T100 101T56 356Q56 484 101 590T202 763T292 849L331 824Q301 804 256 743T176 581T140 356Q140 229 176 127T258 -38T337 -124L291 -152Q252 -129 198 -68Z" />
<glyph unicode=")" glyph-name="parenright" horiz-adv-x="399" d="M97 -105T142 -43T223 120T259 347Q259 472 223 573T141 736T62 823L108 851Q147 828 200 767T298 598T343 344Q343 216 298 110T197 -63T107 -150L67 -125Q97 -105 142 -43Z" />
<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="631" d="M299 602L267 568L121 455L76 519L242 608L287 624L243 640L75 727L120 792L264 681L299 647L287 703L272 872H355L341 703L326 647L370 686L506 791L557 726L388 643L338 624L387 607L557 518L505
456L363 568L327 602L339 550L355 375H272L287 550L299 602Z" />
<glyph unicode="+" glyph-name="plus" horiz-adv-x="618" d="M277 358L197 355H86V425H197L277 419L273 502V616H345V502L340 419L408 425H533V355H408L340 358L345 277V157H273V277L277 358Z" />
<glyph unicode="," glyph-name="comma" horiz-adv-x="338" d="M152 136Q203 124 229 77T256 -19Q257 -97 211 -156T102 -240L81 -213V-201Q117 -189 145 -146T173 -54Q173 -15 152 16T102 50V71L152 136Z" />
<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="605" d="M519 424V358H86V424H519Z" />
<glyph unicode="." glyph-name="period" horiz-adv-x="346" d="M202 110T216 95T230 56Q230 27 210 9T163 -9Q141 -9 125 6T108 44Q108 71 129 90T175 110Q202 110 216 95Z" />
<glyph unicode="/" glyph-name="slash" horiz-adv-x="398" d="M373 781L97 -177H15L290 781H373Z" />
<glyph unicode="0" glyph-name="zero" horiz-adv-x="649" d="M468 611T526 530T585 312Q585 218 551 144T456 28T322 -14Q234 -14 176 29T91 145T64 306Q64 398 101 467T202 574T338 611Q468 611 526 530ZM158 563T158 313Q158 196 201 116T329 35Q492 35 492
300Q492 419 451 491T327 563Q158 563 158 313Z" />
<glyph unicode="1" glyph-name="one" horiz-adv-x="457" d="M202 52V509Q159 498 68 478L59 523Q102 536 174 569T264 617L291 610L289 51L423 40V0H58V40L202 52Z" />
<glyph unicode="2" glyph-name="two" horiz-adv-x="596" d="M206 125T300 228T394 420Q394 476 361 516T261 557Q216 557 168 534T88 483L66 518Q97 556 160 587T291 618Q355 618 399 595T464 532T486 449Q486 350 399 243T176 54L466 74L512 148L551 135L519
0H78L65 36Q206 125 300 228Z" />
<glyph unicode="3" glyph-name="three" horiz-adv-x="524" d="M320 620T374 580T428 460Q428 422 405 382T343 311T264 268Q331 270 377 249T447 190T470 104Q470 37 429 -24T312 -124T142 -162Q100 -162 61 -155V-105Q93 -111 123 -111Q194 -111 252 -84T344
-12T377 87Q377 159 339 196T211 234L180 228L127 217V278Q217 294 276 337T335 435Q336 498 302 528T214 559Q162 559 126 544T57 501L32 542Q61 572 116 596T244 620Q320 620 374 580Z" />
<glyph unicode="4" glyph-name="four" horiz-adv-x="613" d="M80 121T116 185Q188 312 240 411T344 622H373L426 582Q315 388 146 98L115 52L379 66L380 301L460 323V72L566 77V0H460V-184H377L379 0H51L37 43Q80 121 116 185Z" />
<glyph unicode="5" glyph-name="five" horiz-adv-x="528" d="M89 -124T99 -124Q167 -124 231 -92T336 -5T376 116Q376 203 332 245T212 287Q153 287 81 262L63 276L101 604H366L380 613H431L409 529H167L131 312Q191 340 256 340Q314 340 362 316T440 244T469
126Q469 57 422 -12T294 -126T118 -171Q86 -171 66 -165L67 -122Q89 -124 99 -124Z" />
<glyph unicode="6" glyph-name="six" horiz-adv-x="612" d="M502 711Q396 678 319 615T200 475T156 320Q152 198 191 118T324 37Q373 37 407 65T457 135T474 223Q474 313 439 363T350 413Q311 413 289 405T248 382L228 418Q242 437 285 455T373 473Q432 473 474
442T538 362T559 259Q559 193 529 131T442 30T309 -10Q234 -10 178 28T92 132T62 275Q62 394 124 496T283 664T482 749L502 711Z" />
<glyph unicode="7" glyph-name="seven" horiz-adv-x="604" d="M251 20T314 168T472 551L159 541L115 448L69 452L93 612H530L546 593L273 -154L182 -122Q251 20 314 168Z" />
<glyph unicode="8" glyph-name="eight" horiz-adv-x="607" d="M376 758T422 737T494 677T521 589Q521 533 483 474T383 383Q466 335 509 293T553 193Q553 124 518 78T428 9T310 -14Q249 -14 193 7T100 73T63 182Q63 240 104 294T219 384Q163 421 132 465T101 570Q101
631 132 673T214 737T320 758Q376 758 422 737ZM278 714T246 698T193 652T173 586Q173 535 214 496T334 412Q376 423 410 468T446 561Q448 628 411 671T315 714Q278 714 246 698ZM214 335T181 293T147 203Q147 156 168 117T230 56T324 33Q363 33 397 50T453 97T474
164Q474 200 453 228T399 277T312 329Q270 353 263 357Q214 335 181 293Z" />
<glyph unicode="9" glyph-name="nine" horiz-adv-x="616" d="M221 -126T297 -54T413 109T456 282Q464 565 294 565Q245 565 211 537T161 467T144 379Q144 290 179 240T269 189Q301 189 329 198T371 220L391 184Q371 161 331 145T246 129Q187 129 145 160T82 240T60
343Q60 409 90 471T177 572T310 612Q385 612 439 574T520 470T548 327Q548 200 488 86T332 -103T135 -198L118 -158Q221 -126 297 -54Z" />
<glyph unicode=":" glyph-name="colon" horiz-adv-x="346" d="M203 504T216 489T230 451Q230 422 210 404T164 386Q141 386 125 400T109 438Q109 467 130 486T177 505Q203 504 216 489ZM203 110T216 95T230 56Q230 27 210 9T164 -9Q141 -9 125 5T109 43Q109 71
129 90T175 110Q203 110 216 95Z" />
<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="346" d="M201 505T215 490T229 452Q229 423 209 405T162 386Q140 386 124 401T107 440Q107 467 128 486T174 505Q201 505 215 490ZM215 124T242 77T269 -19Q269 -97 223 -156T114 -240L93 -213V-201Q129
-189 157 -146T185 -54Q185 -15 164 16T114 50V71L164 136Q215 124 242 77Z" />
<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="645" d="M538 642V559L144 367L538 176V93L65 337V397L538 642Z" />
<glyph unicode="=" glyph-name="equal" horiz-adv-x="605" d="M519 518V451H86V518H519ZM519 324V257H86V324H519Z" />
<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="645" d="M501 367L107 558V642L580 398V337L107 92V176L501 367Z" />
<glyph unicode="?" glyph-name="question" horiz-adv-x="494" d="M257 896T324 854T422 749T453 617Q453 588 450 573T435 544T397 516Q379 505 329 481Q276 455 252 438T227 396Q227 368 236 339T264 267L276 239H227Q200 260 171 316T141 408Q142 437 177 459T281
511Q338 536 353 545Q371 555 378 563T384 582Q385 644 353 698T264 784T144 816Q123 816 99 813T62 804V874Q69 883 97 889T155 896Q257 896 324 854ZM300 110T314 95T328 56Q328 27 308 9T261 -9Q239 -9 223 6T206 44Q206 71 227 90T273 110Q300 110 314 95Z"
/>
<glyph unicode="@" glyph-name="at" horiz-adv-x="1043" d="M689 702T765 665T886 559T930 396Q930 271 889 186T787 60T665 19Q640 19 626 43T611 119Q611 166 625 244Q589 149 535 81T426 12Q395 12 366 36T319 105T301 210Q301 276 328 347T407 467T524 515Q555
515 593 508T654 488Q659 501 663 506T676 510L730 493Q710 410 687 192Q684 164 684 139Q684 109 688 91T701 73Q733 73 772 112T838 217T866 362Q866 461 824 523T712 614T558 643Q457 643 371 595T234 453T182 232Q182 108 229 29T356 -87T535 -123Q579 -123
630 -113T709 -89L726 -128Q694 -153 630 -169T501 -185Q389 -185 301 -138T164 -2T114 202Q114 350 180 464T356 640T594 702Q689 702 765 665ZM448 448T411 390T373 244Q373 187 389 138T444 88Q476 88 518 143T594 277T636 415Q621 429 587 438T525 448Q448
448 411 390Z" />
<glyph unicode="A" glyph-name="A" horiz-adv-x="730" d="M71 53L328 759H404L651 53L730 40V0H477V40L558 53L503 215H203L148 53L230 40V0H1V40L71 53ZM397 527L355 663L221 269H484L397 527Z" />
<glyph unicode="B" glyph-name="B" horiz-adv-x="693" d="M136 53V705L54 713V758H176L221 761Q313 766 358 765Q474 764 536 719T599 594Q599 523 563 473T454 407Q508 406 552 383T622 319T648 223Q648 118 568 56T332 -7Q295 -7 202 -2Q182 0 174 0H52V40L136
53ZM243 423T295 423H350Q426 423 470 462T514 578Q514 646 466 681T340 716Q313 716 277 715T225 709L224 426Q243 423 295 423ZM238 54T278 49T352 43Q442 43 498 81T554 206Q554 290 506 329T365 368L291 369Q251 369 224 366V61Q238 54 278 49Z" />
<glyph unicode="C" glyph-name="C" horiz-adv-x="689" d="M487 765T527 758T612 737L640 730L632 590H584L560 674Q529 694 495 703T409 712Q336 712 274 673T174 559T136 383Q136 288 168 209T262 82T412 35Q529 35 575 91L601 169H641V31Q628 30 610 25T585
18Q540 3 497 -5T392 -14Q292 -14 212 33T87 164T42 355Q42 476 96 569T240 714T437 765Q487 765 527 758Z" />
<glyph unicode="D" glyph-name="D" horiz-adv-x="778" d="M136 53V705L54 713V758H180Q227 758 289 762Q301 763 325 764T370 765Q552 765 643 669T734 399Q734 276 685 185T550 44T355 -5Q334 -5 280 -3Q233 0 182 0H52V40L136 53ZM286 45T348 45Q429 45 495
81T601 191T641 376Q641 542 565 628T351 714Q278 714 225 704V59Q286 45 348 45Z" />
<glyph unicode="E" glyph-name="E" horiz-adv-x="645" d="M134 53V705L52 713V758H560L574 618H523L500 704L222 710V422L409 426L423 503H470V293H423L409 371L222 374V50L521 58L569 163L613 152L591 0H50V40L134 53Z" />
<glyph unicode="F" glyph-name="F" horiz-adv-x="605" d="M137 53V705L55 713V758H578L587 618L542 617L519 702L226 708V412L443 416L461 489H507V283H456L443 358L226 362V53L371 43V0H51V40L137 53Z" />
<glyph unicode="G" glyph-name="G" horiz-adv-x="750" d="M502 765T549 757T623 742T657 733L649 594H601L577 677Q565 690 520 701T407 712Q334 711 272 672T172 558T135 383Q135 291 165 212T255 84T403 35Q454 35 499 46T570 85V293L447 301V346H721V301L659
292V39Q646 38 623 32T592 23Q525 5 479 -4T382 -14Q283 -14 206 33T85 165T42 355Q42 477 95 570T237 714T434 765Q502 765 549 757Z" />
<glyph unicode="H" glyph-name="H" horiz-adv-x="834" d="M135 53V705L54 713V758H303V713L224 705V395H608V705L528 713V758H775V713L697 705V53L782 40V0H524V40L608 53V341H224V53L307 40V0H52V40L135 53Z" />
<glyph unicode="I" glyph-name="I" horiz-adv-x="374" d="M141 53V705L57 713V758H314V713L230 705V53L318 40V0H55V40L141 53Z" />
<glyph unicode="J" glyph-name="J" horiz-adv-x="353" d="M310 713L229 705V257V191Q229 75 219 21Q207 -38 171 -83T84 -155T-19 -190L-42 -157Q16 -139 57 -107T117 -29Q131 11 135 61T140 196V705L57 713V758H310V713Z" />
<glyph unicode="K" glyph-name="K" horiz-adv-x="694" d="M132 53V705L53 713V758H301V713L221 705V361L510 705L433 713V758H671V713L592 705L358 438Q388 405 482 254Q542 158 577 108T625 56L703 40V0H558Q546 0 520 40T441 171Q394 254 362 305T307 378L221
281V52L310 40V0H51V40L132 53Z" />
<glyph unicode="L" glyph-name="L" horiz-adv-x="607" d="M136 53V701L54 708V758H314V708L225 701L224 51L495 62L553 180L594 160L562 0H52V40L136 53Z" />
<glyph unicode="M" glyph-name="M" horiz-adv-x="966" d="M124 53L172 701L76 709V758H296L477 215L499 143L521 216L693 758H896V709L803 701L859 53L947 40V0H690V40L776 53L734 523L729 692L707 607L501 -7H470L278 539Q261 587 225 695L220 520L195 53L292
40V0H38V40L124 53Z" />
<glyph unicode="N" glyph-name="N" horiz-adv-x="812" d="M775 709L693 701V0H598L269 518L200 655V53L297 40V0H51V40L135 53V701L54 708V758H224L558 215L628 82V700L546 709V758H775V709Z" />
<glyph unicode="O" glyph-name="O" horiz-adv-x="759" d="M498 765T569 718T679 587T718 397Q718 270 672 177T547 35T372 -14Q274 -14 200 34T85 166T44 358Q44 486 94 580T229 721Q309 765 403 765Q498 765 569 718ZM312 716T258 675T173 555T143 368Q143 278
172 202T257 81T387 35Q459 35 511 77T592 198T620 385Q620 474 593 550T512 671T385 716Q312 716 258 675Z" />
<glyph unicode="P" glyph-name="P" horiz-adv-x="634" d="M136 53V701L53 708V758H180Q203 758 241 761Q311 765 354 765Q477 765 543 714T610 538Q610 463 571 407T469 321T334 291H286Q240 291 225 288V53L357 40V0H42V40L136 53ZM261 337T325 337Q413 338 466
382T519 537Q519 625 471 669T331 714Q261 714 224 705V343Q261 337 325 337Z" />
<glyph unicode="Q" glyph-name="Q" horiz-adv-x="759" d="M497 765T568 718T678 587T717 397Q717 287 682 202T584 64T442 -6Q462 -57 514 -77T630 -98Q679 -98 703 -81L711 -138Q702 -153 672 -163T601 -174Q522 -174 468 -138T391 -14H378Q278 -14 202 34T84
167T44 358Q44 482 91 573T217 714Q301 765 402 765Q497 765 568 718ZM312 716T257 675T172 555T142 368Q142 278 171 202T256 81T386 35Q458 35 510 77T591 198T619 385Q619 474 592 550T511 671T384 716Q312 716 257 675Z" />
<glyph unicode="R" glyph-name="R" horiz-adv-x="694" d="M452 765T518 720T585 562Q585 486 540 429T419 348L427 344Q462 329 494 283T567 162Q598 107 614 82T645 53L700 40V0H563Q550 0 534 26T488 115Q438 219 398 277T313 331Q286 330 225 330V53L328 40V0H42V40L136
53V701L54 708V758H180Q200 758 230 761Q286 765 329 765Q452 765 518 720ZM308 715T290 715Q243 713 225 708V382Q252 379 320 379Q403 379 448 422T493 552Q493 634 452 677T316 716Q308 715 290 715Z" />
<glyph unicode="S" glyph-name="S" horiz-adv-x="601" d="M231 -11T165 2T75 30V184H116L145 86Q172 58 216 47T310 35Q374 35 420 68T467 169Q467 209 443 240T382 296T285 353Q220 387 178 415T108 484T80 576Q80 640 116 682T207 745T316 765Q382 765 431 754T506
727V593H466L431 688Q413 704 381 712T310 720Q277 720 243 707T186 665T162 591Q162 539 205 502T332 422Q404 384 447 356T520 288T551 194Q551 91 480 40T299 -11Q231 -11 165 2Z" />
<glyph unicode="T" glyph-name="T" horiz-adv-x="622" d="M275 53V708L89 698L57 610L15 609L27 758H609L608 607L571 603L547 699L364 708V53L493 40V0H152V40L275 53Z" />
<glyph unicode="U" glyph-name="U" horiz-adv-x="770" d="M749 713L673 704V331Q673 156 598 71T390 -14Q231 -14 162 68T92 320L93 705L16 713V758H268V713L182 704V317Q182 162 240 98T406 34Q604 34 604 331V704L517 713V758H749V713Z" />
<glyph unicode="V" glyph-name="V" horiz-adv-x="697" d="M2 713V758H261V713L170 705L343 192L375 89L403 193L555 705L474 713V758H695V713L632 706L395 -10H327L68 706L2 713Z" />
<glyph unicode="W" glyph-name="W" horiz-adv-x="1028" d="M2 713V758H259V713L165 705L324 189L353 95L382 184L490 617L463 705L383 713V758H645V713L558 705L695 180L718 89L743 197L875 705L789 713V758H1023V713L949 705L741 -10H667L523 498L383 -10H307L67
705L2 713Z" />
<glyph unicode="X" glyph-name="X" horiz-adv-x="694" d="M94 53L305 382L100 705L25 713V758H295V713L206 705L363 436L520 705L443 713V758H674V713L603 706L396 399L611 53L694 40V0H416V40L509 53L335 337L177 53L271 40V0H7V40L94 53Z" />
<glyph unicode="Y" glyph-name="Y" horiz-adv-x="661" d="M299 54V311L63 705L6 713V758H242V713L165 705L325 410L350 360L372 411L517 704L434 713V758H655V713L590 701L388 310V54L493 40V0H199V40L299 54Z" />
<glyph unicode="Z" glyph-name="Z" horiz-adv-x="660" d="M625 719L203 117L150 51L527 68L588 192L634 181L599 0H59L46 41L472 645L525 709L167 698L123 588L75 596L90 758H611L625 719Z" />
<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="419" d="M99 776T106 800T132 834T188 844Q222 844 248 840T316 824V785H173V-31L316 -32V-71Q277 -81 249 -86T185 -91Q149 -91 131 -81T106 -47T99 24V727Q99 776 106 800Z" />
<glyph unicode="\" glyph-name="backslash" horiz-adv-x="398" d="M373 -177H290L15 781H97L373 -177Z" />
<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="420" d="M247 785H104V824Q145 834 172 839T231 844Q269 844 288 835T314 801T321 727V24Q321 -23 314 -47T289 -81T234 -91Q200 -91 171 -86T104 -71V-32L247 -31V785Z" />
<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="641" d="M365 748L555 275H475L316 655L158 275H86L275 748H365Z" />
<glyph unicode="_" glyph-name="underscore" horiz-adv-x="691" d="M701 -161H-10V-94H701V-161Z" />
<glyph unicode="`" glyph-name="grave" horiz-adv-x="432" d="M237 683T182 723T101 801L167 868L314 680L289 655Q237 683 182 723Z" />
<glyph unicode="a" glyph-name="a" horiz-adv-x="560" d="M364 556T416 516T468 377L467 40H529V9Q512 0 488 -5T439 -11Q406 -11 396 3T385 55Q329 -12 218 -12Q173 -12 136 7T78 61T57 139Q57 206 109 247T235 307T380 325V367Q380 441 345 468T243 496Q211
496 169 485T96 456L79 498Q112 525 171 540T275 556Q364 556 416 516ZM292 278T219 249T145 154Q145 46 265 46Q298 46 330 60T379 91L380 278Q292 278 219 249Z" />
<glyph unicode="b" glyph-name="b" horiz-adv-x="618" d="M18 736V777L169 793L188 785V500Q223 528 262 545T352 563Q410 563 459 536T538 448T568 295Q568 211 533 140T435 28T292 -14Q228 -14 170 -1T101 24V721L18 736ZM195 54T226 45T302 35Q349 35 390 63T456
147T481 286Q481 394 433 447T315 500Q276 500 243 486T188 452V73Q195 54 226 45Z" />
<glyph unicode="c" glyph-name="c" horiz-adv-x="512" d="M372 561T406 555T459 537V423H416L392 485Q377 497 355 502T309 508Q261 508 222 484T160 413T136 297Q136 214 162 159T230 78T318 52Q355 52 397 61T460 84L476 43Q448 18 396 4T287 -10Q213 -10 160
25T79 123T51 265Q51 355 88 422T190 525T333 561Q372 561 406 555Z" />
<glyph unicode="d" glyph-name="d" horiz-adv-x="621" d="M517 782V40H583V5Q568 -3 539 -8T484 -14Q454 -14 444 -2T433 46Q411 24 368 6T264 -12Q206 -12 158 18T80 111T50 264Q50 349 87 417T189 524T332 563Q382 563 429 548V718L327 734V774L496 793L517
782ZM276 514T234 490T165 414T137 280Q137 205 159 153T218 76T299 50Q336 50 371 61T429 95V475Q414 491 390 502T321 514Q276 514 234 490Z" />
<glyph unicode="e" glyph-name="e" horiz-adv-x="560" d="M408 565T459 507T510 347Q510 312 504 270H137Q137 168 186 109T328 50Q368 50 412 59T478 82L495 43Q467 18 410 3T294 -12Q178 -12 114 64T49 271Q49 354 83 419T179 523T319 563Q408 565 459 507ZM233
514T190 467T138 324H417Q418 334 418 354Q418 463 362 497Q336 514 296 514Q233 514 190 467Z" />
<glyph unicode="f" glyph-name="f" horiz-adv-x="410" d="M154 49V481H45V517L154 543V587Q154 656 185 710T262 794T352 824Q407 824 423 815V738Q414 744 390 749T335 754Q305 754 285 745T254 707T242 624V543H392V481H242V48L355 39V0H55V39L154 49Z" />
<glyph unicode="g" glyph-name="g" horiz-adv-x="588" d="M381 567T434 529Q458 553 516 553H575V484H474Q500 443 500 386Q500 324 470 279T390 210T281 186Q223 186 179 204Q173 190 172 175T170 137Q170 108 185 94T236 77Q252 75 303 75H360Q445 75 488 52T543
-2T555 -75Q555 -131 524 -172T434 -236T298 -259Q196 -259 129 -226T61 -115Q61 -73 87 -37T144 19Q119 29 107 42T93 69T91 103Q91 124 107 159T151 218Q69 266 69 368Q69 433 102 478T187 545T299 567Q381 567 434 529ZM225 519T191 487T157 385Q157 318 191
277T295 235Q355 235 384 269T413 373Q413 448 380 483T281 519Q225 519 191 487ZM146 -26T146 -86Q146 -150 193 -179T310 -208Q391 -208 437 -180T484 -97Q484 -63 473 -42T429 -10T338 2L269 3Q212 3 180 10Q146 -26 146 -86Z" />
<glyph unicode="h" glyph-name="h" horiz-adv-x="686" d="M134 49V718L51 733V774L200 793L222 782L221 561L219 482Q261 515 317 536T414 557Q480 557 515 531T565 448T578 296V49L651 39V0H408V39L490 49V260Q490 347 482 394T449 466T371 490Q335 490 295 479T222
445V49L294 39V0H61V39L134 49Z" />
<glyph unicode="i" glyph-name="i" horiz-adv-x="334" d="M204 801T218 785T233 744Q233 715 212 696T164 677Q141 677 124 692T106 733Q106 761 127 781T175 801Q204 801 218 785ZM225 545V49L305 39V0H59V39L141 49V458L72 490V519L204 555L225 545Z" />
<glyph unicode="j" glyph-name="j" horiz-adv-x="317" d="M200 809T214 794T229 752Q229 722 209 704T161 686Q137 686 120 701T103 742Q103 770 124 789T171 809Q200 809 214 794ZM224 545V-8Q224 -122 178 -186T50 -251Q36 -251 18 -249T-4 -244V-182Q9 -187
49 -187Q81 -187 100 -170T129 -113T138 -1V458L69 491V519L203 555L224 545Z" />
<glyph unicode="k" glyph-name="k" horiz-adv-x="623" d="M135 49V718L48 733V774L204 793L223 782V354L220 268L446 496L373 503V551H599V503L525 495L354 327L564 49L632 40V0H399V40L468 50L301 283L220 204L223 103V49L295 39V0H56V39L135 49Z" />
<glyph unicode="l" glyph-name="l" horiz-adv-x="341" d="M139 49V718L61 733V774L208 793L227 782V49L313 39V0H57V39L139 49Z" />
<glyph unicode="m" glyph-name="m" horiz-adv-x="1012" d="M135 49V456L64 490V520L191 555L211 545V477Q247 507 304 531T409 556Q462 558 494 542T545 487Q580 515 641 535T751 556Q808 556 841 532T889 458T904 323V49L986 39V0H749V39L816 49V308Q816 374
808 412T775 470T705 490Q636 490 559 447Q570 404 570 328V49L646 39V0H409V39L483 49V315Q483 380 475 417T443 472T374 490Q343 490 303 477T223 437V49L297 39V0H67V39L135 49Z" />
<glyph unicode="n" glyph-name="n" horiz-adv-x="683" d="M135 49V456L64 490V519L190 555L211 545V477Q248 508 310 532T421 557Q505 557 540 503T575 326V49L653 39V0H413V39L487 49V312Q487 376 478 414T445 471T377 490Q342 490 301 477T223 437V49L297 39V0H67V39L135
49Z" />
<glyph unicode="o" glyph-name="o" horiz-adv-x="602" d="M393 563T447 526T528 424T554 281Q553 196 519 129T427 25T298 -12Q213 -12 158 27T76 133T50 279Q50 364 86 429T183 528T313 563Q393 563 447 526ZM221 514T182 455T143 289Q143 182 184 111T306 39Q386
39 423 99T460 276Q460 383 420 448T299 514Q221 514 182 455Z" />
<glyph unicode="p" glyph-name="p" horiz-adv-x="646" d="M441 563T489 535T567 446T597 291Q597 208 562 139T463 30T319 -11Q269 -11 215 6L218 -126V-201L319 -210V-250H60V-210L130 -200V456L61 490V519L186 555L207 545V489Q233 518 280 540T382 563Q441
563 489 535ZM304 504T273 489T217 451L218 81Q230 61 258 49T329 37Q377 37 418 63T484 145T510 285Q510 397 463 450T346 504Q304 504 273 489Z" />
<glyph unicode="q" glyph-name="q" horiz-adv-x="607" d="M367 563T396 558T445 543L498 556L517 547V-197L584 -206V-250H326V-206L429 -196V-23L432 51Q361 -14 263 -14Q216 -14 168 8T85 93T49 266Q49 349 86 417T188 524T337 563Q367 563 396 558ZM271 514T229
487T161 406T136 274Q136 166 179 108T295 49Q341 49 375 63T429 97V475Q397 514 323 514Q271 514 229 487Z" />
<glyph unicode="r" glyph-name="r" horiz-adv-x="451" d="M135 49V456L64 490V519L190 555L212 545V472Q232 501 285 528T383 556Q419 556 431 553V460Q409 468 393 471T348 475Q279 475 223 438V50L365 39V0H54V39L135 49Z" />
<glyph unicode="s" glyph-name="s" horiz-adv-x="512" d="M121 140L140 79Q165 57 193 48T260 38Q314 38 346 62T379 132Q379 156 365 173T331 203T272 232L237 248Q167 280 126 318T85 413Q85 462 114 496T190 546T284 563Q328 563 368 553T432 528V425H393L373
482Q340 517 284 517Q231 517 200 495T169 429Q169 395 196 374T280 325L305 313Q313 309 362 287T437 230T463 152Q463 73 404 31T250 -12Q205 -12 161 -2T80 30V140H121Z" />
<glyph unicode="t" glyph-name="t" horiz-adv-x="422" d="M215 546H385V489H215V228Q215 150 219 115T235 66T275 52Q301 52 332 60T378 78L392 38Q372 16 326 2T241 -12Q185 -12 157 21T129 128V489H54V523L76 528Q108 535 120 540T141 564Q147 579 157 619T170
677H215V546Z" />
<glyph unicode="u" glyph-name="u" horiz-adv-x="631" d="M53 496V531L173 556L190 547V211Q190 152 198 119T230 69T298 52Q340 52 381 72T441 110V478L380 492V530L514 556L527 548V40H587V5Q572 -3 546 -9T499 -15Q450 -15 450 25V66Q406 29 359 10T264 -10Q192
-10 157 18T113 94T103 224V483L53 496Z" />
<glyph unicode="v" glyph-name="v" horiz-adv-x="581" d="M1 498V546H214V498L150 489L263 214L309 77L353 208L452 489L387 498V546H581V498L526 488L328 -12H266L50 490L1 498Z" />
<glyph unicode="w" glyph-name="w" horiz-adv-x="827" d="M1 498V546H213V498L142 488L240 171L264 75L285 171L383 546H461L562 171L587 74L612 168L694 489L631 498V546H826V498L765 488L619 -12H538L430 366L416 441L397 366L295 -12H220L53 490L1 498Z" />
<glyph unicode="x" glyph-name="x" horiz-adv-x="571" d="M78 49L242 265L72 490L2 498V546H245V498L174 490L272 346L297 308L319 343L419 491L349 498V546H564V498L490 489L328 276L502 49L569 39V0H329V39L399 49L272 229L251 192L151 49L240 39V0H5V39L78 49Z" />
<glyph unicode="y" glyph-name="y" horiz-adv-x="563" d="M90 -177T113 -177Q157 -177 200 -139T273 -4H254L55 490L1 498V546H226V498L153 489L270 158L306 51L336 155L438 490L360 498V546H569V498L513 491L491 426Q423 226 385 117T336 -18Q296 -121 255 -176T157
-246Q141 -251 113 -251Q102 -251 90 -249T73 -244V-171Q90 -177 113 -177Z" />
<glyph unicode="z" glyph-name="z" horiz-adv-x="527" d="M493 508L199 108L150 45L422 56L452 140L499 139L491 0H58L43 38L79 86L347 455L388 503L137 496L109 405H66V546H480L493 508Z" />
<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="503" d="M163 300T125 320T41 351V408Q90 420 126 437T163 472V727Q163 776 170 800T197 834T253 844Q285 844 309 840T354 831T381 824V785H238V474Q238 451 225 436T191 410T128 379Q183 354 210 333T238
281V-32H381V-72Q374 -74 349 -80T300 -89T251 -92Q215 -92 197 -82T171 -47T163 26V282Q163 300 125 320Z" />
<glyph unicode="|" glyph-name="bar" horiz-adv-x="428" d="M254 869V-91H175V869H254Z" />
<glyph unicode="}" glyph-name="braceright" horiz-adv-x="470" d="M232 281Q232 312 260 333T341 379Q296 401 277 411T245 437T232 474V785H90L89 824Q135 835 159 839T217 844Q255 844 274 835T300 801T307 727V472Q307 454 343 437T429 408V351Q385 340 346
320T307 282V26Q307 -23 300 -47T275 -82T219 -92Q187 -92 163 -88T117 -79T89 -72L90 -32H232V281Z" />
<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="615" d="M97 462T142 488T238 515Q265 515 285 504T332 469Q353 450 368 441T402 431Q454 431 507 484L541 443Q518 405 473 378T377 351Q349 351 329 363T282 398Q261 417 246 426T212 436Q181 436 156
421T107 382L74 423Q97 462 142 488Z" />
<glyph unicode="&#xa0;" glyph-name="uni00A0" horiz-adv-x="231" />
<glyph unicode="&#xa1;" glyph-name="exclamdown" horiz-adv-x="349" d="M208 700T222 685T236 646Q236 617 216 599T167 581Q145 583 130 597T114 636Q114 663 135 681T181 700Q208 700 222 685ZM226 -198H110L143 454H197L226 -198Z" />
<glyph unicode="&#xa2;" glyph-name="cent" horiz-adv-x="546" d="M160 9T111 82T61 265Q61 345 91 408T175 510T296 558L304 665H360L354 561Q389 560 421 554T469 537V423H426L402 485Q387 497 364 502T319 508Q271 508 232 484T170 413T146 298Q146 215 172
160T240 78T328 52Q365 52 406 61T470 84L486 43Q460 20 412 6T309 -10L303 -105H246L251 -6Q160 9 111 82Z" />
<glyph unicode="&#xa3;" glyph-name="sterling" horiz-adv-x="705" d="M490 765T521 756T580 724L536 641Q531 650 514 666T468 695T406 708Q293 708 293 483V427H457V361H294V336Q294 240 280 189T232 84Q259 83 315 61Q337 53 360 46T404 35Q428 31 452 31Q517
31 545 63T574 142Q574 164 565 184T536 219L577 298Q616 282 637 244T659 155Q659 90 618 39T473 -17Q462 -18 441 -18Q385 -18 341 -6T250 27Q224 38 210 42T186 45Q166 18 141 2T88 -15Q70 -15 56 -6T42 27Q42 44 59 61T108 89T178 98Q191 119 198 159T206 237V361H115V427H206V481Q206
557 236 621T322 725T447 764Q490 765 521 756Z" />
<glyph unicode="&#xa4;" glyph-name="currency" horiz-adv-x="702" d="M238 487Q289 522 347 522Q412 522 463 482L552 570L591 530L501 440Q533 392 533 337Q533 274 497 227L585 139L545 100L457 188Q408 152 347 152Q289 152 240 185L152 97L113 136L199 223Q161
272 161 337Q161 400 198 448L111 536L150 576L238 487ZM315 463T287 446T243 400T227 337Q227 303 243 274T287 228T347 211Q397 211 432 248T468 337Q468 389 433 426T347 463Q315 463 287 446Z" />
<glyph unicode="&#xa5;" glyph-name="yen" horiz-adv-x="688" d="M299 53V189H90V247H299V363H90V421H269L80 705L15 713V758H256V713L181 705L324 477L350 421H362L393 481L524 705L450 713V758H669V713L600 700L421 421H592V363H389V247H592V189H389V54L490
40V0H202V40L299 53Z" />
<glyph unicode="&#xa6;" glyph-name="brokenbar" horiz-adv-x="428" d="M254 869V433H175V869H254ZM254 346V-91H175V346H254Z" />
<glyph unicode="&#xa7;" glyph-name="section" horiz-adv-x="648" d="M404 863T453 852T529 825V691H490L454 786Q441 801 408 808T333 816Q301 816 267 802T208 757T183 679Q183 643 206 616T264 569T358 522Q426 492 467 468T537 407T566 318Q566 275 554 233T520
163Q559 118 559 62Q559 -37 489 -85T308 -133Q240 -133 175 -120T85 -91V63H125L155 -32Q185 -65 224 -74T319 -84Q382 -84 428 -56T474 35Q474 74 450 103T392 152T296 201Q230 231 189 255T120 317T92 405Q92 445 107 485T147 552Q100 601 100 662Q100 727 137
772T230 840T338 863Q404 863 453 852ZM176 471T176 422Q176 385 199 358T258 311T351 265Q380 252 411 237T464 207Q482 248 482 291Q482 330 459 359T401 408T305 457Q229 493 199 512Q176 471 176 422Z" />
<glyph unicode="&#xa8;" glyph-name="dieresis" horiz-adv-x="509" d="M111 682T95 696T78 736Q78 763 98 782T145 801Q174 801 188 786T203 746Q203 717 183 700T135 682Q111 682 95 696ZM337 682T321 696T304 736Q304 763 325 782T371 801Q400 801 414 786T429
746Q429 717 409 700T361 682Q337 682 321 696Z" />
<glyph unicode="&#xa9;" glyph-name="copyright" horiz-adv-x="996" d="M609 938T703 882T854 730T910 522Q910 411 854 316T703 164T498 107Q387 107 293 163T142 315T86 522Q86 634 142 729T292 881T498 938Q609 938 703 882ZM402 883T321 834T193 702T145 522Q145
426 192 343T321 211T498 162Q594 162 674 211T802 343T850 522Q850 619 803 702T675 834T498 883Q402 883 321 834ZM511 769T532 769Q573 769 608 763T656 740V639H603L580 702Q566 710 550 714T496 718Q437 718 403 668T368 537Q368 482 389 439T448 372T535
347Q566 347 600 357T647 379L661 345Q639 320 596 303T502 285Q397 285 341 349T284 518Q284 584 316 640T400 730T503 768Q511 769 532 769Z" />
<glyph unicode="&#xaa;" glyph-name="ordfeminine" horiz-adv-x="473" d="M282 778T316 767T371 725T391 632L390 406H433V372Q419 366 395 361T355 356Q329 356 322 367T313 410Q254 359 162 359Q137 359 109 372T61 411T41 476Q41 556 115 585T311 614V628Q311
669 298 689T263 715T203 722Q165 722 131 714T79 691L62 731Q84 750 133 764T229 778Q282 778 316 767ZM226 572T176 554T126 490Q126 455 145 434T196 413Q228 413 260 423T311 449V572Q226 572 176 554Z" />
<glyph unicode="&#xab;" glyph-name="guillemotleft" horiz-adv-x="928" d="M437 526L461 486L204 300L461 121L437 79L120 261V336L437 526ZM784 526L807 486L551 300L807 121L784 79L466 261V336L784 526Z" />
<glyph unicode="&#xac;" glyph-name="logicalnot" horiz-adv-x="601" d="M515 424V164H440V365H86V424H515Z" />
<glyph unicode="&#xad;" glyph-name="uni00AD" horiz-adv-x="601" d="M519 424V358H86V424H519Z" />
<glyph unicode="&#xae;" glyph-name="registered" horiz-adv-x="996" d="M609 938T703 882T854 730T910 522Q910 411 854 316T703 164T498 107Q387 107 293 163T142 315T86 522Q86 634 142 729T292 881T498 938Q609 938 703 882ZM402 883T321 834T193 702T145
522Q145 426 192 343T321 211T498 162Q594 162 674 211T802 343T850 522Q850 619 803 702T675 834T498 883Q402 883 321 834ZM595 762T638 732T683 647Q683 594 655 554T555 502Q582 487 600 464T637 406Q659 368 675 350T721 332V290H649Q618 290 601 312T564
380Q546 423 528 450T479 491L435 493V340L482 329V290H307V329L353 342V704L307 715V760H385Q404 759 417 759T441 760Q473 762 512 762Q595 762 638 732ZM493 716T476 716T435 711V538Q465 534 503 535Q546 535 573 562T600 626Q600 673 573 694T497 716Q493
716 476 716Z" />
<glyph unicode="&#xaf;" glyph-name="overscore" horiz-adv-x="438" d="M355 777V709H96V777H355Z" />
<glyph unicode="&#xb0;" glyph-name="degree" horiz-adv-x="333" d="M209 778T245 757T302 701T323 624Q323 582 302 546T245 489T166 468Q124 468 89 489T32 546T11 624Q11 665 32 700T88 757T166 778Q209 778 245 757ZM122 726T94 697T65 624Q65 582 94 552T167
521Q210 521 239 550T268 624Q268 666 239 696T167 726Q122 726 94 697Z" />
<glyph unicode="&#xb1;" glyph-name="plusminus" horiz-adv-x="619" d="M277 358L197 355H86V425H197L277 419L274 502V616H345V502L340 419L408 425H533V355H408L340 358L345 277V157H274V277L277 358ZM525 87V20H91V87H525Z" />
<glyph unicode="&#xb2;" glyph-name="uni00B2" horiz-adv-x="613" d="M129 434T201 480T328 581T383 681Q383 730 352 753T273 777Q228 777 180 754T103 694L70 734Q87 760 121 785T199 827T292 844Q361 844 403 823T464 772T482 711Q482 652 444 601T340 504T175
406L439 424L495 497L541 478L513 352H90L70 404Q129 434 201 480Z" />
<glyph unicode="&#xb3;" glyph-name="uni00B3" horiz-adv-x="559" d="M139 370T177 363T247 356Q318 357 356 387T394 465Q394 506 365 528T287 551Q279 550 256 546L188 532V587Q266 601 310 634T355 716Q355 749 329 764T262 779Q227 779 183 765T115 733L92
774Q126 800 183 819T292 838Q339 838 373 822T426 778T445 719Q445 629 332 589Q387 592 421 572T469 521T483 461Q483 415 456 379T377 321T257 300Q225 300 184 308T110 331V382Q139 370 177 363Z" />
<glyph unicode="&#xb4;" glyph-name="acute" horiz-adv-x="432" d="M264 868L330 801Q306 764 252 724T142 655L117 680L264 868Z" />
<glyph unicode="&#xb6;" glyph-name="paragraph" horiz-adv-x="659" d="M342 768T442 762L525 758H610V712L525 697V53L599 40V0H448L447 697L343 698V0H170V40L268 53V331Q163 331 111 384T58 551Q58 651 121 709T310 768Q342 768 442 762Z" />
<glyph unicode="&#xb7;" glyph-name="middot" horiz-adv-x="336" d="M202 473T216 458T230 419Q230 391 211 373T166 355Q143 355 126 369T109 407Q109 435 129 454T175 473Q202 473 216 458Z" />
<glyph unicode="&#xb8;" glyph-name="cedilla" horiz-adv-x="301" d="M157 -15T157 -39Q157 -54 158 -61Q220 -60 252 -88T285 -153Q285 -201 251 -228T161 -257L138 -258Q108 -258 89 -253T64 -247L65 -201Q76 -204 93 -206T123 -208Q157 -208 176 -197T196 -152Q196
-127 176 -114T117 -100Q108 -100 101 -101T91 -102Q91 -55 107 0H160Q157 -15 157 -39Z" />
<glyph unicode="&#xb9;" glyph-name="uni00B9" horiz-adv-x="530" d="M240 403V734Q178 717 104 701L95 747Q140 761 213 794T304 842L329 834L328 402L457 390V351H105V390L240 403Z" />
<glyph unicode="&#xba;" glyph-name="ordmasculine" horiz-adv-x="489" d="M340 778T392 722T444 569Q444 498 413 451T336 382T240 359Q185 359 141 383T72 453T46 564Q46 635 76 683T154 755T251 778Q340 778 392 722ZM193 731T164 692T135 574Q135 498 164
452T239 406Q298 406 326 439T355 559Q355 637 327 684T247 731Q193 731 164 692Z" />
<glyph unicode="&#xbb;" glyph-name="guillemotright" horiz-adv-x="928" d="M376 304L121 483L145 526L462 344V268L145 79L121 118L376 304ZM469 483L493 526L810 344V268L493 79L469 118L724 304L469 483Z" />
<glyph unicode="&#xbc;" glyph-name="onequarter" horiz-adv-x="1283" d="M821 899L451 -167L391 -148L762 918L821 899ZM256 403V734Q194 717 120 701L111 747Q156 761 229 794T320 842L345 834L344 402L473 390V351H121V390L256 403ZM995 544L1045 498L794 180L763
138L972 153L974 282L1053 291V159L1187 168L1189 91H1053V-87H971L972 91H709L685 134L964 543L995 544Z" />
<glyph unicode="&#xbd;" glyph-name="onehalf" horiz-adv-x="1283" d="M819 902L449 -167L389 -153L760 918L819 902ZM256 403V734Q194 717 120 701L111 747Q156 761 229 794T320 842L345 834L344 402L473 390V351H121V390L256 403ZM773 82T845 128T971 229T1026
328Q1026 377 995 401T915 425Q871 425 823 402T745 342L712 381Q729 407 763 433T842 475T935 492Q1005 492 1047 471T1107 419T1125 357Q1125 298 1087 247T983 152T818 54L1081 71L1137 145L1184 126L1155 -1H733L713 52Q773 82 845 128Z" />
<glyph unicode="&#xbe;" glyph-name="threequarters" horiz-adv-x="1293" d="M822 902L452 -167L392 -153L763 918L822 902ZM140 370T178 363T248 356Q319 357 357 387T395 465Q395 506 366 528T288 551Q280 550 257 546L189 532V587Q267 601 311 634T356 716Q356
749 330 764T263 779Q228 779 184 765T116 733L93 774Q127 800 184 819T293 838Q340 838 374 822T427 778T446 719Q446 629 333 589Q388 592 422 572T470 521T484 461Q484 415 457 379T378 321T258 300Q226 300 185 308T111 331V382Q140 370 178 363ZM995 544L1045
498L794 180L762 138L972 153L974 282L1053 291V159L1187 168L1189 91H1053V-87H971L972 91H709L685 134L964 543L995 544Z" />
<glyph unicode="&#xbf;" glyph-name="questiondown" horiz-adv-x="494" d="M257 701T271 686T285 647Q285 618 265 600T218 582Q196 582 180 597T163 635Q163 662 184 681T230 701Q257 701 271 686ZM270 452Q296 432 325 376T355 284Q354 255 319 232T216 181Q209
178 184 167T143 146Q125 136 119 128T112 107Q112 45 145 -8T233 -92T353 -124Q409 -124 434 -112V-182Q427 -191 399 -197T341 -204Q239 -204 173 -163T75 -57T43 75Q43 104 46 119T61 148T99 176Q120 189 166 211Q219 237 244 254T269 296Q269 324 260 353T231
426L220 452H270Z" />
<glyph unicode="&#xc0;" glyph-name="Agrave" horiz-adv-x="730" d="M325 795T274 818T180 871L246 938L394 810L370 785Q325 795 274 818ZM71 53L328 759H404L651 53L730 40V0H477V40L558 53L503 215H203L148 53L230 40V0H1V40L71 53ZM397 527L355 663L221 269H484L397
527Z" />
<glyph unicode="&#xc1;" glyph-name="Aacute" horiz-adv-x="730" d="M486 938L551 871Q508 842 457 819T361 785L337 810L486 938ZM71 53L328 759H404L651 53L730 40V0H477V40L558 53L503 215H203L148 53L230 40V0H1V40L71 53ZM397 527L355 663L221 269H484L397 527Z" />
<glyph unicode="&#xc2;" glyph-name="Acircumflex" horiz-adv-x="730" d="M322 938H408L535 812L496 789L365 876L232 789L196 811L322 938ZM71 53L328 759H404L651 53L730 40V0H477V40L558 53L503 215H203L148 53L230 40V0H1V40L71 53ZM397 527L355 663L221 269H484L397
527Z" />
<glyph unicode="&#xc3;" glyph-name="Atilde" horiz-adv-x="730" d="M190 889T228 913T305 938Q328 938 343 929T379 902Q397 887 408 880T435 873Q485 873 526 916L550 882Q532 848 494 824T417 799Q394 799 378 808T342 836Q324 852 313 859T286 866Q262 866
238 854T195 823L171 855Q190 889 228 913ZM71 53L328 759H404L651 53L730 40V0H477V40L558 53L503 215H203L148 53L230 40V0H1V40L71 53ZM397 527L355 663L221 269H484L397 527Z" />
<glyph unicode="&#xc4;" glyph-name="Adieresis" horiz-adv-x="730" d="M285 934T299 919T314 878Q314 849 294 831T246 812Q222 812 206 827T189 868Q189 895 210 914T257 934Q285 934 299 919ZM512 934T526 919T541 878Q541 849 521 831T473 812Q449 812 432
827T415 868Q415 895 436 914T483 934Q512 934 526 919ZM651 53L730 40V0H477V40L558 53L503 215H203L148 53L230 40V0H1V40L71 53L328 759H404L651 53ZM221 269H484L397 527L355 663L221 269Z" />
<glyph unicode="&#xc5;" glyph-name="Aring" horiz-adv-x="730" d="M416 938T452 906T488 825Q488 790 469 763T417 722L651 53L730 40V0H477V40L558 53L503 215H203L148 53L230 40V0H1V40L71 53L314 721Q281 735 261 762T241 825Q241 873 277 905T365 938Q416
938 452 906ZM336 887T316 869T296 824Q296 797 316 779T365 760Q393 760 413 778T434 824Q434 851 414 869T365 887Q336 887 316 869ZM221 269H484L397 527L355 663L221 269Z" />
<glyph unicode="&#xc6;" glyph-name="AE" horiz-adv-x="947" d="M69 53L355 758H825L840 618L793 617L771 704L465 710L550 423L746 426L760 503H802V298H760L746 371L565 373L662 50L825 58L873 162L913 142L891 0H480V40L562 53L510 226H211L146 53L230 40V0H1V40L69
53ZM398 585L381 676L231 279H493L398 585Z" />
<glyph unicode="&#xc7;" glyph-name="Ccedilla" horiz-adv-x="689" d="M487 765T527 758T612 737L640 730L632 590H584L560 674Q529 694 495 703T409 712Q336 712 274 673T174 559T136 383Q136 288 168 209T262 82T412 35Q529 35 575 91L601 169H641V31Q623 30
586 18Q544 5 506 -3T414 -13L413 -37L414 -61Q477 -60 509 -88T542 -153Q542 -201 507 -228T417 -257L395 -258Q365 -258 346 -253T321 -247V-201Q332 -204 350 -206T380 -208Q414 -208 433 -197T453 -152Q453 -127 433 -114T373 -100Q364 -100 357 -101T347 -102Q347
-60 360 -12Q268 -5 196 43T83 173T42 355Q42 476 96 569T240 714T437 765Q487 765 527 758Z" />
<glyph unicode="&#xc8;" glyph-name="Egrave" horiz-adv-x="645" d="M315 795T264 818T170 871L235 938L383 810L359 785Q315 795 264 818ZM134 53V705L52 713V758H560L574 618H523L500 704L222 710V422L409 426L423 503H470V293H423L409 371L222 374V50L521 58L569
163L613 152L591 0H50V40L134 53Z" />
<glyph unicode="&#xc9;" glyph-name="Eacute" horiz-adv-x="645" d="M474 938L539 871Q497 842 447 819T351 785L326 810L474 938ZM134 53V705L52 713V758H560L574 618H523L500 704L222 710V422L409 426L423 503H470V293H423L409 371L222 374V50L521 58L569 163L613
152L591 0H50V40L134 53Z" />
<glyph unicode="&#xca;" glyph-name="Ecircumflex" horiz-adv-x="645" d="M311 938H397L525 812L485 789L354 876L221 789L185 811L311 938ZM134 53V705L52 713V758H560L574 618H523L500 704L222 710V422L409 426L423 503H470V293H423L409 371L222 374V50L521
58L569 163L613 152L591 0H50V40L134 53Z" />
<glyph unicode="&#xcb;" glyph-name="Edieresis" horiz-adv-x="645" d="M274 934T288 919T303 878Q303 849 282 831T234 812Q211 812 194 827T177 868Q177 896 198 915T245 934Q274 934 288 919ZM500 934T514 919T529 878Q529 849 509 831T461 812Q437 812 420
827T403 868Q403 895 424 914T471 934Q500 934 514 919ZM574 618H523L500 704L222 710V422L409 426L423 503H470V293H423L409 371L222 374V50L521 58L569 163L613 152L591 0H50V40L134 53V705L52 713V758H560L574 618Z" />
<glyph unicode="&#xcc;" glyph-name="Igrave" horiz-adv-x="374" d="M148 795T97 818T3 871L68 938L216 810L192 785Q148 795 97 818ZM141 53V705L57 713V758H314V713L230 705V53L318 40V0H55V40L141 53Z" />
<glyph unicode="&#xcd;" glyph-name="Iacute" horiz-adv-x="374" d="M307 938L373 871Q330 842 279 819T184 785L159 810L307 938ZM141 53V705L57 713V758H314V713L230 705V53L318 40V0H55V40L141 53Z" />
<glyph unicode="&#xce;" glyph-name="Icircumflex" horiz-adv-x="374" d="M145 938H231L358 812L319 789L188 876L55 789L19 811L145 938ZM141 53V705L57 713V758H314V713L230 705V53L318 40V0H55V40L141 53Z" />
<glyph unicode="&#xcf;" glyph-name="Idieresis" horiz-adv-x="374" d="M107 934T121 919T136 878Q136 849 115 831T67 812Q44 812 27 827T10 868Q10 895 31 914T78 934Q107 934 121 919ZM334 934T348 919T363 878Q363 849 343 831T295 812Q271 812 254 827T237
868Q237 895 258 914T306 934Q334 934 348 919ZM314 713L230 705V53L318 40V0H55V40L141 53V705L57 713V758H314V713Z" />
<glyph unicode="&#xd0;" glyph-name="Eth" horiz-adv-x="778" d="M135 53V359H47V425H135V705L54 713V758H180Q228 758 288 762Q301 763 325 764T370 765Q551 765 642 669T734 399Q734 276 685 185T550 44T355 -5Q333 -5 279 -3Q234 0 182 0H52V40L135 53ZM286
45T348 45Q429 45 495 81T601 191T641 376Q641 542 565 628T351 714Q278 714 225 704V425H353V359H225V59Q286 45 348 45Z" />
<glyph unicode="&#xd1;" glyph-name="Ntilde" horiz-adv-x="812" d="M775 709L693 701V0H598L269 518L200 655V53L297 40V0H51V40L135 53V701L54 708V758H224L558 215L628 82V700L546 709V758H775V709ZM232 890T270 914T346 938Q369 938 385 929T422 902Q439 887
450 880T477 873Q526 873 567 916L592 882Q573 847 535 823T459 799Q436 799 420 808T383 836Q367 851 355 858T328 866Q304 866 280 854T237 823L213 855Q232 890 270 914Z" />
<glyph unicode="&#xd2;" glyph-name="Ograve" horiz-adv-x="759" d="M498 765T569 718T679 587T718 397Q718 270 672 177T547 35T372 -14Q274 -14 200 34T85 166T44 358Q44 486 94 580T229 721Q309 765 403 765Q498 765 569 718ZM312 716T258 675T173 555T143
368Q143 278 172 202T257 81T387 35Q459 35 511 77T592 198T620 385Q620 474 593 550T512 671T385 716Q312 716 258 675ZM347 795T296 818T202 871L267 938L416 810L391 785Q347 795 296 818Z" />
<glyph unicode="&#xd3;" glyph-name="Oacute" horiz-adv-x="759" d="M572 871Q530 842 479 819T383 785L358 810L507 938L572 871ZM497 765T568 718T678 586T717 395Q717 269 671 177T546 35T371 -14Q274 -14 200 34T85 166T44 358Q44 484 92 576T221 716T402
765Q497 765 568 718ZM312 716T258 675T173 555T143 368Q143 278 172 202T257 81T387 35Q459 35 511 77T592 198T620 385Q620 474 593 550T512 671T385 716Q312 716 258 675Z" />
<glyph unicode="&#xd4;" glyph-name="Ocircumflex" horiz-adv-x="759" d="M557 812L517 789L387 876L254 789L218 811L344 938H430L557 812ZM497 765T568 718T678 586T717 395Q717 269 671 177T546 35T371 -14Q274 -14 200 34T85 166T44 358Q44 484 92 576T221
716T402 765Q497 765 568 718ZM312 716T258 675T173 555T143 368Q143 278 172 202T257 81T387 35Q459 35 511 77T592 198T620 385Q620 474 593 550T512 671T385 716Q312 716 258 675Z" />
<glyph unicode="&#xd5;" glyph-name="Otilde" horiz-adv-x="759" d="M498 765T569 718T679 587T718 397Q718 270 672 177T547 35T372 -14Q274 -14 200 34T85 166T44 358Q44 486 94 580T229 721Q309 765 403 765Q498 765 569 718ZM312 716T258 675T173 555T143
368Q143 278 172 202T257 81T387 35Q459 35 511 77T592 198T620 385Q620 474 593 550T512 671T385 716Q312 716 258 675ZM211 890T249 914T325 938Q348 938 364 929T401 902Q418 887 429 880T456 873Q505 873 546 916L571 882Q552 847 514 823T438 799Q415 799
399 808T362 836Q346 851 334 858T307 866Q283 866 259 854T216 823L192 855Q211 890 249 914Z" />
<glyph unicode="&#xd6;" glyph-name="Odieresis" horiz-adv-x="759" d="M498 765T569 718T679 587T718 397Q718 270 672 177T547 35T372 -14Q274 -14 200 34T85 166T44 358Q44 486 94 580T229 721Q309 765 403 765Q498 765 569 718ZM312 716T258 675T173 555T143
368Q143 278 172 202T257 81T387 35Q459 35 511 77T592 198T620 385Q620 474 593 550T512 671T385 716Q312 716 258 675ZM434 934Q463 934 477 919T492 878Q492 849 471 831T423 812Q401 812 385 826Q366 812 343 812Q319 812 302 827T285 867Q285 895 306 914T354
934Q378 934 393 921Q412 934 432 934H434Z" />
<glyph unicode="&#xd7;" glyph-name="multiply" horiz-adv-x="608" d="M258 389L89 551L142 607L303 434L465 607L519 551L349 389L519 227L465 170L303 343L142 170L89 227L258 389Z" />
<glyph unicode="&#xd8;" glyph-name="Oslash" horiz-adv-x="759" d="M152 48T98 140T44 355Q44 482 91 574T220 716T402 765Q453 765 504 749L532 826L570 811L543 732Q625 689 671 601T717 396Q717 271 672 179T550 38T378 -14Q328 -14 288 -4L256 -96L215 -81L247
9Q152 48 98 140ZM437 716T385 716Q312 716 258 675T173 555T143 368Q143 272 176 192T270 70L484 690Q437 716 385 716ZM343 35T386 35Q458 35 511 77T592 198T620 385Q620 471 594 545T519 664L309 49Q343 35 386 35Z" />
<glyph unicode="&#xd9;" glyph-name="Ugrave" horiz-adv-x="770" d="M749 713L673 704V331Q673 156 598 71T390 -14Q231 -14 162 68T92 320L93 705L16 713V758H268V713L182 704V317Q182 162 240 98T406 34Q604 34 604 331V704L517 713V758H749V713ZM358 795T307
818T213 871L278 938L427 810L402 785Q358 795 307 818Z" />
<glyph unicode="&#xda;" glyph-name="Uacute" horiz-adv-x="770" d="M749 713L673 704V331Q673 156 598 71T390 -14Q231 -14 162 68T92 320L93 705L16 713V758H268V713L182 704V317Q182 162 240 98T406 34Q604 34 604 331V704L517 713V758H749V713ZM517 938L583
871Q540 842 489 819T394 785L369 810L517 938Z" />
<glyph unicode="&#xdb;" glyph-name="Ucircumflex" horiz-adv-x="770" d="M568 812L528 789L397 876L264 789L228 811L355 938H440L568 812ZM749 713L673 704V331Q673 156 598 71T390 -14Q231 -14 162 68T92 320L93 705L16 713V758H268V713L182 704V317Q182 162
240 98T406 34Q604 34 604 331V704L517 713V758H749V713Z" />
<glyph unicode="&#xdc;" glyph-name="Udieresis" horiz-adv-x="770" d="M749 713L673 704V331Q673 156 598 71T390 -14Q231 -14 162 68T92 320L93 705L16 713V758H268V713L182 704V317Q182 162 240 98T406 34Q604 34 604 331V704L517 713V758H749V713ZM444 934Q473
934 487 919T502 878Q502 849 481 831T433 812Q411 812 395 826Q376 812 353 812Q329 812 312 827T295 867Q295 895 316 914T364 934Q388 934 403 921Q422 934 442 934H444Z" />
<glyph unicode="&#xdd;" glyph-name="Yacute" horiz-adv-x="661" d="M299 54V311L63 705L6 713V758H242V713L165 705L325 410L350 360L372 411L517 704L434 713V758H655V713L590 701L388 310V54L493 40V0H199V40L299 54ZM465 938L531 871Q488 842 437 819T342
785L317 810L465 938Z" />
<glyph unicode="&#xde;" glyph-name="Thorn" horiz-adv-x="634" d="M136 53V705L52 713V758H309V713L225 705V600L258 604Q318 612 353 612Q471 612 540 559T610 401Q610 326 572 271T470 188T334 159L256 160Q244 161 225 161V53L357 40V0H42V40L136 53ZM262
205T325 205Q411 205 465 249T520 399Q520 475 472 518T333 561Q283 561 225 547V211Q262 205 325 205Z" />
<glyph unicode="&#xdf;" glyph-name="germandbls" horiz-adv-x="652" d="M110 54V428Q110 542 146 619T243 735T375 773Q440 773 484 740T528 630Q528 588 514 559T474 494Q452 465 441 444T430 394Q430 363 451 344T518 302Q557 282 581 265T623 220T640 152Q640
71 583 31T438 -10Q368 -10 329 -1V77Q354 64 386 53T444 41Q494 41 525 63T557 136Q557 171 533 192T457 240Q402 268 374 293T346 360Q346 381 356 402T391 461Q420 504 434 534T449 600Q449 653 425 686T351 719Q296 719 263 692T214 601T198 429V0H33V40L110
54Z" />
<glyph unicode="&#xe0;" glyph-name="agrave" horiz-adv-x="560" d="M364 556T416 516T468 377L467 40H529V9Q512 0 488 -5T439 -11Q406 -11 396 3T385 55Q329 -12 218 -12Q173 -12 136 7T78 61T57 139Q57 206 109 247T235 307T380 325V367Q380 441 345 468T243
496Q211 496 169 485T96 456L79 498Q112 525 171 540T275 556Q364 556 416 516ZM292 278T219 249T145 154Q145 46 265 46Q298 46 330 60T379 91L380 278Q292 278 219 249ZM289 683T234 723T153 801L219 868L366 680L341 655Q289 683 234 723Z" />
<glyph unicode="&#xe1;" glyph-name="aacute" horiz-adv-x="560" d="M364 556T416 516T468 377L467 40H529V9Q512 0 488 -5T439 -11Q406 -11 396 3T385 55Q329 -12 218 -12Q173 -12 136 7T78 61T57 139Q57 206 109 247T235 307T380 325V367Q380 441 345 468T243
496Q211 496 169 485T96 456L79 498Q112 525 171 540T275 556Q364 556 416 516ZM292 278T219 249T145 154Q145 46 265 46Q298 46 330 60T379 91L380 278Q292 278 219 249ZM366 868L432 801Q408 764 354 724T244 655L219 680L366 868Z" />
<glyph unicode="&#xe2;" glyph-name="acircumflex" horiz-adv-x="560" d="M364 556T416 516T468 377L467 40H529V9Q512 0 488 -5T439 -11Q406 -11 396 3T385 55Q329 -12 218 -12Q173 -12 136 7T78 61T57 139Q57 206 109 247T235 307T380 325V367Q380 441 345 468T243
496Q211 496 169 485T96 456L79 498Q112 525 171 540T275 556Q364 556 416 516ZM292 278T219 249T145 154Q145 46 265 46Q298 46 330 60T379 91L380 278Q292 278 219 249ZM241 866H327L457 674L418 652L284 789L142 652L107 673L241 866Z" />
<glyph unicode="&#xe3;" glyph-name="atilde" horiz-adv-x="560" d="M364 556T416 516T468 377L467 40H529V9Q512 0 488 -5T439 -11Q406 -11 396 3T385 55Q329 -12 218 -12Q173 -12 136 7T78 61T57 139Q57 206 109 247T235 307T380 325V367Q380 441 345 468T243
496Q211 496 169 485T96 456L79 498Q112 525 171 540T275 556Q364 556 416 516ZM292 278T219 249T145 154Q145 46 265 46Q298 46 330 60T379 91L380 278Q292 278 219 249ZM110 763T148 788T225 813Q248 813 263 804T299 775Q317 759 328 752T355 744Q381 744 403
757T446 792L470 759Q451 724 413 700T337 675Q314 675 298 684T262 712Q245 728 233 735T206 743Q163 743 115 695L91 729Q110 763 148 788Z" />
<glyph unicode="&#xe4;" glyph-name="adieresis" horiz-adv-x="560" d="M364 556T416 516T468 377L467 40H529V9Q512 0 488 -5T439 -11Q406 -11 396 3T385 55Q329 -12 218 -12Q173 -12 136 7T78 61T57 139Q57 206 109 247T235 307T380 325V367Q380 441 345 468T243
496Q211 496 169 485T96 456L79 498Q112 525 171 540T275 556Q364 556 416 516ZM292 278T219 249T145 154Q145 46 265 46Q298 46 330 60T379 91L380 278Q292 278 219 249ZM141 682T125 696T108 736Q108 763 128 782T175 801Q204 801 218 786T233 746Q233 717 213
700T165 682Q141 682 125 696ZM367 682T351 696T334 736Q334 763 355 782T401 801Q430 801 444 786T459 746Q459 717 439 700T391 682Q367 682 351 696Z" />
<glyph unicode="&#xe5;" glyph-name="aring" horiz-adv-x="560" d="M364 556T416 516T468 377L467 40H529V9Q512 0 488 -5T439 -11Q406 -11 396 3T385 55Q329 -12 218 -12Q173 -12 136 7T78 61T57 139Q57 206 109 247T235 307T380 325V367Q380 441 345 468T243
496Q211 496 169 485T96 456L79 498Q112 525 171 540T275 556Q364 556 416 516ZM292 278T219 249T145 154Q145 46 265 46Q298 46 330 60T379 91L380 278Q292 278 219 249ZM321 879T356 845T392 761Q392 712 357 679T270 645Q220 645 184 678T148 761Q148 810 184
844T270 879Q321 879 356 845ZM242 828T222 808T202 760Q202 732 222 712T270 692Q299 692 318 712T338 760Q338 789 319 808T270 828Q242 828 222 808Z" />
<glyph unicode="&#xe6;" glyph-name="ae" horiz-adv-x="881" d="M737 563T786 505T835 344Q835 303 830 270H466Q467 154 522 102T659 50Q697 50 741 60T808 82L825 43Q798 18 740 3T625 -12Q493 -12 428 86Q401 42 344 15T216 -12Q171 -12 135 7T77 61T56 139Q56
150 57 155Q63 218 119 256T247 310T379 325V367Q379 441 344 468T244 495Q213 495 171 484T95 455L79 498Q112 524 172 540T277 556Q339 556 382 537T447 468Q483 513 535 538T651 563Q737 563 786 505ZM560 514T517 466T467 324H742Q743 335 743 356Q743 463
687 497Q662 514 623 514Q560 514 517 466ZM329 278T276 267T184 228T145 154Q145 46 263 46Q298 46 331 64T378 106L379 278Q329 278 276 267Z" />
<glyph unicode="&#xe7;" glyph-name="ccedilla" horiz-adv-x="512" d="M308 -60Q370 -59 402 -87T435 -152Q435 -200 401 -227T311 -256L288 -257Q258 -257 239 -252T214 -246L215 -200Q226 -203 243 -205T273 -207Q307 -207 326 -196T346 -151Q346 -126 326 -113T267
-99Q258 -99 251 -100T241 -101Q241 -56 254 -7Q157 4 104 78T50 265Q50 355 87 422T189 525T332 561Q371 561 405 555T458 537V423H415L391 485Q376 497 354 502T308 508Q260 508 221 484T159 413T135 297Q135 214 161 159T229 78T317 52Q354 52 396 61T459 84L475
43Q450 21 405 7T308 -9Q307 -17 307 -35Q307 -53 308 -61V-60Z" />
<glyph unicode="&#xe8;" glyph-name="egrave" horiz-adv-x="560" d="M408 565T459 507T510 347Q510 312 504 270H137Q137 168 186 109T328 50Q368 50 412 59T478 82L495 43Q467 18 410 3T294 -12Q178 -12 114 64T49 271Q49 354 83 419T179 523T319 563Q408 565
459 507ZM233 514T190 467T138 324H417Q418 334 418 354Q418 463 362 497Q336 514 296 514Q233 514 190 467ZM302 683T247 723T166 801L232 868L379 680L354 655Q302 683 247 723Z" />
<glyph unicode="&#xe9;" glyph-name="eacute" horiz-adv-x="560" d="M408 565T459 507T510 347Q510 312 504 270H137Q137 168 186 109T328 50Q368 50 412 59T478 82L495 43Q467 18 410 3T294 -12Q178 -12 114 64T49 271Q49 354 83 419T179 523T319 563Q408 565
459 507ZM233 514T190 467T138 324H417Q418 334 418 354Q418 463 362 497Q336 514 296 514Q233 514 190 467ZM380 868L446 801Q422 764 368 724T258 655L233 680L380 868Z" />
<glyph unicode="&#xea;" glyph-name="ecircumflex" horiz-adv-x="560" d="M408 565T459 507T510 347Q510 312 504 270H137Q137 168 186 109T328 50Q368 50 412 59T478 82L495 43Q467 18 410 3T294 -12Q178 -12 114 64T49 271Q49 354 83 419T179 523T319 563Q408
565 459 507ZM233 514T190 467T138 324H417Q418 334 418 354Q418 463 362 497Q336 514 296 514Q233 514 190 467ZM256 866H342L472 674L433 652L299 789L157 652L122 673L256 866Z" />
<glyph unicode="&#xeb;" glyph-name="edieresis" horiz-adv-x="560" d="M408 565T459 507T510 347Q510 312 504 270H137Q137 168 186 109T328 50Q368 50 412 59T478 82L495 43Q467 18 410 3T294 -12Q178 -12 114 64T49 271Q49 354 83 419T179 523T319 563Q408
565 459 507ZM233 514T190 467T138 324H417Q418 334 418 354Q418 463 362 497Q336 514 296 514Q233 514 190 467ZM155 682T139 696T122 736Q122 763 142 782T189 801Q218 801 232 786T247 746Q247 717 227 700T179 682Q155 682 139 696ZM381 682T365 696T348 736Q348
763 369 782T415 801Q444 801 458 786T473 746Q473 717 453 700T405 682Q381 682 365 696Z" />
<glyph unicode="&#xec;" glyph-name="igrave" horiz-adv-x="334" d="M179 683T124 723T43 801L109 868L256 680L231 655Q179 683 124 723ZM141 49V458L72 490V519L204 555L225 545V49L305 39V0H59V39L141 49Z" />
<glyph unicode="&#xed;" glyph-name="iacute" horiz-adv-x="334" d="M141 49V458L72 490V519L204 555L225 545V49L305 39V0H59V39L141 49ZM512 868L578 801Q554 764 500 724T390 655L365 680L512 868Z" />
<glyph unicode="&#xee;" glyph-name="icircumflex" horiz-adv-x="334" d="M132 866H218L347 674L308 652L174 789L33 652L-3 673L132 866ZM141 49V458L72 490V519L204 555L225 545V49L305 39V0H59V39L141 49Z" />
<glyph unicode="&#xef;" glyph-name="idieresis" horiz-adv-x="334" d="M141 49V458L72 490V519L204 555L225 545V49L305 39V0H59V39L141 49ZM287 682T271 696T254 736Q254 763 274 782T321 801Q350 801 364 786T379 746Q379 717 359 700T311 682Q287 682 271
696ZM513 682T497 696T480 736Q480 763 501 782T547 801Q576 801 590 786T605 746Q605 717 585 700T537 682Q513 682 497 696Z" />
<glyph unicode="&#xf0;" glyph-name="eth" horiz-adv-x="602" d="M423 802L343 732Q446 654 499 541T553 301Q553 201 519 130T425 23T292 -13Q220 -13 165 20T80 112T49 245Q49 328 83 386T174 474T301 504Q342 504 387 485T456 437Q440 512 398 576T299 690L216
617L171 652L252 726Q202 759 158 773L169 823Q231 803 293 767L346 813L376 841L423 802ZM221 455T182 401T143 250Q143 192 162 143T218 65T307 35Q353 35 387 68T440 155T459 267Q459 315 456 329Q449 357 430 386T377 435T300 455Q221 455 182 401Z" />
<glyph unicode="&#xf1;" glyph-name="ntilde" horiz-adv-x="683" d="M135 49V456L64 490V519L190 555L211 545V477Q248 508 310 532T421 557Q505 557 540 503T575 326V49L653 39V0H413V39L487 49V312Q487 376 478 414T445 471T377 490Q342 490 301 477T223 437V49L297
39V0H67V39L135 49ZM190 763T228 788T305 813Q328 813 343 804T379 775Q397 759 408 752T435 744Q461 744 483 757T526 792L550 759Q531 724 493 700T417 675Q394 675 378 684T342 712Q325 728 313 735T286 743Q243 743 195 695L171 729Q190 763 228 788Z" />
<glyph unicode="&#xf2;" glyph-name="ograve" horiz-adv-x="602" d="M393 563T447 526T528 424T554 281Q553 196 519 129T427 25T298 -12Q213 -12 158 27T76 133T50 279Q50 364 86 429T183 528T313 563Q393 563 447 526ZM221 514T182 455T143 289Q143 182 184
111T306 39Q386 39 423 99T460 276Q460 383 420 448T299 514Q221 514 182 455ZM314 683T259 723T178 801L244 868L391 680L366 655Q314 683 259 723Z" />
<glyph unicode="&#xf3;" glyph-name="oacute" horiz-adv-x="602" d="M393 563T447 526T528 424T554 281Q553 196 519 129T427 25T298 -12Q213 -12 158 27T76 133T50 279Q50 364 86 429T183 528T313 563Q393 563 447 526ZM221 514T182 455T143 289Q143 182 184
111T306 39Q386 39 423 99T460 276Q460 383 420 448T299 514Q221 514 182 455ZM389 868L455 801Q431 764 377 724T267 655L242 680L389 868Z" />
<glyph unicode="&#xf4;" glyph-name="ocircumflex" horiz-adv-x="602" d="M393 563T447 526T528 424T554 281Q553 196 519 129T427 25T298 -12Q213 -12 158 27T76 133T50 279Q50 364 86 429T183 528T313 563Q393 563 447 526ZM221 514T182 455T143 289Q143 182
184 111T306 39Q386 39 423 99T460 276Q460 383 420 448T299 514Q221 514 182 455ZM265 866H351L481 674L442 652L308 789L166 652L131 673L265 866Z" />
<glyph unicode="&#xf5;" glyph-name="otilde" horiz-adv-x="602" d="M393 563T447 526T528 424T554 281Q553 196 519 129T427 25T298 -12Q213 -12 158 27T76 133T50 279Q50 364 86 429T183 528T313 563Q393 563 447 526ZM221 514T182 455T143 289Q143 182 184
111T306 39Q386 39 423 99T460 276Q460 383 420 448T299 514Q221 514 182 455ZM133 763T171 788T248 813Q271 813 286 804T322 775Q340 759 351 752T378 744Q404 744 426 757T469 792L493 759Q474 724 436 700T360 675Q337 675 321 684T285 712Q268 728 256 735T229
743Q186 743 138 695L114 729Q133 763 171 788Z" />
<glyph unicode="&#xf6;" glyph-name="odieresis" horiz-adv-x="602" d="M393 563T447 526T528 424T554 281Q553 196 519 129T427 25T298 -12Q213 -12 158 27T76 133T50 279Q50 364 86 429T183 528T313 563Q393 563 447 526ZM221 514T182 455T143 289Q143 182 184
111T306 39Q386 39 423 99T460 276Q460 383 420 448T299 514Q221 514 182 455ZM165 682T149 696T132 736Q132 763 152 782T199 801Q228 801 242 786T257 746Q257 717 237 700T189 682Q165 682 149 696ZM391 682T375 696T358 736Q358 763 379 782T425 801Q454 801
468 786T483 746Q483 717 463 700T415 682Q391 682 375 696Z" />
<glyph unicode="&#xf7;" glyph-name="divide" horiz-adv-x="624" d="M348 642T362 626T377 585Q377 556 356 538T308 519Q284 519 268 534T251 575Q251 602 272 622T319 642Q348 642 362 626ZM539 358H86V424H539V358ZM347 259T360 244T374 205Q374 176 355 158T308
140Q285 140 269 155T253 194Q253 221 274 240T319 259Q347 259 360 244Z" />
<glyph unicode="&#xf8;" glyph-name="oslash" horiz-adv-x="602" d="M120 39T85 111T50 276Q50 362 85 426T180 526T310 563Q348 563 380 554L408 636L445 622L418 541Q483 510 517 442T552 288Q552 201 519 133T427 26T297 -12Q265 -12 231 -4L201 -95L162 -80L192
8Q120 39 85 111ZM333 514T300 514Q221 514 182 456T143 289Q143 219 161 162T216 73L360 502Q333 514 300 514ZM276 39T306 39Q459 39 459 276Q459 343 444 395T395 478L251 50Q276 39 306 39Z" />
<glyph unicode="&#xf9;" glyph-name="ugrave" horiz-adv-x="631" d="M307 683T252 723T171 801L237 868L384 680L359 655Q307 683 252 723ZM53 496V531L173 556L190 547V211Q190 152 198 119T230 69T298 52Q340 52 381 72T441 110V478L380 492V530L514 556L527
548V40H587V5Q572 -3 546 -9T499 -15Q450 -15 450 25V66Q406 29 359 10T264 -10Q192 -10 157 18T113 94T103 224V483L53 496Z" />
<glyph unicode="&#xfa;" glyph-name="uacute" horiz-adv-x="631" d="M383 868L449 801Q424 763 370 724T261 655L236 680L383 868ZM53 496V531L173 556L190 547V211Q190 152 198 119T230 69T298 52Q340 52 381 72T441 110V478L380 492V530L514 556L527 548V40H587V5Q572
-3 546 -9T499 -15Q450 -15 450 25V66Q406 29 359 10T264 -10Q192 -10 157 18T113 94T103 224V483L53 496Z" />
<glyph unicode="&#xfb;" glyph-name="ucircumflex" horiz-adv-x="631" d="M261 866H346L476 674L437 652L303 789L161 652L126 673L261 866ZM53 496V531L173 556L190 547V211Q190 152 198 119T230 69T298 52Q340 52 381 72T441 110V478L380 492V530L514 556L527
548V40H587V5Q572 -3 546 -9T499 -15Q450 -15 450 25V66Q406 29 359 10T264 -10Q192 -10 157 18T113 94T103 224V483L53 496Z" />
<glyph unicode="&#xfc;" glyph-name="udieresis" horiz-adv-x="631" d="M53 496V531L173 556L190 547V211Q190 152 198 119T230 69T298 52Q340 52 381 72T441 110V478L380 492V530L514 556L527 548V40H587V5Q572 -3 546 -9T499 -15Q450 -15 450 25V66Q406 29 359
10T264 -10Q192 -10 157 18T113 94T103 224V483L53 496ZM160 682T144 696T127 736Q127 763 147 782T194 801Q223 801 237 786T252 746Q252 717 232 700T184 682Q160 682 144 696ZM386 682T370 696T353 736Q353 763 374 782T420 801Q449 801 463 786T478 746Q478
717 458 700T410 682Q386 682 370 696Z" />
<glyph unicode="&#xfd;" glyph-name="yacute" horiz-adv-x="563" d="M90 -177T113 -177Q157 -177 200 -139T273 -4H254L55 490L1 498V546H226V498L153 489L270 158L306 51L336 155L438 490L360 498V546H569V498L513 491L491 426Q423 226 385 117T336 -18Q296 -121
255 -176T157 -246Q141 -251 113 -251Q102 -251 90 -249T73 -244V-171Q90 -177 113 -177ZM380 868L446 801Q422 764 368 724T258 655L233 680L380 868Z" />
<glyph unicode="&#xfe;" glyph-name="thorn" horiz-adv-x="619" d="M189 785V500Q265 563 352 563Q411 563 460 536T539 448T569 295Q569 211 534 140T436 28T293 -14Q245 -14 186 -3L189 -50V-201L291 -210V-250H31V-210L102 -200V721L19 736V777L170 793L189
785ZM277 500T243 486T189 452V73Q196 54 227 45T302 35Q350 35 391 62T457 146T482 284Q482 393 434 446T316 500Q277 500 243 486Z" />
<glyph unicode="&#xff;" glyph-name="ydieresis" horiz-adv-x="563" d="M90 -177T113 -177Q157 -177 200 -139T273 -4H254L55 490L1 498V546H226V498L153 489L270 158L306 51L336 155L438 490L360 498V546H569V498L513 491L491 426Q423 226 385 117T336 -18Q296
-121 255 -176T157 -246Q141 -251 113 -251Q102 -251 90 -249T73 -244V-171Q90 -177 113 -177ZM156 682T140 696T123 736Q123 763 143 782T190 801Q219 801 233 786T248 746Q248 717 228 700T180 682Q156 682 140 696ZM382 682T366 696T349 736Q349 763 370 782T416
801Q445 801 459 786T474 746Q474 717 454 700T406 682Q382 682 366 696Z" />
<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="679" d="M593 424V358H86V424H593Z" />
<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="1172" d="M1086 424V358H86V424H1086Z" />
<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="379" d="M153 473T126 520T99 615Q99 693 145 752T254 837L275 811V797Q239 785 211 742T183 651Q183 612 204 581T254 546V525L204 459Q153 473 126 520Z" />
<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="379" d="M97 508Q133 519 160 562T188 654Q188 693 167 724T118 759V780L168 845Q219 833 246 786T273 690Q274 612 228 553T119 468L97 495V508Z" />
<glyph unicode="&#x201a;" glyph-name="quotesinglbase" horiz-adv-x="376" d="M168 128Q219 114 246 67T273 -28Q274 -106 228 -165T119 -250L97 -223V-211Q133 -199 160 -156T188 -64Q188 -25 167 6T118 41V62L168 128Z" />
<glyph unicode="&#x201c;" glyph-name="quotedblleft" horiz-adv-x="638" d="M153 472T126 519T99 615Q99 693 145 752T254 837L276 810V797Q240 785 212 742T184 651Q184 612 205 581T254 546V525L204 459Q153 472 126 519ZM411 472T385 520T358 617Q358 695
403 753T512 837L534 810V797Q498 786 471 743T443 651Q443 612 464 581T513 546V525L462 459Q411 472 385 520Z" />
<glyph unicode="&#x201d;" glyph-name="quotedblright" horiz-adv-x="649" d="M103 508Q139 519 166 562T194 654Q194 693 173 724T124 759V780L174 845Q226 833 252 786T279 690Q280 612 234 553T125 468L103 495V508ZM373 508Q409 519 436 562T464 654Q464 693
443 724T394 759V780L444 845Q496 833 522 786T549 690Q550 612 504 553T395 468L373 495V508Z" />
<glyph unicode="&#x201e;" glyph-name="quotedblbase" horiz-adv-x="646" d="M179 129Q230 116 257 69T284 -27Q285 -105 239 -164T130 -249L108 -222V-209Q143 -197 171 -154T199 -63Q199 -24 178 7T129 42V63L179 129ZM449 129Q501 116 527 69T554 -27Q555 -104
509 -163T400 -249L378 -222V-209Q414 -198 441 -155T469 -63Q469 -24 448 7T399 42V63L449 129Z" />
<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="412" d="M165 258T139 285T112 352Q112 394 141 424T211 455Q248 455 274 429T301 364Q301 321 272 290T202 258Q165 258 139 285Z" />
<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="582" d="M437 526L461 486L204 300L461 121L437 79L120 261V336L437 526Z" />
<glyph unicode="&#x203a;" glyph-name="guilsinglright" horiz-adv-x="582" d="M376 304L121 483L145 526L462 344V268L145 79L121 118L376 304Z" />
</font>
</defs>
</svg>