Bug 568167: [RJ-Data] Improve implementation of factor stores

  - Add unit tests
  - Add additional constructors

Change-Id: If4384a6e62c67bf15d459d3259ffcfa26a1bee38
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactor32StoreSerTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactor32StoreSerTest.java
new file mode 100644
index 0000000..0e068eb
--- /dev/null
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactor32StoreSerTest.java
@@ -0,0 +1,36 @@
+/*=============================================================================#
+ # Copyright (c) 2020 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.rj.data.impl;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.rj.data.RFactorStore;
+
+
+@NonNullByDefault
+public class RFactor32StoreSerTest extends RFactor32StoreTest {
+	
+	
+	public RFactor32StoreSerTest() {
+	}
+	
+	
+	@Override
+	protected RFactorStore createStore(final FactorCaseData data) {
+		return createSerStore((RFactor32Store)super.createStore(data),
+				RFactor32Store::new );
+	}
+	
+}
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactor32StoreTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactor32StoreTest.java
new file mode 100644
index 0000000..a383883
--- /dev/null
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactor32StoreTest.java
@@ -0,0 +1,41 @@
+/*=============================================================================#
+ # Copyright (c) 2020 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.rj.data.impl;
+
+import java.util.Arrays;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.rj.data.RFactorStore;
+
+
+@NonNullByDefault
+public class RFactor32StoreTest extends RFactorStoreTest {
+	
+	
+	public RFactor32StoreTest() {
+	}
+	
+	
+	@Override
+	protected RFactorStore createStore(final FactorCaseData data) {
+		return new RFactor32Store(
+				Arrays.copyOf(data.codes, data.length),
+				Arrays.copyOf(data.nas, data.length),
+				data.ordered,
+				Arrays.copyOf(data.levels, data.levels.length) );
+	}
+	
+}
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactorFix64StoreTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactorFix64StoreTest.java
new file mode 100644
index 0000000..70e2679
--- /dev/null
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactorFix64StoreTest.java
@@ -0,0 +1,51 @@
+/*=============================================================================#
+ # Copyright (c) 2020 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.rj.data.impl;
+
+import static org.eclipse.statet.rj.data.impl.RIntegerFix64Store.SEGMENT_LENGTH;
+
+import java.util.Arrays;
+
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.rj.data.RFactorStore;
+
+
+@NonNullByDefault
+public class RFactorFix64StoreTest extends RFactorStoreTest {
+	
+	
+	public RFactorFix64StoreTest() {
+	}
+	
+	
+	@Override
+	protected RFactorStore createStore(final FactorCaseData data) {
+		final int nSegments= getSegmentCount(data, SEGMENT_LENGTH);
+		final var values= new int [nSegments] @NonNull[];
+		final var nas= new boolean [nSegments] @NonNull[];
+		for (int i= 0, start= 0; i < values.length; i++) {
+			final int end= Math.min(start + SEGMENT_LENGTH, data.length);
+			values[i]= Arrays.copyOfRange(data.codes, start, end);
+			nas[i]= Arrays.copyOfRange(data.nas, start, end);
+			start= end;
+		}
+		return new RFactorFix64Store(values, nas,
+				data.ordered,
+				Arrays.copyOf(data.levels, data.levels.length) );
+	}
+	
+}
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactorStoreTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactorStoreTest.java
new file mode 100644
index 0000000..807cd42
--- /dev/null
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RFactorStoreTest.java
@@ -0,0 +1,349 @@
+/*=============================================================================#
+ # Copyright (c) 2020 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.rj.data.impl;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import static org.eclipse.statet.rj.data.impl.AbstractRStore.DEFAULT_LONG_DATA_SEGMENT_LENGTH;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.rj.data.RFactorStore;
+import org.eclipse.statet.rj.data.RObject;
+import org.eclipse.statet.rj.data.RStore;
+
+
+@NonNullByDefault
+public abstract class RFactorStoreTest extends AbstractRStoreTest {
+	
+	
+	static class FactorCaseData extends CaseData {
+		
+		final boolean ordered;
+		final int[] codes;
+		final @Nullable String[] levels;
+		
+		
+		public FactorCaseData(final String label, final boolean ordered,
+				final int[] codes, final boolean[] nas, final String[] levels) {
+			super(label, nas);
+			assert (codes.length == this.length);
+			assert (nas.length == this.length);
+			this.ordered= ordered;
+			this.codes= codes;
+			this.levels= levels;
+		}
+		
+		public FactorCaseData(final String label, final boolean ordered,
+				final int[] codes, final String[] levels) {
+			super(label, codes.length);
+			assert (codes.length == this.length);
+			this.ordered= ordered;
+			this.codes= codes;
+			this.levels= levels;
+		}
+		
+	}
+	
+	protected static final List<FactorCaseData> DEFAULT_DATA_SOURCES;
+	static {
+		final var datas= new ArrayList<FactorCaseData>();
+		
+		datas.add(new FactorCaseData("empty-unordered", false, new int[0],
+				new String[0] ));
+		datas.add(new FactorCaseData("empty-unordered-AB", false, new int[0],
+				new String[] { "A", "B" } ));
+		datas.add(new FactorCaseData("empty-ordered", true, new int[0],
+				new String[0] ));
+		datas.add(new FactorCaseData("empty-ordered-AB", false, new int[0],
+				new String[] { "A", "B" } ));
+		
+		datas.add(new FactorCaseData("single-unordered-1", false, new int[] { 1 },
+				new String[] { "A", } ));
+		datas.add(new FactorCaseData("single-ordered-1", true, new int[] { 1 },
+				new String[] { "A", } ));
+		datas.add(new FactorCaseData("single-unordered-2", false, new int[] { 2 },
+				new String[] { "A", "B" } ));
+		datas.add(new FactorCaseData("single-ordered-2", true, new int[] { 2 },
+				new String[] { "A", "B" } ));
+		datas.add(new FactorCaseData("single-NA", false, new int[] { 0 }, new boolean[] { true },
+				new String[0] ));
+		datas.add(new FactorCaseData("single-NA-AB", false, new int[] { 0 }, new boolean[] { true },
+				new String[] { "A", "B" }));
+		
+		{	final int[] values= new int[0x0FF];
+			final boolean[] nas= new boolean[values.length];
+			final String[] levels= new String[] { "A", "B", "C", "D", "E" };
+			Arrays.fill(values, 1);
+			int i= 0;
+			values[i++]= 2;
+			nas[i++]= true;
+			nas[i++]= true;
+			nas[i++]= true;
+			values[i++]= 2;
+			values[i++]= 3;
+			values[i++]= 5;
+			datas.add(new FactorCaseData("special", false, values, nas, levels));
+		}
+		{	final Random rand= new Random(68465);
+			final int[] values= new int[100000];
+			final String[] levels= new String[100];
+			for (int i= 0; i < levels.length; i++) {
+				levels[i]= "F" + (i + 1);
+			}
+			for (int i= 0; i < values.length; i++) {
+				values[i]= rand.nextInt(99) + 1;
+			}
+			
+			datas.add(new FactorCaseData("rand100000", false, values, levels));
+		}
+		if (isBigDataEnabled(4)) {
+			final Random rand= new Random(46);
+			final int[] values= new int[DEFAULT_LONG_DATA_SEGMENT_LENGTH * 2 + 13];
+			final boolean[] nas= new boolean[DEFAULT_LONG_DATA_SEGMENT_LENGTH * 2 + 13];
+			final String[] levels= new String[100];
+			for (int i= 0; i < levels.length; i++) {
+				levels[i]= "F" + (i + 1);
+			}
+			for (int i= 0; i < values.length; i++) {
+				values[i]= rand.nextInt(100);
+				if (values[i] == 0) {
+					nas[i]= true;
+				}
+			}
+			datas.add(new FactorCaseData("randMultiSeg", false, values, nas, levels));
+		}
+		
+		DEFAULT_DATA_SOURCES= datas;
+	}
+	
+	
+	public RFactorStoreTest() {
+	}
+	
+	
+	public static List<FactorCaseData> provideCaseDatas() {
+		return new ArrayList<>(DEFAULT_DATA_SOURCES);
+	}
+	
+	protected abstract RFactorStore createStore(final FactorCaseData data);
+	
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void getStoreType(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		assertEquals(RStore.FACTOR, store.getStoreType());
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void getBaseVectorRClassName(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		final String expected= (data.ordered) ? RObject.CLASSNAME_ORDERED : RObject.CLASSNAME_FACTOR;
+		assertEquals(expected, store.getBaseVectorRClassName());
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void isOrdered(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		assertEquals(data.ordered, store.isOrdered());
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void length(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		checkLength(data, store);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void getLevels(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		assertEquals(data.levels.length, store.getLevelCount());
+		assertArrayEquals(data.levels, store.getLevels().toArray());
+	}
+	
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void isNA(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		checkIsNA(data, store);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void isMissing(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		checkIsMissingNonNum(data, store);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	@SuppressWarnings("boxing")
+	public void getLogi(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		for (int i= 0; i < data.length; i++) {
+			final int i0= i;
+			if (data.nas[i0]) {
+				// undefined
+			}
+			else {
+				final Boolean expected= Boolean.valueOf(data.codes[i0] != 0);
+				assertEquals(expected, store.getLogi(i0));
+				assertEquals(expected, store.getLogi((long)i0));
+			}
+		}
+		assertIndexOutOfBounds(data, store::getLogi, store::getLogi);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void getInt(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		for (int i= 0; i < data.length; i++) {
+			final int i0= i;
+			if (data.nas[i0]) {
+				// undefined
+			}
+			else {
+				final int expected= data.codes[i0];
+				assertEquals(expected, store.getInt(i0));
+				assertEquals(expected, store.getInt((long)i0));
+			}
+		}
+		assertIndexOutOfBounds(data, store::getInt, store::getInt);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void getNum(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		for (int i= 0; i < data.length; i++) {
+			final int i0= i;
+			if (data.nas[i0]) {
+				// undefined
+			}
+			else {
+				final double expected= data.codes[i0];
+				assertEquals(expected, store.getNum(i0));
+				assertEquals(expected, store.getNum((long)i0));
+			}
+		}
+		assertIndexOutOfBounds(data, store::getNum, store::getNum);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void getChar(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		for (int i= 0; i < data.length; i++) {
+			final int i0= i;
+			if (data.nas[i0]) {
+				// undefined
+			}
+			else {
+				final String expected= data.levels[data.codes[i0] - 1];
+				assertEquals(expected, store.getChar(i0));
+				assertEquals(expected, store.getChar((long)i0));
+			}
+		}
+		assertIndexOutOfBounds(data, store::getChar, store::getChar);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void getRaw(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		for (int i= 0; i < data.length; i++) {
+			final int i0= i;
+			if (data.nas[i0]) {
+				// undefined
+			}
+			else if (data.codes[i0] >= 0 && data.codes[i0] < 0xFF) {
+				final byte expected= (byte)(data.codes[i0] & 0xFF);
+				assertEquals(expected, store.getRaw(i0));
+				assertEquals(expected, store.getRaw((long)i0));
+			}
+		}
+		assertIndexOutOfBounds(data, store::getRaw, store::getRaw);
+	}
+	
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void get(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		for (int i= 0; i < data.length; i++) {
+			final int i0= i;
+			if (data.nas[i0]) {
+				assertNull(store.get(i0), storeDiffersAt(i0));
+			}
+			else {
+				final Integer expected= Integer.valueOf(data.codes[i0]);
+				assertEquals(expected, store.get(i0));
+				assertEquals(expected, store.get((long)i0));
+			}
+		}
+		assertIndexOutOfBounds(data, store::get, store::get);
+	}
+	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void toArray(final FactorCaseData data) {
+		final RFactorStore store= createStore(data);
+		
+		final @Nullable Integer[] array= store.toArray();
+		assertEquals(data.codes.length, array.length);
+		for (int i= 0; i < data.length; i++) {
+			final int i0= i;
+			if (data.nas[i0]) {
+				assertNull(store.get(i0), storeDiffersAt(i0));
+			}
+			else {
+				final Integer expected= Integer.valueOf(data.codes[i]);
+				assertEquals(expected, array[i], arrayDiffersAt(i));
+			}
+		}
+	}
+	
+}
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32StoreSerTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32StoreSerTest.java
new file mode 100644
index 0000000..8daacf6
--- /dev/null
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32StoreSerTest.java
@@ -0,0 +1,36 @@
+/*=============================================================================#
+ # Copyright (c) 2020 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.rj.data.impl;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.rj.data.RCharacterStore;
+
+
+@NonNullByDefault
+public class RUniqueCharacter32StoreSerTest extends RUniqueCharacter32StoreTest {
+	
+	
+	public RUniqueCharacter32StoreSerTest() {
+	}
+	
+	
+	@Override
+	protected RCharacterStore createStore(final CharCaseData data) {
+		return createSerStore((RUniqueCharacter32Store)super.createStore(data),
+				RUniqueCharacter32Store::new );
+	}
+	
+}
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32StoreTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32StoreTest.java
new file mode 100644
index 0000000..d1e15cf
--- /dev/null
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32StoreTest.java
@@ -0,0 +1,44 @@
+/*=============================================================================#
+ # Copyright (c) 2020 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.rj.data.impl;
+
+import java.util.Arrays;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.rj.data.RCharacterStore;
+
+
+@NonNullByDefault
+public class RUniqueCharacter32StoreTest extends RCharacterStoreTest {
+	
+	
+	public RUniqueCharacter32StoreTest() {
+	}
+	
+	
+	@Override
+	protected RCharacterStore createStore(final CharCaseData data) {
+		final @Nullable String[] encoded= Arrays.copyOf(data.values, data.length);
+		for (int j= 0; j < data.length; j++) {
+			if (data.nas[j]) {
+				encoded[j]= null;
+			}
+		}
+		return new RUniqueCharacter32Store(encoded);
+	}
+	
+}
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RFactorStore.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RFactorStore.java
index 5a9f45f..57b1b8b 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RFactorStore.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RFactorStore.java
@@ -15,6 +15,7 @@
 package org.eclipse.statet.rj.data;
 
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 
 /**
@@ -26,6 +27,11 @@
 @NonNullByDefault
 public interface RFactorStore extends RIntegerStore {
 	
+	
+	int MIN_INT= 0x00000001;
+	int MAX_INT= RIntegerStore.MAX_INT;
+	
+	
 	// TODO Docu && Constructors (-> 1-based codes)
 	// TODO getFactorName(code) for 1-based code
 	
@@ -34,11 +40,28 @@
 	RCharacterStore getLevels();
 	int getLevelCount();
 	
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * May return {@code null} if it has an extra NA level.
+	 */
+	@Override
+	@Nullable String getChar(final int idx);
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * May return {@code null} if it has an extra NA level.
+	 */
+	@Override
+	@Nullable String getChar(final long idx);
+	RCharacterStore toCharacterData();
+	
+	
 //	void addLevel(final String label);
 //	void renameLevel(final String oldLabel, final String newLabel);
 //	void insertLevel(final int position, final String label);
 //	void removeLevel(final String label);
 	
-	RCharacterStore toCharacterData();
 	
 }
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/AbstractFactorStore.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/AbstractFactorStore.java
index 2ac3357..230bb59 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/AbstractFactorStore.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/AbstractFactorStore.java
@@ -86,6 +86,16 @@
 		setInt(idx, (int)real);
 	}
 	
+	@Override
+	public byte getRaw(final int idx) {
+		return AbstractIntegerStore.toRaw(getInt(idx));
+	}
+	
+	@Override
+	public byte getRaw(final long idx) {
+		return AbstractIntegerStore.toRaw(getInt(idx));
+	}
+	
 	
 	@Override
 	public abstract @Nullable Integer [] toArray();
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactor32Store.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactor32Store.java
index 58074c7..e66ab1b 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactor32Store.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactor32Store.java
@@ -20,6 +20,9 @@
 import java.io.ObjectOutput;
 import java.util.Arrays;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.rj.data.RCharacterStore;
 import org.eclipse.statet.rj.data.RJIO;
 import org.eclipse.statet.rj.data.RStore;
@@ -28,21 +31,20 @@
 /**
  * This implementation is limited to length of 2<sup>31</sup>-1.
  */
+@NonNullByDefault
 public class RFactor32Store extends AbstractFactorStore
 		implements RDataResizeExtension<Integer>, ExternalizableRStore, Externalizable {
 	
 	
 	private int length;
 	
-	protected int[] codes;
+	protected int [] codes;
 	
 	protected RCharacter32Store codeLabels;
 	
 	
-	public RFactor32Store(final int length, final boolean isOrdered, final String[] levelLabels) {
-		if (levelLabels == null) {
-			throw new NullPointerException();
-		}
+	public RFactor32Store(final int length,
+			final boolean isOrdered, final @Nullable String [] levelLabels) {
 		this.length= length;
 		this.isOrdered= isOrdered;
 		this.codes= new int[length];
@@ -50,16 +52,45 @@
 		this.codeLabels= new RUniqueCharacter32Store(levelLabels);
 	}
 	
-	public RFactor32Store(final int[] codes, final boolean isOrdered, final String[] levelLabels) {
-		if (codes == null || levelLabels == null) {
-			throw new NullPointerException();
-		}
+	public RFactor32Store(final int [] codes,
+			final boolean isOrdered, final @Nullable String [] levelLabels) {
 		this.length= codes.length;
 		this.isOrdered= isOrdered;
 		this.codes= codes;
 		this.codeLabels= new RUniqueCharacter32Store(levelLabels);
 	}
 	
+	public RFactor32Store(final int [] codes, final int @Nullable[] naIdxs,
+			final boolean isOrdered, final @Nullable String [] levelLabels) {
+		this.length= codes.length;
+		this.isOrdered= isOrdered;
+		this.codes= codes;
+		this.codeLabels= new RUniqueCharacter32Store(levelLabels);
+		if (naIdxs != null) {
+			for (int i= 0; i < naIdxs.length; i++) {
+				this.codes[naIdxs[i]]= NA_integer_INT;
+			}
+		}
+	}
+	
+	public RFactor32Store(final int [] codes, final boolean @Nullable[] isNAs,
+			final boolean isOrdered, final @Nullable String [] levelLabels) {
+		this.length= codes.length;
+		this.isOrdered= isOrdered;
+		this.codes= codes;
+		this.codeLabels= new RUniqueCharacter32Store(levelLabels);
+		if (isNAs != null) {
+			if (isNAs.length != this.length) {
+				throw new IllegalArgumentException();
+			}
+			for (int i= 0; i < isNAs.length; i++) {
+				if (isNAs[i]) {
+					this.codes[i]= NA_integer_INT;
+				}
+			}
+		}
+	}
+	
 	
 	public RFactor32Store(final RJIO io, final int length) throws IOException {
 		this.length= length;
@@ -332,30 +363,28 @@
 	
 	
 	@Override
-	public Integer get(final int idx) {
+	public @Nullable Integer get(final int idx) {
 		if (idx < 0 || idx >= length()) {
 			throw new IndexOutOfBoundsException(Long.toString(idx));
 		}
 		final int code= this.codes[idx];
 		return (code > 0) ?
-				Integer.valueOf(code) :
-				null;
+				Integer.valueOf(code) : null;
 	}
 	
 	@Override
-	public Integer get(final long idx) {
+	public @Nullable Integer get(final long idx) {
 		if (idx < 0 || idx >= length()) {
 			throw new IndexOutOfBoundsException(Long.toString(idx));
 		}
 		final int code= this.codes[(int)idx];
 		return (code > 0) ?
-				Integer.valueOf(code) :
-				null;
+				Integer.valueOf(code) : null;
 	}
 	
 	@Override
-	public Integer[] toArray() {
-		final Integer[] array= new Integer[length()];
+	public @Nullable Integer[] toArray() {
+		final var array= new @Nullable Integer [this.length];
 		final int[] ints= this.codes;
 		for (int i= 0; i < array.length; i++) {
 			final int code= ints[i];
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactorFix64Store.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactorFix64Store.java
index 4b1d208..8e8c85a 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactorFix64Store.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RFactorFix64Store.java
@@ -45,7 +45,8 @@
 	protected final RCharacter32Store codeLabels;
 	
 	
-	public RFactorFix64Store(final long length, final boolean isOrdered, final String[] levelLabels) {
+	public RFactorFix64Store(final long length,
+			final boolean isOrdered, final @Nullable String [] levelLabels) {
 		this.length= length;
 		this.isOrdered= isOrdered;
 		this.codes= new2dIntArray(length, SEGMENT_LENGTH);
@@ -53,13 +54,36 @@
 		this.codeLabels= new RUniqueCharacter32Store(levelLabels);
 	}
 	
-	public RFactorFix64Store(final int [] @NonNull[] codes, final boolean isOrdered, final String[] levelLabels) {
+	public RFactorFix64Store(final int [] @NonNull[] codes,
+			final boolean isOrdered, final @Nullable String [] levelLabels) {
 		this.length= check2dArrayLength(codes, SEGMENT_LENGTH);
 		this.isOrdered= isOrdered;
 		this.codes= codes;
 		this.codeLabels= new RUniqueCharacter32Store(levelLabels);
 	}
 	
+	public RFactorFix64Store(final int [] @NonNull[] codes,
+			final boolean @Nullable[] @NonNull[] isNAs,
+			final boolean isOrdered, final @Nullable String [] levelLabels) {
+		this.length= check2dArrayLength(codes, SEGMENT_LENGTH);
+		this.isOrdered= isOrdered;
+		this.codes= codes;
+		this.codeLabels= new RUniqueCharacter32Store(levelLabels);
+		if (isNAs != null) {
+			if (check2dArrayLength(isNAs, SEGMENT_LENGTH) != this.length) {
+				throw new IllegalArgumentException();
+			}
+			for (int i= 0; i < isNAs.length; i++) {
+				final boolean[] isNAi= isNAs[i];
+				for (int j= 0; j < isNAi.length; j++) {
+					if (isNAi[j]) {
+						this.codes[i][j]= NA_integer_INT;
+					}
+				}
+			}
+		}
+	}
+	
 	
 	public RFactorFix64Store(final RJIO io, final long length) throws IOException {
 		this.length= length;
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32Store.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32Store.java
index 1b7e5a8..479b8b2 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32Store.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RUniqueCharacter32Store.java
@@ -16,9 +16,13 @@
 
 import java.io.IOException;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.rj.data.RJIO;
 
 
+@NonNullByDefault
 public class RUniqueCharacter32Store extends RCharacter32Store {
 	
 	
@@ -26,7 +30,7 @@
 		super();
 	}
 	
-	public RUniqueCharacter32Store(final String[] initialValues) {
+	public RUniqueCharacter32Store(final @Nullable String [] initialValues) {
 		super(initialValues, initialValues.length);
 	}