1
0
mirror of https://github.com/chylex/IntelliJ-AceJump.git synced 2025-08-14 08:17:06 +02:00

Merge branch 'chylex-optimizations'

This commit is contained in:
breandan
2020-11-19 17:54:15 -05:00
13 changed files with 243 additions and 96 deletions

@@ -1,5 +1,9 @@
# Changelog
### 3.6.4
- Improvements to tag latency. Thanks to @chylex for [the PR](https://github.com/acejump/AceJump/pull/339)!
### 3.6.3
- Vote for your favorite <a href="https://twitter.com/breandan/status/1274169810411274241">AceJump logo</a>!

@@ -171,6 +171,7 @@ The following individuals have significantly improved AceJump through their cont
* [John Lindquist](https://github.com/johnlindquist) for creating AceJump and supporting it for many years.
* [Breandan Considine](https://github.com/breandan) for maintaining the project and adding some new features.
* [Alex Plate](https://github.com/AlexPl292) for submitting [several PRs](https://github.com/acejump/AceJump/pulls?q=is%3Apr+author%3AAlexPl292).
* [Daniel Chýlek](https://github.com/chylex) for several [performance optimizations](https://github.com/acejump/AceJump/pull/339).
* [Sven Speckmaier](https://github.com/svensp) for [improving](https://github.com/acejump/AceJump/pull/214) search latency.
* [Stefan Monnier](https://www.iro.umontreal.ca/~monnier/) for algorithmic advice and maintaining Emacs for several years.
* [Fool's Mate](https://www.fools-mate.de/) for the [icon](https://github.com/acejump/AceJump/issues/313) and graphic design.

@@ -61,4 +61,4 @@ intellij {
}
group = "org.acejump"
version = "3.6.3"
version = "3.6.4"

@@ -22,6 +22,7 @@ import kotlin.system.measureTimeMillis
internal object Listener: FocusListener, AncestorListener, VisibleAreaListener {
private val logger = Logger.getInstance(Listener::class.java)
private val redoFindTrigger = Trigger()
fun enable() =
// TODO: Do we really need `synchronized` here?
@@ -55,8 +56,8 @@ internal object Listener: FocusListener, AncestorListener, VisibleAreaListener {
return
}
}
elapsed = (750L - elapsed).coerceAtLeast(0L)
Trigger(withDelay = elapsed) {
elapsed = (350L - elapsed).coerceAtLeast(0L)
redoFindTrigger(withDelay = elapsed) {
logger.info("Visible area changed")
redoFind()
}

@@ -19,10 +19,13 @@ object Selector {
if (matches.size == 1 && wasAlreadyVisible) Handler.reset()
}
fun nearestVisibleMatches(forward: Boolean = true) =
Finder.visibleResults().sortedWith(compareBy(
fun nearestVisibleMatches(forward: Boolean = true): List<Int> {
val caretOffset = caretOffset
return Finder.visibleResults().sortedWith(compareBy(
{ it == caretOffset },
{ (it <= caretOffset) == forward },
{ if (forward) it - caretOffset else caretOffset - it }
))
}
}

@@ -2,47 +2,40 @@ package org.acejump.control
import com.intellij.openapi.diagnostic.Logger
import org.acejump.search.runLater
import java.lang.System.currentTimeMillis
import kotlin.math.abs
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
/**
* Timer for triggering events with a designated delay.
*/
object Trigger: () -> Unit {
private val logger = Logger.getInstance(Trigger::class.java)
private var delay = 0L
private var timer = currentTimeMillis()
private var isRunning = false
private var invokable: () -> Unit = {}
override fun invoke() {
timer = currentTimeMillis()
if (isRunning) return
synchronized(this) {
isRunning = true
while (currentTimeMillis() - timer <= delay)
Thread.sleep(abs(delay - (currentTimeMillis() - timer)))
try {
invokable()
} catch (e: Exception) {
logger.error("Exception occurred while triggering event!", e)
}
isRunning = false
}
class Trigger {
private companion object {
private val executor = Executors.newSingleThreadScheduledExecutor()
}
private val logger = Logger.getInstance(Trigger::class.java)
private var task: Future<*>? = null
/**
* Can be called multiple times inside [delay], but doing so will reset the
* timer, delaying the [event] from occurring by [withDelay] milliseconds.
*/
operator fun invoke(withDelay: Long = 750, event: () -> Unit = {}) {
delay = withDelay
invokable = event
runLater(this)
@Synchronized
operator fun invoke(withDelay: Long, event: () -> Unit = {}) {
task?.cancel(true)
task = executor.schedule({
runLater {
try {
event()
} catch (e: Exception) {
logger.error("Exception occurred while triggering event!", e)
} finally {
task = null
}
}
}, withDelay, TimeUnit.MILLISECONDS)
}
}

@@ -42,8 +42,8 @@ enum class Pattern(val string: String) {
QGMLWB("1234567890", "qgmlwbyuv", "dstnriaeoh", "zxcfjkp"),
NORMAN("1234567890", "qwdfkjurl", "asetgynioh", "zxcvbpm");
private val priority
get() = when (this) {
private val priority by lazy {
when (this) {
QWERTY -> "fjghdkslavncmbxzrutyeiwoqp5849673210"
QWERTZ -> "fjghdkslavncmbxyrutzeiwoqp5849673210"
COLEMK -> "tndhseriaovkcmbxzgjplfuwyq5849673210"
@@ -53,6 +53,7 @@ enum class Pattern(val string: String) {
QGMLWB -> "naterisodhfkcpjxzlymuwbgvq5849673210"
WORKMN -> "tnhegysoaiclvkmxzwfrubjdpq5849673210"
}.mapIndices()
}
val text by lazy {
joinBy("").toCharArray().sortedBy { priority[it] }.joinToString("")

@@ -1,12 +1,11 @@
package org.acejump.label
import com.google.common.collect.Multimaps
import com.google.common.collect.Ordering
import com.google.common.collect.TreeMultimap
import com.intellij.openapi.diagnostic.Logger
import it.unimi.dsi.fastutil.ints.*
import it.unimi.dsi.fastutil.objects.Object2IntMap
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
import org.acejump.config.AceConfig
import org.acejump.search.wordBoundsPlus
import kotlin.collections.set
import kotlin.math.max
import kotlin.system.measureTimeMillis
@@ -53,9 +52,11 @@ class Solver(val text: String,
val availableTags: Set<String>,
val viewBounds: IntRange = 0..text.length) {
private val logger = Logger.getInstance(Solver::class.java)
private var newTags: MutableMap<String, Int> = HashMap(Pattern.NUM_TAGS)
private var newTags: Object2IntMap<String> =
Object2IntOpenHashMap(Pattern.NUM_TAGS)
private val newTagIndices: IntSet = IntOpenHashSet()
private var strings: Set<String> =
HashSet(results.map { getWordFragments(it) }.flatten())
HashSet(results.flatMap { getWordFragments(it) })
/**
* Iterates through remaining available tags, until we find one matching our
@@ -65,10 +66,12 @@ class Solver(val text: String,
* @param sites potential indices where a tag may be assigned
*/
private fun tryToAssignTag(tag: String, sites: Collection<Int>): Boolean {
if (tag in newTags.keys) return false
val index = sites.firstOrNull { it !in newTags.values } ?: return false
newTags[tag] = index
private fun tryToAssignTag(tag: String, sites: IntArray): Boolean {
if (newTags.containsKey(tag)) return false
val index = sites.firstOrNull { it !in newTagIndices } ?: return false
@Suppress("ReplacePutWithAssignment")
newTags.put(tag, index)
newTagIndices.add(index)
return true
}
@@ -88,7 +91,7 @@ class Solver(val text: String,
*/
private val tagOrder = AceConfig.defaultTagOrder
.thenBy { eligibleSitesByTag[it].size }
.thenComparingInt { eligibleSitesByTag.getValue(it).size }
.thenBy(AceConfig.layout.priority { it.last() })
/**
@@ -99,15 +102,31 @@ class Solver(val text: String,
* more likely to target words by their leading character.
*/
private val siteOrder: Comparator<Int> = compareBy(
// Sites in immediate view should come first
{ it !in viewBounds },
// Ensure that the first letter of a word is prioritized for tagging
{ text[max(0, it - 1)].isLetterOrDigit() },
{ it })
private val siteOrder: IntComparator = IntComparator { a, b ->
val aInBounds = a in viewBounds
val bInBounds = b in viewBounds
private val eligibleSitesByTag = Multimaps.synchronizedSetMultimap(
TreeMultimap.create<String, Int>(Ordering.natural(), siteOrder))
if (aInBounds != bInBounds) {
// Sites in immediate view should come first
return@IntComparator if (aInBounds) -1 else 1
}
val aIsNotFirstLetter = text[max(0, a - 1)].isLetterOrDigit()
val bIsNotFirstLetter = text[max(0, b - 1)].isLetterOrDigit()
if (aIsNotFirstLetter != bIsNotFirstLetter) {
// Ensure that the first letter of a word is prioritized for tagging
return@IntComparator if (bIsNotFirstLetter) -1 else 1
}
when {
a < b -> -1
a > b -> 1
else -> 0
}
}
private val eligibleSitesByTag: MutableMap<String, IntList> = HashMap(100)
/**
* Maps tags to search results according to the following constraints.
@@ -128,18 +147,30 @@ class Solver(val text: String,
var timeAssigned = 0L
val timeElapsed = measureTimeMillis {
val tagsByFirstLetter = availableTags.groupBy { it[0] }
results.parallelStream().forEach { site ->
val compatibleTags = tagsByFirstLetter.getTagsCompatibleWith(site)
compatibleTags.forEach { tag -> eligibleSitesByTag.put(tag, site) }
for (site in results) {
for (tag in tagsByFirstLetter.getTagsCompatibleWith(site)) {
eligibleSitesByTag.getOrPut(tag){ IntArrayList(10) }.add(site)
}
}
val sortedTags = eligibleSitesByTag.keySet().sortedWith(tagOrder)
val sortedTags = eligibleSitesByTag.keys.toMutableList().apply {
sortWith(tagOrder)
}
val matchingSites = HashMap<IntList, IntArray>()
val matchingSitesAsArrays = HashMap<String, IntArray>()
for ((key, value) in eligibleSitesByTag.entries) {
matchingSitesAsArrays[key] = matchingSites.getOrPut(value){
value.toIntArray().apply { IntArrays.mergeSort(this, siteOrder) }
}
}
timeAssigned = measureTimeMillis {
for (tagString in sortedTags) {
val eligibleSites = eligibleSitesByTag[tagString]
for (tag in sortedTags) {
if (totalAssigned == results.size) break
else if (tryToAssignTag(tagString, eligibleSites)) totalAssigned++
if (tryToAssignTag(tag, matchingSitesAsArrays.getValue(tag)))
totalAssigned++
}
}
}
@@ -172,7 +203,8 @@ class Solver(val text: String,
private fun getWordFragments(site: Int): List<String> {
val left = max(0, site + query.length - 1)
val right = text.wordBoundsPlus(site).second
val lowercase = text.substring(left, right).toLowerCase()
return (left..right).map { text.substring(left, it).toLowerCase() }
return (0..(right - left)).map { lowercase.substring(0, it) }
}
}

@@ -7,6 +7,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.ProjectManager
import com.intellij.ui.awt.RelativePoint
import com.intellij.util.IntPair
import org.acejump.view.Model.MAX_TAG_RESULTS
import org.acejump.view.Model.viewBounds
import java.awt.Point
@@ -59,21 +60,29 @@ fun Editor.isNotFolded(offset: Int) = !foldingModel.isOffsetCollapsed(offset)
* is seen on either side.
*/
fun String.wordBounds(index: Int): Pair<Int, Int> {
var (first, last) = index to index
fun String.wordBounds(index: Int): IntPair {
var first = index
var last = index
while (0 < first && get(first - 1).isJavaIdentifierPart()) first--
while (last < length && get(last).isJavaIdentifierPart()) last++
return first to last
return IntPair(first, last)
}
fun String.wordBoundsPlus(index: Int): Pair<Int, Int> {
operator fun IntPair.component1() = this.first
operator fun IntPair.component2() = this.second
fun String.wordBoundsPlus(index: Int): IntPair {
var (left, right) = wordBounds(index)
(right..(right + 3).coerceAtMost(length - 1)).asSequence()
.takeWhile { !(get(it) == '\n' || get(it) == '\r') }
.forEach { right = it }
for (it in (right..(right + 3).coerceAtMost(length - 1))) {
if (get(it) == '\n' || get(it) == '\r') {
break
} else {
right = it
}
}
return left to right
return IntPair(left, right)
}
fun defaultEditor(): Editor =
@@ -86,8 +95,8 @@ fun defaultEditor(): Editor =
fun Editor.getPoint(idx: Int) = visualPositionToXY(offsetToVisualPosition(idx))
fun Editor.getPointRelative(index: Int, relativeToComponent: JComponent) =
RelativePoint(relativeToComponent, getPoint(index)).originalPoint
fun Editor.getPointRelative(absolute: Point, relativeToComponent: JComponent) =
RelativePoint(relativeToComponent, absolute).originalPoint
fun Editor.isFirstCharacterOfLine(index: Int) =
index == getLineStartOffset(offsetToLogicalPosition(index).line)

@@ -32,6 +32,7 @@ object Finder : Resettable {
private var textHighlights = listOf<RangeHighlighter>()
private var HIGHLIGHT_LAYER = HighlighterLayer.LAST + 1
private val logger = Logger.getInstance(Finder::class.java)
private val skimTrigger = Trigger()
var isShiftSelectEnabled = false
var skim = false
@@ -79,7 +80,7 @@ object Finder : Resettable {
skim = true
logger.info("Skimming document for matches of: $query")
search()
Trigger(400L) { skim = false; search() }
skimTrigger(400L) { skim = false; search() }
} else search()
fun search(pattern: Pattern, bounds: Boundary = FULL_FILE_BOUNDARY) {

@@ -70,8 +70,10 @@ class Marker {
editor.run {
start = getPoint(index)
srcPoint = getPointRelative(index, contentComponent)
tagPoint = getPointRelative(tgIdx, contentComponent)
srcPoint = getPointRelative(start, contentComponent)
tagPoint =
if (index == tgIdx) srcPoint
else getPointRelative(getPoint(tgIdx), contentComponent)
}
startY = start.y + rectVOffset

@@ -11,6 +11,7 @@ import org.acejump.label.Tagger
import org.acejump.view.Canvas
import kotlin.random.Random
import kotlin.system.measureTimeMillis
import java.io.File
/**
* Functional test cases and end-to-end performance tests.
@@ -18,7 +19,7 @@ import kotlin.system.measureTimeMillis
* TODO: Add more structure to test cases, use test resources to define files.
*/
class AceTest : BasePlatformTestCase() {
class AceTest: BasePlatformTestCase() {
fun `test that scanner finds all occurrences of single character`() =
assertEquals("test test test".search("t"), setOf(0, 3, 5, 8, 10, 13))
@@ -133,31 +134,44 @@ class AceTest : BasePlatformTestCase() {
myFixture.checkResult("test <selection>拼音<caret></selection> selection")
}
fun `test tag latency`() {
fun `test tag latency`(editorText: String) {
var time = 0L
val random = Random(0)
repeat(100) {
makeEditor(
generateSequence {
generateSequence {
generateSequence {
('a'..'z').random(random)
}.take(5).joinToString("")
}.take(20).joinToString(" ")
}.take(100).joinToString("\n")
)
editorText.toCharArray().distinct().filter { !it.isWhitespace() }
.forEach { query ->
repeat(10) {
makeEditor(editorText)
myFixture.testAction(AceAction())
time += measureTimeMillis { typeAndWaitForResults("a") }
assertNotEmpty(Tagger.markers)
resetEditor()
}
myFixture.testAction(AceAction())
time += measureTimeMillis { typeAndWaitForResults("$query") }
assert(Tagger.markers.isNotEmpty()) { "Should be tagged: $query" }
resetEditor()
}
}
println("Average time to tag results: ${time / 100}ms")
}
fun getSettings() = ServiceManager.getService(AceConfig::class.java).aceSettings
fun `test random text latency`() =
`test tag latency`(
generateSequence {
generateSequence {
generateSequence {
('a'..'z').random(Random(0))
}.take(5).joinToString("")
}.take(20).joinToString(" ")
}.take(100).joinToString("\n")
)
fun `test lorem ipsum latency`() =
`test tag latency`(
File(
javaClass.classLoader.getResource("lipsum.txt")!!.file
).readText()
)
fun getSettings() =
ServiceManager.getService(AceConfig::class.java).aceSettings
// Enforces the results are available in less than 100ms
private fun String.search(query: String) =
@@ -176,6 +190,7 @@ class AceTest : BasePlatformTestCase() {
}).size - 1, myFixture.editor.markupModel.allHighlighters.size)
private var shouldWarmup = true
// Should be run exactly once to warm up the JVM
private fun maybeWarmUp(text: String, query: String) {
if (shouldWarmup) {

@@ -0,0 +1,85 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dignissim, nisl vitae commodo rutrum, nunc ante congue odio, vitae elementum libero diam et ipsum. Pellentesque gravida auctor ligula at venenatis. Sed varius libero sed lorem tristique cursus a in sapien. Pellentesque sodales eleifend ultricies. Donec ut massa nec urna maximus semper. Etiam tincidunt mauris nisl, ac semper sapien gravida vitae. Duis quis hendrerit elit. Pellentesque auctor lorem metus, sed auctor metus suscipit et. Vivamus sollicitudin neque eget tristique vestibulum.
In faucibus quam quis magna vehicula vestibulum. Aenean et sapien risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nunc vitae velit ac diam lobortis fringilla. Cras ac nunc facilisis, vulputate ipsum eu, sollicitudin tortor. Integer vitae justo vulputate, varius metus sit amet, mattis ante. Nulla pretium lacus vitae dui pharetra, ac efficitur odio facilisis. Nam tempor libero felis. Mauris facilisis sem eu nunc pharetra efficitur.
Ut pretium pharetra rhoncus. Proin pharetra non nisi id suscipit. Mauris porta dolor vel est pulvinar cursus. Mauris eget est ac sem lacinia tristique a eget ante. Proin id dictum nisi. Suspendisse hendrerit sem vitae nunc rhoncus, sit amet blandit mi malesuada. Aliquam lobortis enim velit, non rutrum eros cursus in. Etiam volutpat ut elit nec ornare. Quisque lacinia, augue sit amet mattis ultricies, dui orci iaculis dui, non interdum tortor arcu ut urna. Donec arcu justo, suscipit vel neque vel, finibus imperdiet eros.
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas scelerisque fringilla dolor, eget facilisis quam. Donec in venenatis mauris. Nunc lobortis lorem a mauris dapibus accumsan. Nulla nunc nunc, facilisis ac vestibulum ut, consectetur quis elit. Nullam sollicitudin eros magna, ut efficitur libero imperdiet consectetur. Maecenas commodo pretium risus. Praesent pretium metus vitae urna accumsan, vel dapibus mauris posuere. Sed vitae mauris nec augue consectetur feugiat. Curabitur luctus, felis in blandit tempor, libero mi suscipit libero, in bibendum nisi mauris ut magna. Etiam vitae volutpat risus, at rhoncus ipsum. Mauris id sapien sit amet lectus placerat feugiat. Pellentesque condimentum lacinia ipsum, at faucibus diam. In hac habitasse platea dictumst.
Nulla quis velit eu risus viverra volutpat a ut augue. Nam accumsan velit at purus malesuada accumsan. Morbi convallis dolor vitae velit scelerisque rhoncus. Quisque auctor velit quis nisi rutrum, in suscipit mi fringilla. Suspendisse ultrices nisl nec lorem porta cursus. Suspendisse sollicitudin ante at mattis pretium. Praesent vulputate et massa at laoreet. Nulla pharetra enim mollis, bibendum erat at, aliquam velit. Nulla et eros lorem. Mauris a sapien non sem congue volutpat non a turpis. Duis et urna lectus. Morbi eleifend scelerisque nisl, eget laoreet nibh dignissim et.
Proin mollis magna nunc, a varius leo pretium ac. Aliquam id eleifend elit, sollicitudin pharetra augue. Morbi aliquam turpis id congue sollicitudin. Proin pellentesque libero dictum rutrum ullamcorper. Nullam risus sem, pretium vitae nulla non, viverra sodales lorem. Nam est ligula, suscipit a nisi in, vestibulum aliquet quam. Nulla sollicitudin, justo ut suscipit porttitor, tellus lacus sodales sem, eget molestie sapien nisi condimentum ex. Aliquam erat volutpat. Suspendisse auctor ac orci at semper. Nam condimentum, ligula nec finibus pretium, purus dolor accumsan metus, eu laoreet est turpis id elit. Nullam eu facilisis eros, id pharetra magna. Nulla facilisi. Integer pellentesque suscipit tellus, ac convallis urna. Praesent dapibus enim ac risus blandit, quis maximus enim volutpat. Vestibulum est tellus, semper ac erat ac, eleifend sodales augue.
Nunc purus tellus, elementum ac arcu eu, dictum ultricies massa. Nam nec hendrerit ante, et efficitur leo. Praesent eget mollis erat. Nunc consectetur pretium urna sit amet eleifend. Nullam dignissim ipsum vitae faucibus faucibus. Vivamus ligula ante, iaculis sit amet mattis a, rutrum sit amet sapien. Duis eu velit accumsan, accumsan velit quis, pellentesque massa.
Donec finibus magna et varius accumsan. Duis condimentum augue vel tempor laoreet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras a nisl nec sem ultrices condimentum sit amet id tortor. Nullam in semper enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean aliquam nunc nibh, et posuere erat convallis tincidunt. Aliquam fermentum gravida pretium. Nulla ullamcorper neque eget quam lacinia, ac laoreet magna vehicula. Curabitur sodales eros nibh. Aliquam sit amet feugiat enim. Proin aliquam nisl vel mauris viverra, a mollis massa pulvinar. Quisque felis odio, rhoncus sed consectetur vel, sodales sed est. Pellentesque volutpat, odio quis tincidunt tempus, ipsum tortor placerat erat, sed tempus enim tortor a lorem. Duis ultricies bibendum rhoncus.
Etiam faucibus, nisl id imperdiet ornare, lectus tellus condimentum nisi, finibus commodo justo ante id sem. Donec dapibus semper imperdiet. Donec ex diam, efficitur ut viverra consectetur, congue ut eros. Morbi sed odio diam. Maecenas varius velit at nibh sagittis laoreet. Aliquam semper lorem non iaculis convallis. Nunc ac maximus ligula. Phasellus eget rhoncus libero, nec mattis purus. Nulla congue lacus nec augue eleifend blandit.
In non dui nunc. Sed vitae turpis consectetur lorem tincidunt mollis. Donec ac nulla gravida, tempus leo sit amet, tincidunt turpis. Nulla blandit ac ante fringilla volutpat. Nam vitae fringilla diam, sit amet pellentesque metus. Etiam nec quam tristique, condimentum lacus id, vehicula velit. Nunc pretium tortor sed condimentum consectetur. Cras laoreet non nulla ac bibendum. Nam consectetur, velit aliquet iaculis vehicula, enim risus ultrices risus, non maximus ligula nulla et metus.
Maecenas mattis condimentum elit, in ultrices leo lobortis vitae. Curabitur ac mattis quam. Nulla vel volutpat urna. Maecenas ornare leo nisi, eu maximus eros vestibulum et. Vestibulum ullamcorper ultrices sapien ac aliquet. Vivamus in bibendum tellus, eu lacinia elit. Integer eget ante eu metus interdum dapibus. Curabitur pulvinar enim a orci ullamcorper, quis laoreet dui bibendum. Integer purus odio, pellentesque pharetra viverra bibendum, eleifend non elit. Morbi suscipit finibus nibh. In varius malesuada urna, id ultrices sem mattis non. Etiam magna justo, fringilla at laoreet in, porttitor a tellus.
Curabitur varius porttitor nibh tristique aliquam. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec sodales finibus diam et scelerisque. Nulla non nisi vitae nunc aliquet ullamcorper. Ut dignissim augue id orci luctus, a porta libero consequat. Mauris condimentum, augue vitae consectetur accumsan, augue massa ullamcorper ligula, eu dictum nisl diam vel nisi. Sed suscipit, diam a lacinia egestas, tellus tortor rutrum risus, quis suscipit urna eros vel ex. Quisque id pharetra nibh. Fusce at pharetra lectus. Cras tincidunt lobortis sapien, sed ornare elit mattis eu. Pellentesque elementum, mauris id pellentesque lobortis, risus lectus euismod quam, et facilisis sapien arcu id velit. Praesent rutrum sagittis varius. In rhoncus libero eget ullamcorper luctus. In hac habitasse platea dictumst.
Nulla scelerisque velit egestas leo efficitur, ut cursus enim dapibus. Quisque non ornare tortor. Morbi molestie sem justo, id ultricies nibh mattis ut. Suspendisse potenti. Nullam maximus purus id leo vulputate, non ultricies velit auctor. Aenean ut laoreet neque. Donec at congue dolor, in rutrum mi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce finibus, dui sed vulputate pulvinar, purus nisl mattis nisl, sed egestas diam metus sed lorem. Quisque at venenatis ligula. Sed in eros tortor. Morbi luctus condimentum ipsum commodo congue.
Praesent ut ipsum consectetur, dignissim erat sed, eleifend tellus. Donec eros nisi, gravida ut tortor sed, dapibus consectetur enim. Nunc sollicitudin in urna quis rutrum. Maecenas non diam nisi. Curabitur scelerisque nunc quis dui rutrum tempus. Aliquam a lobortis risus. Suspendisse potenti. Vestibulum eu neque tincidunt, tincidunt velit ac, euismod ex. Vivamus ac leo enim.
Maecenas mollis maximus metus et convallis. Vestibulum accumsan sollicitudin urna, sit amet malesuada ante rutrum at. Praesent porta a urna id egestas. Nulla maximus massa et efficitur fringilla. Sed sit amet mauris sodales, aliquam magna ut, condimentum nibh. Proin pulvinar mi ipsum, vitae laoreet nulla suscipit eget. Aliquam vulputate, nisi id sodales aliquet, ipsum dolor sodales quam, sit amet vulputate sem nunc a diam. Pellentesque ultricies lacus elit, vitae pulvinar est lacinia ac. Fusce turpis tellus, pretium a eleifend et, tempus vel nisl. Vestibulum elementum volutpat nibh ut porttitor. Curabitur porttitor ullamcorper dolor, quis semper purus sollicitudin in.
Aenean id blandit est. In quis arcu placerat, efficitur turpis sed, ornare enim. Fusce sed lectus suscipit, interdum urna sit amet, imperdiet mauris. Duis finibus lacus accumsan bibendum convallis. Nam sit amet augue condimentum, posuere arcu in, posuere orci. Mauris mi sapien, dictum sit amet arcu eu, lacinia consectetur nulla. Maecenas dictum mollis efficitur. Suspendisse sed ipsum accumsan, maximus tellus eu, sodales dui. Nulla a velit et metus auctor euismod. Vestibulum tempus augue et felis fringilla, fringilla viverra nibh commodo. Aenean ac finibus felis. Aliquam porta sapien ut enim elementum, a iaculis lorem aliquet. Suspendisse euismod luctus dui, eget fermentum est ullamcorper quis. Duis pharetra nibh eget mauris aliquam fermentum. Pellentesque non rutrum sem.
Donec dui lacus, ultrices quis tellus vitae, euismod sollicitudin nibh. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed ullamcorper facilisis nisl, quis ornare lacus fringilla vitae. Nullam sollicitudin diam lacinia diam venenatis, in efficitur diam accumsan. Duis ullamcorper nisl eget nulla pharetra molestie. Curabitur malesuada blandit mauris, in dignissim ipsum fringilla in. Donec tempor sit amet sem eu facilisis. Morbi molestie ligula quis orci blandit volutpat. Sed placerat vel nisl vel posuere.
Suspendisse libero orci, auctor et posuere id, malesuada nec velit. Quisque pharetra neque lorem, non vehicula mauris eleifend nec. Nam rhoncus bibendum lectus, et fermentum ante fermentum ac. Vivamus porttitor mi gravida dui volutpat, ac ultrices nisl cursus. Praesent ac lacus et lectus tincidunt lobortis a eu nisl. Donec ut velit odio. Duis congue dapibus libero nec sodales. Vivamus volutpat velit nec varius facilisis. Vivamus lobortis et dui in interdum. Aenean laoreet sed tortor eget sodales. Pellentesque posuere posuere felis sed sollicitudin. Sed eget tortor lectus. Proin mattis libero in nisl congue fermentum. Fusce id libero eget justo ullamcorper ultrices sit amet eu justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer id est odio.
Proin egestas risus vitae libero tempus volutpat. Proin faucibus felis in vulputate finibus. Fusce id tempus dolor, sed venenatis magna. Quisque odio nunc, tincidunt et facilisis quis, eleifend quis odio. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam posuere rhoncus enim, ac condimentum leo volutpat tempus. Cras sagittis, felis non lobortis rhoncus, eros nunc dignissim turpis, vel ullamcorper eros augue ut leo. Donec lobortis eros pulvinar facilisis convallis. In dictum, massa nec dapibus efficitur, orci ipsum congue neque, sit amet mattis nisi lacus id justo. Pellentesque eget porttitor mauris. Nam rhoncus velit vel velit accumsan, vel commodo sapien interdum. Vivamus lectus enim, lobortis ac ipsum at, posuere rutrum dui.
Sed vulputate mollis aliquam. Etiam sed auctor lorem, nec ornare justo. Sed blandit scelerisque nunc id elementum. Sed laoreet arcu et dolor volutpat, a viverra est aliquam. Sed nec nisi varius, congue eros non, ornare lacus. Aliquam vel arcu fermentum, convallis nisi quis, pharetra magna. Nulla facilisi. Curabitur quis dolor vestibulum, volutpat elit sit amet, iaculis arcu. Praesent sed tincidunt dolor, id consequat ante. Pellentesque aliquet congue ante viverra semper. Phasellus suscipit eros ac lorem feugiat, eu feugiat enim rhoncus.
Nam egestas eu tortor at porta. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam ut mauris malesuada, suscipit lectus et, porttitor sapien. Phasellus vitae nibh tincidunt, malesuada lacus ac, tincidunt nunc. Sed et rhoncus justo. Nunc ullamcorper lacus neque, at pretium dui luctus et. Vivamus aliquet, nisi ut placerat venenatis, arcu lectus elementum enim, a imperdiet dolor dolor at ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut elementum lectus dolor. Vivamus velit erat, tincidunt vel augue ornare, scelerisque tempor orci. Sed tristique sed metus sit amet consectetur. In vel lobortis nulla. Etiam sit amet dolor nec diam convallis ultricies egestas vitae diam. Ut ut eros a mi sagittis ornare sed nec urna. Morbi condimentum magna quis augue vehicula placerat.
Proin in aliquet quam. Sed porttitor lacus ut eros auctor, ut laoreet leo varius. Morbi lobortis sem neque, et commodo enim tincidunt a. Nam placerat semper faucibus. Aliquam sodales accumsan malesuada. Integer ipsum ipsum, bibendum sed ante nec, eleifend semper nibh. Nunc suscipit ullamcorper velit, sed fringilla nulla consequat ut. Curabitur sodales nisl at metus lacinia mollis. Nunc finibus vulputate rutrum. Donec vitae congue sapien. Nunc at elit sed purus molestie scelerisque. Morbi nisi metus, tempor quis nibh in, elementum hendrerit lacus. Fusce id lacinia lectus. Phasellus cursus imperdiet dolor. Donec rutrum lacus nec quam efficitur, sed suscipit lacus efficitur. Donec laoreet augue magna, non tincidunt orci pellentesque vel.
Vivamus ut justo non nulla tristique vehicula. Praesent in aliquam purus, vitae viverra lacus. Vivamus ex leo, tempus vitae mauris id, aliquam sollicitudin lorem. Etiam ac neque nisl. Nulla facilisi. Aliquam erat volutpat. Curabitur quis maximus est, sed faucibus tellus. Vestibulum a hendrerit massa. Quisque eget ultricies tortor. Nulla rutrum vel arcu ac faucibus. In sed accumsan tortor. Maecenas fermentum ex ante, quis placerat metus mollis et.
Mauris in suscipit quam, at suscipit risus. Praesent dapibus, tellus id mollis sodales, magna nibh cursus ipsum, a laoreet metus dui nec sem. Quisque tempor euismod eros sit amet ornare. Morbi velit mi, condimentum eget mauris et, pretium vulputate ipsum. Vivamus aliquam egestas nibh, quis tincidunt arcu rutrum id. Donec eget accumsan velit. Nulla fringilla velit nunc, eget luctus mauris mollis accumsan. Quisque at tempus arcu. Vestibulum sed scelerisque lectus. Suspendisse varius ultrices justo ac ultrices. In purus orci, accumsan sed neque vitae, dapibus egestas sapien. Phasellus ultrices lacus nec eros malesuada molestie.
Nunc et tortor interdum, auctor augue volutpat, vehicula velit. Ut massa turpis, volutpat et eleifend a, commodo non tortor. Suspendisse molestie sem nec felis egestas, a posuere dui ullamcorper. Nam a finibus nulla. Proin in tortor malesuada, iaculis ligula quis, semper dui. Proin euismod, turpis at pharetra gravida, elit urna vestibulum dui, a vestibulum augue ipsum at dui. In egestas lobortis felis, eu eleifend erat dictum in. Nullam congue finibus nibh, eget molestie magna. Vestibulum est nisi, accumsan eget vehicula eu, mollis eu odio. Nullam mauris nisi, maximus eu augue sed, egestas fringilla sem. Proin consequat sagittis ex, a consectetur felis commodo vitae. In pharetra ipsum id auctor vestibulum. Curabitur quis viverra nisl, eget rutrum purus. In a ligula vel ex tristique accumsan non accumsan ante.
Quisque blandit pellentesque placerat. Mauris eget blandit dui. Suspendisse tellus lectus, pretium ut purus nec, feugiat gravida metus. Ut convallis ante nisl. Duis et purus ut neque imperdiet cursus. Curabitur at eleifend leo. Nam molestie, eros non dignissim cursus, lacus enim interdum purus, et ultrices purus nisl quis nunc. Integer malesuada et tortor a tincidunt. Nam eleifend sit amet risus eu sodales. Quisque blandit consectetur semper. Vivamus tincidunt nibh libero, vel pellentesque risus posuere placerat.
Nullam sit amet posuere nunc. Quisque ut elementum dui, eu semper mauris. Nulla a dolor magna. Proin facilisis eleifend enim, eget ultricies dui mattis vitae. Aliquam viverra, erat id tristique bibendum, neque est sagittis est, ut rutrum ipsum eros placerat sem. Duis ut orci maximus est aliquet volutpat. Phasellus sit amet varius lacus, sit amet mattis ligula. Fusce pulvinar non tellus eu gravida. Morbi a ligula a dui mattis facilisis ac at risus. Nulla facilisi.
Ut a efficitur arcu, a iaculis libero. Curabitur ut ipsum non justo fermentum gravida. Praesent ac eros bibendum, blandit mi sit amet, vulputate elit. Suspendisse vitae erat faucibus, cursus velit et, placerat dui. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla sed efficitur mi. Pellentesque lacinia sollicitudin est ac scelerisque. Maecenas consectetur pulvinar diam, vitae tempus sem bibendum non. Phasellus tortor eros, tristique eget pharetra ac, vehicula non metus.
Suspendisse finibus eget tellus eu ultrices. Maecenas at sodales ligula. Praesent vel turpis mollis, molestie neque laoreet, sollicitudin arcu. Cras ac auctor est. Phasellus in augue bibendum, tempor ipsum in, vulputate ex. Ut in purus risus. Duis ut porttitor orci. Fusce rhoncus metus eu libero sodales sollicitudin. Mauris id risus eu nibh sagittis sollicitudin. Cras dignissim elit et ante laoreet, id sodales diam rutrum. Nam dignissim, magna sed bibendum pharetra, ex massa aliquet ante, quis rutrum nulla diam quis justo. Morbi luctus volutpat arcu. Pellentesque tempus nulla a elit interdum maximus.
In erat magna, ultricies nec ornare sed, accumsan at orci. Vivamus malesuada sed lorem at laoreet. Aliquam pharetra lorem vel neque tristique hendrerit. Aliquam quis lacus finibus, pharetra purus malesuada, efficitur metus. Phasellus eget iaculis nulla. Maecenas porta dolor ut nulla imperdiet egestas. Ut molestie accumsan leo nec imperdiet. Etiam vitae neque at neque bibendum finibus. Nam vestibulum, tortor sit amet rutrum mollis, erat lectus fringilla leo, et porta urna est a nisl. Integer cursus est quis facilisis tincidunt. Cras nec egestas nibh, dignissim maximus nulla. Nunc eu ligula dapibus, semper nisl nec, ullamcorper ante. Vestibulum volutpat enim a tempus aliquet. Integer commodo gravida risus, vitae malesuada arcu aliquam sed. Nam orci nulla, auctor vel dui in, egestas ornare sem.
Morbi laoreet urna sed enim vehicula, non hendrerit sapien feugiat. Duis convallis ac lacus ac malesuada. Cras congue odio ligula, nec ornare velit luctus et. Aliquam sit amet mauris nec felis varius facilisis. Praesent rhoncus libero vitae mi scelerisque, et gravida turpis gravida. Sed pellentesque nibh turpis, ut dictum mauris mollis id. In ultrices magna eu nisl pharetra congue. Quisque finibus eu felis quis semper. Suspendisse bibendum diam blandit luctus vehicula. Suspendisse varius ipsum risus, vulputate ultrices justo suscipit a. Sed augue leo, consequat sed venenatis dapibus, consequat nec urna. Nulla non lectus sem. Maecenas eu mattis leo.
Ut eu felis et nibh lacinia tincidunt. Integer et porttitor tortor, sit amet mattis diam. Aliquam massa libero, maximus sed felis eu, tincidunt placerat justo. Phasellus dui nulla, mattis sit amet lacinia sit amet, dictum vitae neque. Nulla quis dolor interdum, luctus justo et, posuere libero. Nullam a nisi vitae quam venenatis pellentesque. In hendrerit venenatis ante et pharetra. Nullam dictum eu sapien eu varius. Nulla sed viverra tellus, quis gravida velit. Mauris mollis, lacus non auctor pretium, nulla tellus egestas erat, in convallis magna ante ut nunc. Nam elit lectus, finibus eu facilisis eget, bibendum a augue. Suspendisse vitae neque laoreet, convallis ex sodales, tempor velit. Ut sed leo ultrices, tristique neque et, varius lorem.
Donec et lacus ullamcorper, vulputate diam et, pretium lacus. Mauris commodo finibus nulla sit amet sollicitudin. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam porttitor elementum nisl sed malesuada. Duis at tempus augue, id sagittis orci. Morbi ut tellus non diam consequat pretium ac non justo. In magna diam, vehicula non enim a, scelerisque consectetur nisi. Sed at mi vestibulum, feugiat purus non, semper sem. Aenean faucibus nisi eget nisi convallis tristique. Fusce tincidunt tempus est, id mollis lorem congue ac.
Proin pretium metus vitae suscipit laoreet. Etiam sit amet tempor nisi. Quisque finibus dictum nisi ut scelerisque. Suspendisse vitae nisl faucibus, dapibus sapien quis, luctus mi. Aenean venenatis mattis efficitur. Curabitur mauris nibh, interdum ut sodales ut, mollis a magna. Nulla enim quam, fermentum eu molestie at, consectetur laoreet diam. Cras ut posuere tellus. Pellentesque tristique facilisis dui, in scelerisque mauris porta nec. Sed semper massa non commodo vestibulum.
Sed iaculis, elit nec bibendum porttitor, sem justo fermentum dui, et imperdiet magna neque quis velit. Donec luctus cursus quam ut lobortis. Duis ultrices ultricies justo sit amet vestibulum. Suspendisse lorem arcu, cursus vel diam eget, porttitor pharetra ante. Aenean condimentum tincidunt dui et maximus. Suspendisse lobortis tristique tortor, ac maximus arcu. Morbi vel erat ultricies, ultricies nisl a, condimentum libero. Fusce egestas blandit magna non blandit. Cras quis pulvinar augue. Nulla molestie molestie nisl eget euismod. Nulla at lacinia nisl. Duis ut euismod tellus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Phasellus nunc massa, mattis id quam vel, feugiat ultricies massa. Proin fermentum sapien purus, eu rutrum nunc accumsan id. Proin elementum orci eget enim semper fringilla a in nisi. Donec non ligula id ligula facilisis vulputate ac vitae quam. Curabitur placerat pharetra felis, sit amet rhoncus nisl tincidunt at. Pellentesque consequat venenatis laoreet. Integer augue mi, tempus at tincidunt at, finibus non mauris. Nulla accumsan eros metus, quis lobortis magna faucibus at. Aenean efficitur, ipsum at elementum cursus, ex nulla interdum ligula, fringilla dictum mauris elit vitae dolor. Suspendisse bibendum ultrices augue non aliquet.
Morbi nisl elit, pulvinar tristique dapibus commodo, blandit at odio. Integer rhoncus tellus ornare tincidunt fringilla. Morbi id ante metus. Suspendisse potenti. Suspendisse eget dolor aliquet, venenatis nisl quis, dignissim turpis. Vivamus ipsum leo, blandit sodales egestas eget, suscipit eu libero. Pellentesque non molestie diam. Integer quis dui velit.
Pellentesque accumsan lacus molestie purus tincidunt fermentum. Proin auctor ultrices est eu lobortis. Phasellus at eleifend arcu. Mauris sit amet leo arcu. Nullam lobortis laoreet augue ut porta. Ut iaculis sem turpis, sed tempor est laoreet a. Integer tempor dictum nisi id malesuada. Quisque sed lobortis neque, eget tincidunt libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse dui magna, dapibus ac mauris vel, bibendum scelerisque ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam efficitur dictum faucibus. Phasellus tristique, tellus ac sollicitudin cursus, elit diam pulvinar felis, sodales varius leo arcu ultricies diam. Donec quis rutrum leo. Etiam pharetra suscipit nulla fermentum sodales. Nam vitae nisl et erat consectetur sollicitudin.
Duis vehicula erat erat, eu ornare tortor fringilla non. Curabitur sollicitudin, magna eget eleifend eleifend, sapien lectus tempus massa, vitae egestas lacus magna ac lacus. Vivamus eleifend maximus varius. Integer a tempus eros. Morbi tempor dolor mi, in bibendum turpis convallis id. Aenean ut rutrum ligula. Morbi tellus erat, vehicula eu bibendum a, euismod ut neque. Cras id auctor justo. In viverra ornare sem ac pellentesque. Quisque consectetur metus id felis rutrum viverra. Sed semper, odio vel tristique porttitor, erat enim volutpat velit, et sodales nibh tellus tristique enim. Morbi convallis auctor erat egestas aliquam. Phasellus id pellentesque dolor. Cras iaculis lacus consectetur, finibus nibh ut, semper neque.
Vivamus aliquam lectus eget risus posuere, in porta tortor sollicitudin. Mauris scelerisque elit a est convallis ultricies. In tristique arcu vitae tincidunt ultricies. Vivamus efficitur, sem et porttitor lobortis, lorem justo molestie arcu, eget suscipit nibh enim ut velit. Fusce vel vehicula ex. Duis a interdum ipsum, et scelerisque arcu. Integer ut faucibus leo, at molestie eros. Aliquam erat volutpat. Sed congue odio sit amet tempus suscipit.
Donec non orci et est dapibus fermentum. Nulla tristique commodo ligula eu faucibus. Sed volutpat ullamcorper tellus pretium tristique. Vivamus leo tellus, aliquam eget vulputate at, vehicula eu nibh. Integer posuere egestas ipsum quis malesuada. Praesent et mi elit. Maecenas dictum condimentum nisi, in maximus nibh aliquet nec. Morbi vulputate molestie erat sit amet porta. Integer semper dolor sit amet nibh vehicula pellentesque. Donec sagittis, nulla et mattis fermentum, dolor arcu bibendum lectus, ac scelerisque ipsum felis sed ante. Maecenas interdum tellus libero, a pretium sem sodales nec.
Duis sed eros et turpis ultrices tempus vitae id ex. Nunc ex nisi, luctus sit amet felis id, imperdiet laoreet arcu. Ut et dignissim orci, consequat tincidunt nibh. Donec ac erat a neque blandit placerat. Nulla nec quam non mi laoreet congue a sit amet elit. Cras eget rutrum enim. Nulla ultricies lacinia lacus, vitae porttitor nisi tristique sit amet. Pellentesque suscipit mollis nisl, non placerat mauris eleifend ac. Duis diam felis, ornare nec mauris vel, consectetur efficitur orci.
Maecenas hendrerit magna non metus tincidunt pretium. Maecenas euismod dolor eget mattis laoreet. Vestibulum vel diam ultricies, euismod ipsum sed, faucibus velit. Pellentesque eget malesuada ex. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi in magna fringilla lectus scelerisque ullamcorper eu lobortis neque. Phasellus auctor vitae nibh ac auctor. Sed at orci id quam ultricies pellentesque. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus faucibus odio vitae faucibus pulvinar. Quisque tincidunt lacus sed tellus ultrices facilisis.