From 4f9fdc1ebe86dae508d462a77bae561d228ffcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janne=20M=C3=A4ki?= Date: Thu, 19 Jan 2023 17:43:20 +0200 Subject: [PATCH 1/2] Optional configuration for partial analyzing --- .../java/fi/evident/raudikko/Analyzer.java | 2 - .../raudikko/AnalyzerConfiguration.java | 97 +++++++++++++++++++ .../java/fi/evident/raudikko/Morphology.java | 12 ++- .../morphology/FinnishVfstAnalyzer.java | 45 ++++++--- 4 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java diff --git a/src/main/java/fi/evident/raudikko/Analyzer.java b/src/main/java/fi/evident/raudikko/Analyzer.java index b3ebee3..36c64ac 100644 --- a/src/main/java/fi/evident/raudikko/Analyzer.java +++ b/src/main/java/fi/evident/raudikko/Analyzer.java @@ -61,8 +61,6 @@ public interface Analyzer { /** * Analyze given word and return a list of possible interpretations. - * - * At most {@code maxResults} results are returned. */ default @NotNull List baseForms(@NotNull CharSequence word) { List analyses = analyze(word); diff --git a/src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java b/src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java new file mode 100644 index 0000000..2a607c6 --- /dev/null +++ b/src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java @@ -0,0 +1,97 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version + * 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * https://www.mozilla.org/en-US/MPL/2.0/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Libvoikko: Library of natural language processing tools. + * The Initial Developer of the Original Code is Harri Pitkänen . + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Raudikko, the Java port of the Initial Code is Copyright (C) 2020 by + * Evident Solutions Oy. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +package fi.evident.raudikko; + +/** + * Default configuration includes sensible defaults (most of the analysis excluding expensive). + * If this configuration is modified after constructing the analyzer the behaviour is undefined. + */ +public final class AnalyzerConfiguration { + + private boolean includeStructure = true; + private boolean includeBaseForm = true; + private boolean includeBasicAttributes = true; + private boolean includeOrganizationNameAnalysis = true; + private boolean includeFstOutput = true; + + public boolean isIncludeStructure() { + return includeStructure; + } + + public void setIncludeStructure(boolean includeStructure) { + this.includeStructure = includeStructure; + } + + public boolean isIncludeBaseForm() { + return includeBaseForm; + } + + public void setIncludeBaseForm(boolean includeBaseForm) { + this.includeBaseForm = includeBaseForm; + } + + public boolean isIncludeBasicAttributes() { + return includeBasicAttributes; + } + + public void setIncludeBasicAttributes(boolean includeBasicAttributes) { + this.includeBasicAttributes = includeBasicAttributes; + } + + public boolean isIncludeOrganizationNameAnalysis() { + return includeOrganizationNameAnalysis; + } + + public void setIncludeOrganizationNameAnalysis(boolean includeOrganizationNameAnalysis) { + this.includeOrganizationNameAnalysis = includeOrganizationNameAnalysis; + } + + public boolean isIncludeFstOutput() { + return includeFstOutput; + } + + public void setIncludeFstOutput(boolean includeFstOutput) { + this.includeFstOutput = includeFstOutput; + } + + @Override + public String toString() { + return "AnalyzerConfiguration{" + + "includeStructure=" + includeStructure + + ", includeBaseForm=" + includeBaseForm + + ", includeBasicAttributes=" + includeBasicAttributes + + ", includeOrganizationNameAnalysis=" + includeOrganizationNameAnalysis + + ", includeFstOutput=" + includeFstOutput + + '}'; + } +} diff --git a/src/main/java/fi/evident/raudikko/Morphology.java b/src/main/java/fi/evident/raudikko/Morphology.java index 55838f2..78f2fec 100644 --- a/src/main/java/fi/evident/raudikko/Morphology.java +++ b/src/main/java/fi/evident/raudikko/Morphology.java @@ -78,6 +78,16 @@ private Morphology(@NotNull UnweightedTransducer transducer) { * shared between threads. */ public @NotNull Analyzer newAnalyzer() { - return new FinnishVfstAnalyzer(transducer); + return newAnalyzer(new AnalyzerConfiguration()); + } + + /** + * Create a new {@link Analyzer} for this morphology. + * + * The analyzer is a mutable object that can be used repeatedly, but may not be + * shared between threads. + */ + public @NotNull Analyzer newAnalyzer(@NotNull AnalyzerConfiguration configuration) { + return new FinnishVfstAnalyzer(transducer, configuration); } } diff --git a/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java b/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java index 54390fc..de46382 100644 --- a/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java +++ b/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java @@ -34,6 +34,7 @@ import fi.evident.raudikko.Analysis; import fi.evident.raudikko.Analyzer; +import fi.evident.raudikko.AnalyzerConfiguration; import fi.evident.raudikko.internal.fst.Symbol; import fi.evident.raudikko.internal.fst.UnweightedTransducer; import org.jetbrains.annotations.NotNull; @@ -46,6 +47,7 @@ import static fi.evident.raudikko.internal.morphology.Organization.organizationNameAnalysis; import static fi.evident.raudikko.internal.morphology.Structure.parseStructure; import static fi.evident.raudikko.internal.morphology.Validator.isValidAnalysis; +import static java.util.Objects.requireNonNull; public final class FinnishVfstAnalyzer implements Analyzer { @@ -53,12 +55,14 @@ public final class FinnishVfstAnalyzer implements Analyzer { private final @NotNull List inputBuffer = new ArrayList<>(2000); private final @NotNull Symbol[] output = new Symbol[2000]; private final @NotNull SymbolBuffer buffer = new SymbolBuffer(2000); + private final @NotNull AnalyzerConfiguration configuration; private final short[] flags; private static final int MAX_WORD_LENGTH = 255; - public FinnishVfstAnalyzer(@NotNull UnweightedTransducer transducer) { + public FinnishVfstAnalyzer(@NotNull UnweightedTransducer transducer, @NotNull AnalyzerConfiguration configuration) { this.transducer = transducer; this.flags = new short[transducer.flagDiacriticFeatureCount]; + this.configuration = configuration; } @Override @@ -72,7 +76,7 @@ public FinnishVfstAnalyzer(@NotNull UnweightedTransducer transducer) { buffer.reset(output, depth); if (isValidAnalysis(buffer)) - createAnalysis(buffer, word.length(), results); + createAnalysis(buffer, word.length(), results, configuration); }); return results; @@ -100,20 +104,37 @@ public FinnishVfstAnalyzer(@NotNull UnweightedTransducer transducer) { return results; } - private static void createAnalysis(@NotNull SymbolBuffer buffer, int wordLength, @NotNull List results) { - String structure = parseStructure(buffer, wordLength); - + private static void createAnalysis(@NotNull SymbolBuffer buffer, + int wordLength, + @NotNull List results, + @NotNull AnalyzerConfiguration configuration) { Analysis analysis = new Analysis(); - analysis.setStructure(structure); - analysis.setFstOutput(buffer.fullContents()); - analysis.setBaseForm(parseBaseform(buffer, structure)); - parseBasicAttributes(analysis, buffer); + boolean dependsOnStructure = + configuration.isIncludeStructure() + || configuration.isIncludeBaseForm() + || configuration.isIncludeOrganizationNameAnalysis(); + + String structure = dependsOnStructure ? parseStructure(buffer, wordLength) : null; + + if (configuration.isIncludeStructure()) + analysis.setStructure(requireNonNull(structure)); + + if (configuration.isIncludeBaseForm()) + analysis.setBaseForm(parseBaseform(buffer, requireNonNull(structure))); + + if (configuration.isIncludeFstOutput()) + analysis.setFstOutput(buffer.fullContents()); + + if (configuration.isIncludeBasicAttributes()) + parseBasicAttributes(analysis, buffer); results.add(analysis); - Analysis organizationNameAnalysis = organizationNameAnalysis(analysis, buffer, structure); - if (organizationNameAnalysis != null) - results.add(organizationNameAnalysis); + if (configuration.isIncludeOrganizationNameAnalysis()) { + Analysis organizationNameAnalysis = organizationNameAnalysis(analysis, buffer, requireNonNull(structure)); + if (organizationNameAnalysis != null) + results.add(organizationNameAnalysis); + } } } From fb25cc35984f9ebeb5ac0df112620b7897c5d418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janne=20M=C3=A4ki?= Date: Thu, 19 Jan 2023 17:46:33 +0200 Subject: [PATCH 2/2] Base form parts analysis --- .../java/fi/evident/raudikko/Analysis.java | 11 + .../raudikko/AnalyzerConfiguration.java | 10 + .../internal/morphology/BaseForm.java | 19 + .../morphology/FinnishVfstAnalyzer.java | 4 + .../integration/BaseFormPartsTest.java | 141 ++ src/test/resources/base-form-parts-test.txt | 1170 +++++++++++++++++ 6 files changed, 1355 insertions(+) create mode 100644 src/test/java/fi/evident/raudikko/integration/BaseFormPartsTest.java create mode 100644 src/test/resources/base-form-parts-test.txt diff --git a/src/main/java/fi/evident/raudikko/Analysis.java b/src/main/java/fi/evident/raudikko/Analysis.java index c1cc2d3..40579bb 100644 --- a/src/main/java/fi/evident/raudikko/Analysis.java +++ b/src/main/java/fi/evident/raudikko/Analysis.java @@ -36,6 +36,7 @@ import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -64,6 +65,7 @@ public final class Analysis implements Cloneable { private boolean malagaVapaaJalkiosa = false; private boolean possibleGeographicalName = false; private @Nullable String requireFollowingVerb; + private @Nullable List baseFormParts; public @Nullable String getBaseForm() { return baseForm; @@ -209,6 +211,14 @@ public void setRequireFollowingVerb(@Nullable String requireFollowingVerb) { this.requireFollowingVerb = requireFollowingVerb; } + public void setBaseFormParts(@Nullable List baseFormParts) { + this.baseFormParts = baseFormParts; + } + + public @Nullable List getBaseFormParts() { + return baseFormParts; + } + @Override public @NotNull Analysis clone() { try { @@ -277,6 +287,7 @@ public String toString() { ", malagaVapaaJalkiosa=" + malagaVapaaJalkiosa + ", possibleGeographicalName=" + possibleGeographicalName + ", requireFollowingVerb='" + requireFollowingVerb + '\'' + + ", baseFormParts='" + baseFormParts + '\'' + '}'; } } diff --git a/src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java b/src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java index 2a607c6..db3779c 100644 --- a/src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java +++ b/src/main/java/fi/evident/raudikko/AnalyzerConfiguration.java @@ -43,6 +43,7 @@ public final class AnalyzerConfiguration { private boolean includeBasicAttributes = true; private boolean includeOrganizationNameAnalysis = true; private boolean includeFstOutput = true; + private boolean includeBaseFormParts = true; public boolean isIncludeStructure() { return includeStructure; @@ -84,6 +85,14 @@ public void setIncludeFstOutput(boolean includeFstOutput) { this.includeFstOutput = includeFstOutput; } + public boolean isIncludeBaseFormParts() { + return includeBaseFormParts; + } + + public void setIncludeBaseFormParts(boolean includeBaseFormParts) { + this.includeBaseFormParts = includeBaseFormParts; + } + @Override public String toString() { return "AnalyzerConfiguration{" + @@ -92,6 +101,7 @@ public String toString() { ", includeBasicAttributes=" + includeBasicAttributes + ", includeOrganizationNameAnalysis=" + includeOrganizationNameAnalysis + ", includeFstOutput=" + includeFstOutput + + ", includeBaseFormParts=" + includeBaseFormParts + '}'; } } diff --git a/src/main/java/fi/evident/raudikko/internal/morphology/BaseForm.java b/src/main/java/fi/evident/raudikko/internal/morphology/BaseForm.java index f84bd85..25af2b2 100644 --- a/src/main/java/fi/evident/raudikko/internal/morphology/BaseForm.java +++ b/src/main/java/fi/evident/raudikko/internal/morphology/BaseForm.java @@ -36,6 +36,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; + import static fi.evident.raudikko.internal.utils.StringUtils.*; import static java.lang.Character.isDigit; import static java.lang.Character.toUpperCase; @@ -165,6 +168,22 @@ else if (!classTagSeen && tag.matches(Tags.lu)) { return baseform.toString(); } + static @NotNull List parseBaseFormParts(@NotNull SymbolBuffer tokenizer) { + List parts = new ArrayList<>(); + tokenizer.moveToStart(); + + while (tokenizer.nextToken()) { + Symbol tag = tokenizer.getCurrentTag(); + if (tag != null && tag.matches(Tags.xp)) { + String part = withoutChar(tokenizer.readXTagContents(), '='); + if (!part.isEmpty()) { + parts.add(part); + } + } + } + return parts; + } + private static final class StructureIterator { private final @NotNull String structure; diff --git a/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java b/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java index de46382..3edba18 100644 --- a/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java +++ b/src/main/java/fi/evident/raudikko/internal/morphology/FinnishVfstAnalyzer.java @@ -42,6 +42,7 @@ import java.util.ArrayList; import java.util.List; +import static fi.evident.raudikko.internal.morphology.BaseForm.parseBaseFormParts; import static fi.evident.raudikko.internal.morphology.BaseForm.parseBaseform; import static fi.evident.raudikko.internal.morphology.BasicAttributes.parseBasicAttributes; import static fi.evident.raudikko.internal.morphology.Organization.organizationNameAnalysis; @@ -126,6 +127,9 @@ private static void createAnalysis(@NotNull SymbolBuffer buffer, if (configuration.isIncludeFstOutput()) analysis.setFstOutput(buffer.fullContents()); + if (configuration.isIncludeBaseFormParts()) + analysis.setBaseFormParts(parseBaseFormParts(buffer)); + if (configuration.isIncludeBasicAttributes()) parseBasicAttributes(analysis, buffer); diff --git a/src/test/java/fi/evident/raudikko/integration/BaseFormPartsTest.java b/src/test/java/fi/evident/raudikko/integration/BaseFormPartsTest.java new file mode 100644 index 0000000..335a20d --- /dev/null +++ b/src/test/java/fi/evident/raudikko/integration/BaseFormPartsTest.java @@ -0,0 +1,141 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version + * 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * https://www.mozilla.org/en-US/MPL/2.0/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Libvoikko: Library of natural language processing tools. + * The Initial Developer of the Original Code is Harri Pitkänen . + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Raudikko, the Java port of the Initial Code is Copyright (C) 2020 by + * Evident Solutions Oy. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +package fi.evident.raudikko.integration; + +import fi.evident.raudikko.Analysis; +import fi.evident.raudikko.Analyzer; +import fi.evident.raudikko.AnalyzerConfiguration; +import fi.evident.raudikko.Morphology; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; + +@TestInstance(PER_CLASS) +public class BaseFormPartsTest { + + private final @NotNull Morphology morphology = Morphology.loadBundled(); + private final @NotNull Analyzer analyzer = morphology.newAnalyzer(createConfiguration()); + + private static AnalyzerConfiguration createConfiguration() { + AnalyzerConfiguration configuration = new AnalyzerConfiguration(); + configuration.setIncludeBaseFormParts(true); + configuration.setIncludeStructure(false); + configuration.setIncludeBaseForm(false); + configuration.setIncludeFstOutput(false); + configuration.setIncludeOrganizationNameAnalysis(false); + configuration.setIncludeBasicAttributes(false); + return configuration; + } + + @ParameterizedTest(name = "{0}") + @MethodSource("testData") + void baseFormPartsTest(@NotNull BaseFormPartsTest.WordTest data) { + List analyze = analyzer.analyze(data.word); + + assertEquals(data.expectedBaseFormParts.size(), analyze.size()); + + for (int i = 0; i < data.expectedBaseFormParts.size(); i++) { + List expected = data.expectedBaseFormParts.get(i); + List actual = analyze.get(i).getBaseFormParts(); + assertEquals(expected, actual); + } + } + + private static @NotNull Stream testData() throws IOException { + try (InputStream in = BaseFormPartsTest.class.getResourceAsStream("/base-form-parts-test.txt")) { + assertNotNull(in, "could not find base form parts test data"); + + List lines = new BufferedReader(new InputStreamReader(in, UTF_8)).lines() + .map(String::trim) + .filter(it -> !it.isEmpty()) + .collect(Collectors.toList()); + + BaseFormPartsTest.WordTest current = null; + + List result = new ArrayList<>(); + + Pattern splitPattern = Pattern.compile(","); + + for (String line : lines) { + if (line.startsWith("word:")) { + current = new BaseFormPartsTest.WordTest(line.substring("word:".length())); + result.add(current); + } else if (current == null) { + fail(); + } else { + if (line.startsWith("[") && line.endsWith("]")) { + ArrayList parts = new ArrayList<>(); + for (String part : splitPattern.split(line.substring(1, line.length() - 1))) { + if (!part.isEmpty()) { + parts.add(part); + } + } + current.expectedBaseFormParts.add(parts); + } else { + fail("unknown line: '" + line + "'"); + } + } + } + + return result.stream(); + } + } + + private static final class WordTest { + public final @NotNull String word; + public final @NotNull List> expectedBaseFormParts = new ArrayList<>(); + + WordTest(@NotNull String word) { + this.word = word; + } + + @Override + public String toString() { + return word; + } + } +} diff --git a/src/test/resources/base-form-parts-test.txt b/src/test/resources/base-form-parts-test.txt new file mode 100644 index 0000000..a8ca204 --- /dev/null +++ b/src/test/resources/base-form-parts-test.txt @@ -0,0 +1,1170 @@ +word:kissa + [kissa] +word:kuorma-auto + [kuorma,auto] +word:koirra +word:EU + [EU] +word:alusta + [alku] + [alus] + [alunen] + [alusta] + [alustaa] +word:kissan + [kissa] +word:kissojen + [kissa] +word:kissaa + [kissa] +word:meidät + [me] +word:kissana + [kissa] +word:kissaksi + [kissa] +word:kissassa + [kissa] +word:kissasta + [kissa] +word:kissaan + [kissa] +word:kissalla + [kissa] +word:kissalta + [kissa] +word:kissalle + [kissa] +word:kissatta + [kissa] +word:kissoineni + [kissa] +word:kissoin + [kissa] +word:nopeasti + [nopea] +word:juosta + [juosta] + [juosta] +word:nopea + [nopea] +word:hyvin + [hyvä] + [hyvin] +word:taistolainen + [taistolainen] +word:joka + [joka] +word:jotka + [joka] +word:ohi + [ohi] +word:hei + [hei] +word:eli + [elää] + [eli] +word:Tiina + [Tiina] +word:Pitkänen + [Pitkänen] +word:Helsinki + [Helsinki] +word:Neptunus + [Neptunus] +word:ei + [ei] +word:kymmenen + [kymmenen] + [kymmenen] + [kymmen] +word:kymmenet + [kymmenen] + [kymmen] +word:kaksikymmentä + [kaksi] +word:kahtakymmentä + [kaksi,kymmentä] +word:kaksikymmentäyksi + [kaksi,yksi] +word:10 + [] +word:kissat + [kissa] +word:juoksen + [juosta] +word:olen + [olla] +word:juokset + [juosta] +word:juoksee + [juosta] +word:juostaan + [juosta] +word:juoksisin + [juosta] +word:juokse + [juosta] +word:juossee + [juosta] +word:juoksemassa + [juosta] + [juosta] +word:juoksemassamme + [juosta] +word:juostakin + [juosta] + [juosta] +word:juostessa + [juosta] +word:juoksemasta + [juosta] + [juosta] +word:juoksemaan + [juosta] + [juosta] +word:verohallitus + [vero,hallitus] +word:Kaakkois-Panama + [Panama] +word:Suur-Panama + [Panama] +word:Sydän-Suomi + [sydän,Suomi] +word:idänkauppa + [itä,kauppa] +word:hienoittasokereitta + [hienosokeri] +word:hienoinsokerein + [hienosokeri] +word:sana + [sana] +word:relatiivipronomini + [relatiivipronomini] +word:hyppely + [hypellä] +word:ilmaisu + [ilmaista] +word:Pohjois- + [Pohjois] + [pohjoinen] +word:sydän- + [Sydän] + [sydän] +word:Keski-Suomessa + [Suomi] +word:sallitaan + [sallia] +word:sallita + [sallia] +word:sallitakaan + [sallia] +word:sallitaankaan + [sallia] +word:maalaa + [maalata] +word:haravoi + [haravoida] + [haravoida] + [haravoida] +word:maalaakin + [maa,laaki] + [maalata] +word:maalaakaan + [maalata] +word:juoksepa + [juosta] +word:juoksehan + [juosta] +word:juoskoot + [juosta] +word:olla + [olla] +word:luopua + [luopua] +word:mainittaisiin + [mainita] +word:mainittaisi + [mainita] +word:sämpyläjauho + [sämpylä,jauho] +word:sämpyläjoki + [sämpylä,joki] +word:sämpyläjoella + [sämpylä,joki] +word:sämpyläjoellakin + [sämpylä,joki] +word:Välimerta + [Välimeri] + [väli,merta] + [väli,meri] +word:sämpyläjokilaiva + [sämpylä,joki,laiva] +word:haluan + [haluta] +word:ryhdymme + [ryhtyä] +word:juoksemme + [juosta] +word:minä + [minä] + [mikä] + [minä] +word:sinä + [se] + [sinä] +word:hän + [hän] +word:me + [me] +word:te + [te] +word:he + [he] +word:kovalevy + [kova,levy] +word:kovakalloinen + [kova,kallo] +word:harmaapikasuolattu + [harmaa,suolata] +word:leikkivä + [leikkiä] +word:leikkinyt + [leikkiä] +word:piikkinen + [piikki] +word:piikitön + [piikki] +word:piikkimäinen + [piikki] +word:piikeittäin + [piikki] +word:piikkisyys + [piikki] +word:ruotsalainen + [ruotsalainen] +word:ruotsalainenkin + [ruotsalainen] +word:ruotsalaisessa + [ruotsalainen] +word:agraariruotsalainen + [agraari,ruotsalainen] +word:juoksemiseen + [juosta] +word:superhämärästi + [hämärä] +word:tapakristitty + [tapa,kristitty] +word:härkäjuoksijana + [härkä,juosta] +word:kissanvärinen + [kissa,väri] +word:ajattelukykyinen + [ajatella,kyky] +word:pikajuoksukykyinen + [juoksu,kyky] +word:superbiologinen + [biologi] +word:ahdashenkinen + [ahdas,henki] +word:ruotsalaishenkinen + [ruotsalainen,henki] +word:eräs + [eräs] +word:jotta + [jotta] +word:montenegrolainen + [Montenegro] +word:iiii + [] + [] +word:jottei + [jott,ei] +word:Uudenmaansa + [Uusimaa] +word:hyttynen + [hyttynen] +word:hyttysen + [hyttynen] +word:hyttystä + [hyttynen] +word:hyttysessä + [hyttynen] +word:hyttysestä + [hyttynen] +word:hyttyseen + [hyttynen] +word:hyttysellä + [hyttynen] +word:hyttyseltä + [hyttynen] +word:hyttyselle + [hyttynen] +word:ampiaiset + [ampiainen] +word:lapinkirvinen + [lapinkirvinen] +word:Pokkinen + [Pokkinen] +word:Nousiainen + [Nousiainen] +word:Alfred + [Alfred] +word:Kanteletar + [Kanteletar] +word:keltainen + [keltainen] +word:arktisuus + [arktinen] +word:pottusyltty + [pottu,syltty] +word:vieras + [vieras] +word:katsella + [katsella] + [katsella] +word:katselen + [katsella] +word:katselet + [katsella] +word:katselee + [katsella] +word:katselemme + [katsella] +word:katselette + [katsella] +word:lunastetaan + [lunastaa] +word:adverbisija + [adverbi,sija] +word:Pötsönlahdella + [Pötsönlahti] +word:katselemassa + [katsella] + [katsella] +word:katsellessa + [katsella] +word:katselisin + [katsella] + [katse,lisä] +word:pahoinpiteli + [pahoinpidellä] +word:hinaajalla + [hinata] +word:Suomi-Sveitsi + [suomi,Sveitsi] + [Suomi,Sveitsi] +word:CD-ROM + [CD-ROM] + [cd-rom] + [cd-rom] + [CD-ROM] +word:anonyymisti + [anonyymi] +word:autonikin + [auto] +word:talosikaan + [talo,sika] + [talo] +word:talomme + [talo] +word:talonne + [talo] +word:talonsa + [talo] +word:-legenda + [legenda] +word:jäähdytetään + [jäähdyttää] +word:jäähdytettiin + [jäähdyttää] +word:muisto + [muistaa] +word:epäröin + [epäröidä] + [epäröidä] +word:kommentoikaa + [kommentoida] +word:olemme + [olla] +word:kielteisemmin + [kielteinen] +word:kielteisimmin + [kielteinen] +word:lienen + [olla] +word:liene + [olla] +word:älkööt + [ei] +word:kukistaneen + [kukistaa] +word:ab + [ab] +word:ab-kissa + [ab,kissa] +word:esiintyjittäin + [esiintyä] +word:espanjalaisittain + [Espanja] +word:Helsinki-alueellinen + [Helsinki,alue] +word:roomalaiskissa + [Rooma,kissa] +word:kissattomuus + [kissa] +word:ei-kenenkään-maa + [ei-kenenkään-maa] +word:yksi + [yksi] +word:kaksituhattakolmetoista + [kaksi,kolme] +word:kahdeksaakymmentä + [kahdeksan,kymmentä] +word:myyty + [myydä] +word:minut + [minä] +word:Aadolfinkatu + [Aadolf,katu] +word:Suur-Sveitsi + [Sveitsi] +word:asuin-Sveitsi + [Sveitsi] +word:juoksekaan + [juosta] +word:neljännentoista + [neljäs] +word:parhaassa + [hyvä] +word:ja + [ja] +word:Gallen-Kallela-Sirén + [Gallen-Kallela,Sirén] +word:-sävellys- + [säveltää] +word:ilman + [Ilma] + [ilman] + [ilma] +word:suomalais-neuvostoliittolainen + [suomalainen,neuvosto,liittolainen] + [suomalainen,Neuvostoliitto] +word:Isossa-Britanniassa + [iso,Britannia] +word:Uusimaa + [uusia] + [Uusimaa] +word:Keski-Uusimaa + [Uusimaa] +word:Tyynessämeressä + [Tyynimeri] +word:huhti-toukokuusta + [huhti-toukokuu] +word:viisivuotiskausi + [viisi,vuosi,kausi] +word:AIDS-epidemia + [AIDS,epidemia] +word:vammaisuusaste + [vamma,aste] +word:-nimen + [nimi] +word:Euro-sarja + [sarja] +word:tulee + [tulla] +word:saa + [saada] + [saada] +word:julkaistu + [julkaista] +word:julkaistiin + [julkaista] +word:tekijä + [tehdä] +word:urallaan + [ura] +word:käsittelee + [käsitellä] +word:yhdistää + [yhdistää] + [yhdistää] +word:pääsee + [päästä] +word:koiralla + [koira] +word:koirallako + [koira] +word:eikö + [ei] +word:koirallakin + [koira] +word:koirallakaan + [koira] +word:juoksin + [juosta] +word:juokseva + [juosta] +word:juostava + [juosta] +word:metsästettävä + [metsästää] +word:juossut + [juosta] +word:juosseet + [juosta] +word:juostu + [juosta] +word:juoksema + [juosta] +word:juoksematon + [juosta] +word:toimii + [toimia] +word:haluavatkin + [haluta] + [haluta] +word:vähentävät + [vähetä] + [vähetä] +word:jakavat + [jakaa] + [jakaa] +word:aikakauslehti + [lehti] +word:ADSL:n + [ADSL] +word:kokevat + [kokea] + [kokea] +word:itarin + [itara] + [itara] +word:herännäis-kissa + [herännäinen,kissa] +word:nelisormimangusti + [neli,sormi,mangusti] +word:neutraalimpaan + [neutraali] +word:nimeämiskäytännöstä + [nimetä,käytäntö] +word:muokattaisi + [muokata] +word:mutkistuu + [mutkistaa] +word:varo-EU + [EU] +word:alkeissidosta + [sidos] +word:alavireisyyttään + [ala,vire] +word:suunnittelijoita + [suunnitella] +word:avuttomasti + [apu] + [avu] +word:talottomampi + [talo] +word:rakennus + [rakennus] +word:palveluita + [palvella] +word:arkistoinnin + [arkistoida] +word:EU-kieli + [EU,kieli] +word:lento + [lentää] +word:sortoa + [sortaa] +word:laulanta + [laulaa] +word:avaruudellinen + [avara] +word:byrokraattistua + [byrokraattistaa] +word:avoselkäinen + [selkä] +word:esijuoksija + [juosta] +word:elämänviisaus + [elämä,viisas] +word:erehtyväinen + [erehtyä] +word:hallitusti + [hallita] +word:hajakylvö + [kylvää] +word:eteläamerikkalainen + [etelä,Amerikka] +word:moottoroitu + [moottoroida] +word:käyttö + [käyttää] +word:kaavoittuma + [kaavoittua] +word:öykkärimäisesti + [öykkäri] +word:HI-virus + [HI-virus] +word:örinä + [öristä] +word:diivailen + [diivata] +word:haukottelee + [haukottaa] +word:havaittavassa + [havaita] +word:halaistua + [halaista] +word:hyödyntäneen + [hyödyntää] +word:Hanna-Liisa + [Hanna,Liisa] +word:hiihtojen + [hiihtää] +word:jättöä + [jättää] +word:jäljittääkseen + [jäljittää] +word:astahtelen + [astahtaa] +word:kastele + [kastaa] +word:luovalle + [luoda] +word:kontti- + [kontti] +word:-kissakoira + [kissa,koira] +word:kuulemat + [kuulla] +word:muistu + [muistaa] +word:lätkäisseessä + [lätkäistä] +word:valvoessaan + [valvoa] +word:kuuntelisi + [kuunnella] +word:kaksijakoisesti + [kaksi,jako] +word:ionisoituu + [ionisoida] +word:näkeekö + [nähdä] +word:nujersivat + [nujertaa] +word:nimekseen + [nimi] +word:muistelleet + [muistaa] +word:johtajattaren + [johtaa] +word:lähentyy + [lähetä] +word:lähennän + [lähetä] +word:lähentelen + [lähetä] +word:rumuus + [ruma] +word:käsiteltyjen + [käsitellä] +word:pyrimme + [pyrkiä] + [pyrkiä] +word:putoili + [pudota] +word:pysähtyneisyys + [pysähtyä] +word:signeeraa + [signeerata] +word:peittoaa + [peitota] +word:otettakoon + [ottaa] +word:tekisikö + [tehdä] +word:kavensivat + [kaveta] +word:simpukkamurros + [simpukka,murtaa] +word:tallentelen + [tallentaa] +word:tallentuu + [tallentaa] +word:linnoittautuminen + [linnoittaa] +word:nyhdätän + [nyhtää] +word:linnoittautumisensa + [linnoittaa] +word:suppenevat + [supeta] + [supeta] +word:pukemalla + [pukea] + [pukea] +word:täräytti + [täristä] +word:täräytyksen + [täristä] +word:keventämällä + [kevetä] + [kevetä] +word:kanteleeni + [kannel] + [kannel] +word:rikkoneensa + [rikkoa] +word:johtavuus + [johtaa] +word:lehtikultaa + [lehti,kulta] +word:peruskoulukone + [perus,koulu,kone] +word:Pohjois-Texas + [pohjoinen,Texas] +word:murtaakseen + [murtaa] +word:haennoiden + [hakea] +word:liiteli + [liitää] +word:kunnan- + [kunta] +word:kanavittain + [kanava] +word:musiikkioppilaitos + [musiikki,oppi,laitos] +word:marxismi-leninismin + [marxismi-leninismi] +word:väännös + [väännös] +word:pahakseni + [paha] +word:köyhälleen + [köyhä] +word:huutelevat + [huutaa] + [huutaa] +word:aterioi + [aterioida] + [aterioida] + [aterioida] +word:lähdöllä + [lähtö] +word:seitsemästoista + [seitsemäs] +word:kääntelen + [kääntää] +word:soveltelen + [soveltaa] +word:nakertelen + [nakertaa] +word:jäykentelen + [jäykentää] +word:kylittäin + [kylä] +word:ohjattuaan + [ohjata] +word:juostaman + [juosta] +word:kirjailijanimi + [kirjata,nimi] +word:kerrostalo- + [kerros,talo] +word:tarjokkuus + [tarjokas] +word:keskelläkin + [keskellä] +word:rikas- + [rikas] +word:kuljeskelee + [kulkea] +word:komentajuudesta + [komentaa] +word:muttet + [mutt,ei] +word:kohtauksittain + [kohdata] +word:kulta-aarre + [kulta,aarre] +word:rajoittuneisuus + [rajoittua] +word:opiskelleensa + [opiskella] +word:käyttökelvoton + [käyttää,kelpo] +word:mielenterveydellinen + [mieli,terve] +word:juoksekin + [juosta] +word:vesitilavuus + [vesi,tilava] +word:heidänlaisensa + [heidänlainen] +word:puhuu + [puhua] +word:puhuman + [puhua] + [puhua] +word:kalinassa + [kalista] +word:piirteli + [piirtää] +word:koskapa + [koska] +word:kannatan + [kantaa] +word:vaihtelisi + [vaihtaa] +word:huhuiltiin + [huhuta] +word:purrussa + [purra] +word:lentelivät + [lentää] +word:kolahti + [kolista] +word:kanneltiin + [kantaa] +word:saatellaan + [saattaa] +word:ammutti + [ampua] +word:VI:ssa + [] +word:VI:t + [] +word:jaata + [jaa] +word:pantua + [panna] +word:taitaapi + [taitaa] +word:parhaimmat + [hyvä] +word:syöksee + [syöstä] +word:hänetkin + [hän] +word:haltijatar + [haltija] +word:osaamis- + [osata] +word:aita- + [aita] +word:Jack- + [Jack] +word:avo- + [] +word:-vuotias + [vuotias] +word:XX- + [] +word:live- + [] +word:viideksitoista + [viisi] +word:kolmehenkisessä + [kolme,henki] +word:toistasataa + [toista,sataa] +word:parisenkymmentä + [parisen,kymmentä] +word:kymmeniätuhansia + [kymmenen,tuhatta] +word:etkä + [ei] +word:sinfoniaansa + [sinfonia] + [sinfonia] +word:kuutisensataa + [kuutisen,sataa] +word:menikään + [mennä] +word:koe-eläin + [koe,eläin] +word:miljardeista + [miljardi] +word:kahdeksisadaksi + [kaksi,sataa] +word:satojatuhansia + [sata,tuhatta] +word:olemattomien + [olla] +word:älähän + [ei] +word:ettet + [ett,ei] +word:-1600-luvun + [luku] +word:EU:kin + [EU] +word:kokoelma-LP + [kokoelma,LP] +word:j + [j] +word:varapj. + [varapj.] +word:voitane + [voida] +word:meikä-r + [meikä,r] +word:200:sta + [] +word:-200:sta + [] +word:käymisteitse + [käydä,tie] +word:kissankarvoittain + [kissa,karva] +word:koskeekaan + [koskea] +word:hyppäänkään + [hypätä] +word:kissankeltainen + [kissa,keltainen] +word:kolmisen + [kolmisen] +word:hämäräaktiivinen + [hämärä,aktiivi] +word:asiakastyytyväisyyttä + [asiakas,tyytyä] +word:ratakorkeus + [rata,korkea] +word:esikarsinta + [karsia] +word:alikehittynyt + [kehittyä] +word:ylinouseva + [nousta] +word:uudelleenmuotoiltu + [muotoilla] +word:juoksemisestapa + [juosta] +word:hätäistään + [hätä] +word:kpl:in + [kpl] + [kpl] +word:arktis-alpiininen + [arktinen,alpiininen] +word:III-divisioona + [divisioona] +word:EU-laitos + [EU,laitos] +word:Kissalaitos + [kissa,laitos] +word:-kissalaitos + [kissa,laitos] +word:kymmenennen + [kymmenes] +word:suosituimmuusaseman + [suosia,asema] +word:voipi + [voida] +word:väylänopeus + [väylä,nopea] +word:pieniniemisyys + [pieni,niemi] +word:mennyllä + [mennä] +word:viisi- + [viisi] +word:kissavanhempiaan + [kissa,vanhempi] +word:ryhtymättä + [ryhtyä] + [ryhtyä] +word:kahdenkeskistä + [kaksi,keskinen] +word:kolmen- + [kolme] +word:nopeustutkassa + [nopea,tutka] +word:pikilappu + [pikilappu] +word:akveduktien + [akvedukti] +word:historiallinen + [historia] +word:kissattomat + [kissa] +word:etätyö + [työ] +word:aktivoidumme + [aktivoida] +word:juoksija + [juosta] +word:-kentällisessä + [kenttä] +word:suurempikokoinen + [suuri,koko] +word:sadasosasekunti + [sadas,osa,sekunti] +word:merkittävyyskriteeri + [merkitä,kriteeri] +word:yllättäenkin + [yllättää] +word:oltiin + [olla] +word:oletkaan + [olla] +word:olemmekaan + [olla] +word:saakaan + [saada] + [saada] +word:mitkään + [mikä] +word:olkoonkin + [olla] +word:kolmesti + [kolme] +word:kolmikymmenvuotias + [kolmikymmen,vuotias] +word:syötyään + [syödä] +word:kuunneltuamme + [kuunnella] +word:saapas + [saada] + [saada] + [saapas] +word:XXVII + [] + [] +word:romahtamaisillaan + [romahtaa] +word:tunnettuus + [tuntea] +word:hallittavuus + [hallita] +word:edustavuuden + [edustaa] +word:ollut + [olla] + [olla] +word:mitään + [mikään] +word:purrut + [purra] + [purra] +word:kunkin + [kukin] +word:juoksinkaan + [juosta] +word:olekaan + [olla] +word:Kanta- + [Kanta] + [kanta] +word:juoksemattakin + [juosta] + [juosta] +word:olematta + [olla] + [olla] +word:tuta + [tuta] +word:tutaan + [tuta] +word:Itäkissa-Sveitsi + [itä,kissa,Sveitsi] +word:Uudessa-Seelannissa + [uusi,Seelanti] +word:Etelä-Pentti + [etelä,Pentti] +word:Pohjois-Suomessa + [pohjoinen,suomi] + [pohjoinen,Suomi] +word:heinäisensä + [heinä] +word:heinäisempänsä + [heinä] +word:heinäisimpänsä + [heinä] +word:sairastumisriski + [sairastaa,riski] +word:-litraisilla + [litra] +word:hullunkurisia + [hullu,kuri] +word:ajantasaisuus + [aika,tasainen] +word:aihekokonaisuus + [aihe,kokonainen] +word:ilmoituskokonaisuus + [ilmoittaa,kokonainen] +word:juoksijakokonaisuus + [juosta,kokonainen] +word:kerrostalokokonaisuus + [kerros,talo,kokonainen] +word:turkulaiskokonaisuus + [Turku,kokonainen] +word:Matti-kokonaisuus + [Matti,kokonainen] +word:englantilaistyyppinen + [Englanti,tyyppi] +word:Matti-tyyppinen + [Matti,tyyppi] +word:lähtivät + [lähteä] +word:kehitellyn + [kehittää] +word:miellyttämishaluinen + [miellyttää,halu] +word:tapauskohtainen + [tavata,kohta] +word:epävarmuuden + [varma] +word:asiantuntijuus + [asia,tuntea] +word:asianmyyjyys + [asia,myydä] +word:kolmenmallisessa + [kolme,mallinen] +word:kolmenmallinen + [kolme,mallinen] +word:isovanhemmista + [iso,vanhempi] + [iso,vanha] +word:ikäisekseen + [ikä] +word:tyypillisimmillään + [tyyppi] +word:parhaakseen + [hyvä] +word:Isolla-Britannialla + [iso,Britannia] +word:yhteiskuntapoliittinen + [yhteinen,kunta,poliittinen] + [kunta,poliittinen] +word:ollaksemme + [olla] +word:Lounais- + [] + [lounas] +word:Keski- + [keski] + [] +word:METSÄLUOTEIS- + [metsä,luode] +word:muuanna + [muuan] +word:saartaa + [saartaa] + [saartaa] +word:saartavat + [saartaa] + [saartaa] +word:tyhmähkö + [tyhmä] +word:ummikoin + [ummikko] + [ummikko] +word:21. + [] \ No newline at end of file