public void ArrayItems_IndexFilter_ReturnsSplitSequences()
		{
			var input = new[]
		    {
		        ModelGrammar.TokenArrayBeginUnnamed,
		        ModelGrammar.TokenFalse,
		        ModelGrammar.TokenPrimitive("Pick me!"),
		        ModelGrammar.TokenObjectBeginUnnamed,
		        ModelGrammar.TokenProperty("key1"),
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenProperty("key2"),
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenObjectEnd,
		        ModelGrammar.TokenPrimitive(42),
		        ModelGrammar.TokenArrayEnd
		    };

			var expected = new[]
			{
				new[]
				{
					ModelGrammar.TokenPrimitive("Pick me!")
				},
				new[]
				{
					ModelGrammar.TokenPrimitive(42)
				}
			};

			// select items with odd indexes
			var actual = input.ArrayItems(index => (index % 2 == 1)).ToArray();

			Assert.Equal(expected, actual, false);
		}
		public void ArrayItems_FilteringByIndexAndValue_ReturnsSplitSequences()
		{
			var input = new[]
		    {
		        ModelGrammar.TokenArrayBeginUnnamed,
		        ModelGrammar.TokenFalse,
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenObjectBeginUnnamed,
		        ModelGrammar.TokenProperty("key1"),
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenProperty("key2"),
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenObjectEnd,
		        ModelGrammar.TokenPrimitive(42),
		        ModelGrammar.TokenPrimitive(Math.PI),
		        ModelGrammar.TokenArrayEnd
		    };

			var expected = new[]
			{
				new[]
				{
					ModelGrammar.TokenFalse
				},
				new[]
				{
			        ModelGrammar.TokenPrimitive(Math.PI),
				}
			};

			// select all even index primitive items
			var actual = input.ArrayItems(index => (index % 2 == 0)).Where(item => item.IsPrimitive()).ToArray();

			Assert.Equal(expected, actual, false);
		}
		public void ArrayItems_NestedObject_ReturnsSplitSequences()
		{
			var input = new[]
		    {
		        ModelGrammar.TokenArrayBeginUnnamed,
		        ModelGrammar.TokenFalse,
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenObjectBeginUnnamed,
		        ModelGrammar.TokenProperty("key1"),
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenProperty("key2"),
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenObjectEnd,
		        ModelGrammar.TokenPrimitive(42),
		        ModelGrammar.TokenArrayEnd
		    };

			var expected = new[]
			{
				new[]
				{
					ModelGrammar.TokenFalse
				},
				new[]
				{
					ModelGrammar.TokenPrimitive("Hello!")
				},
				new[]
				{
					ModelGrammar.TokenObjectBeginUnnamed,
					ModelGrammar.TokenProperty("key1"),
					ModelGrammar.TokenNull,
					ModelGrammar.TokenProperty("key2"),
					ModelGrammar.TokenPrimitive("Hello!"),
					ModelGrammar.TokenObjectEnd,
				},
				new[]
				{
					ModelGrammar.TokenPrimitive(42)
				},
			};

			// select all items
			var actual = input.ArrayItems(index => true).ToArray();

			Assert.Equal(expected, actual, false);
		}
		public void ArrayItems_MixedPrimitivesFilterNonNull_ReturnsSplitSequences()
		{
			var input = new[]
		    {
		        ModelGrammar.TokenArrayBeginUnnamed,
		        ModelGrammar.TokenFalse,
		        ModelGrammar.TokenTrue,
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenPrimitive(42),
		        ModelGrammar.TokenArrayEnd
		    };

			var expected = new[]
			{
				new[]
				{
					ModelGrammar.TokenFalse
				},
				new[]
				{
					ModelGrammar.TokenTrue
				},
				new[]
				{
					ModelGrammar.TokenPrimitive("Hello!")
				},
				new[]
				{
					ModelGrammar.TokenPrimitive(42)
				},
			};

			// select all items with a non-null value
			var actual = input.ArrayItems().Where(item => item.All(token => token.Value != null)).ToArray();

			Assert.Equal(expected, actual, false);
		}
		public void ArrayItems_MixedPrimitivesNoFilter_ReturnsSplitSequences()
		{
			var input = new[]
		    {
		        ModelGrammar.TokenArrayBeginUnnamed,
		        ModelGrammar.TokenFalse,
		        ModelGrammar.TokenTrue,
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenPrimitive(42),
		        ModelGrammar.TokenArrayEnd
		    };

			var expected = new[]
			{
				new[]
				{
					ModelGrammar.TokenFalse
				},
				new[]
				{
					ModelGrammar.TokenTrue
				},
				new[]
				{
					ModelGrammar.TokenNull
				},
				new[]
				{
					ModelGrammar.TokenPrimitive("Hello!")
				},
				new[]
				{
					ModelGrammar.TokenNull
				},
				new[]
				{
					ModelGrammar.TokenPrimitive(42)
				},
			};

			// select all items
			var actual = input.ArrayItems().ToArray();

			Assert.Equal(expected, actual, false);
		}
		public void ArrayItems_MixedPrimitivesFilterAll_ReturnsSplitSequences()
		{
			var input = new[]
		    {
		        ModelGrammar.TokenArrayBeginUnnamed,
		        ModelGrammar.TokenFalse,
		        ModelGrammar.TokenTrue,
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenPrimitive("Hello!"),
		        ModelGrammar.TokenNull,
		        ModelGrammar.TokenPrimitive(42),
		        ModelGrammar.TokenArrayEnd
		    };

			var expected = new IEnumerable<Token<ModelTokenType>>[0];

			// select no items
			var actual = input.ArrayItems(index => false).ToArray();

			Assert.Equal(expected, actual, false);
		}
		public void Properties_GraphComplex_ReturnsGraph()
		{
			// input from pass1.json in test suite at http://www.json.org/JSON_checker/
			var input = new[]
			{
				ModelGrammar.TokenArrayBeginUnnamed,
				ModelGrammar.TokenPrimitive("JSON Test Pattern pass1"),
				ModelGrammar.TokenObjectBeginUnnamed,
				ModelGrammar.TokenProperty("object with 1 member"),
				ModelGrammar.TokenArrayBeginUnnamed,
				ModelGrammar.TokenPrimitive("array with 1 element"),
				ModelGrammar.TokenArrayEnd,
				ModelGrammar.TokenObjectEnd,
				ModelGrammar.TokenObjectBeginUnnamed,
				ModelGrammar.TokenObjectEnd,
				ModelGrammar.TokenArrayBeginUnnamed,
				ModelGrammar.TokenArrayEnd,
				ModelGrammar.TokenPrimitive(-42),
				ModelGrammar.TokenTrue,
				ModelGrammar.TokenFalse,
				ModelGrammar.TokenNull,
				ModelGrammar.TokenObjectBeginUnnamed,
				ModelGrammar.TokenProperty("integer"),
				ModelGrammar.TokenPrimitive(1234567890),
				ModelGrammar.TokenProperty("real"),
				ModelGrammar.TokenPrimitive(-9876.543210),
				ModelGrammar.TokenProperty("e"),
				ModelGrammar.TokenPrimitive(0.123456789e-12),
				ModelGrammar.TokenProperty("E"),
				ModelGrammar.TokenPrimitive(1.234567890E+34),
				ModelGrammar.TokenProperty(""),
				ModelGrammar.TokenPrimitive(23456789012E66),
				ModelGrammar.TokenProperty("zero"),
				ModelGrammar.TokenPrimitive(0),
				ModelGrammar.TokenProperty("one"),
				ModelGrammar.TokenPrimitive(1),
				ModelGrammar.TokenProperty("space"),
				ModelGrammar.TokenPrimitive(" "),
				ModelGrammar.TokenProperty("quote"),
				ModelGrammar.TokenPrimitive("\""),
				ModelGrammar.TokenProperty("backslash"),
				ModelGrammar.TokenPrimitive("\\"),
				ModelGrammar.TokenProperty("controls"),
				ModelGrammar.TokenPrimitive("\b\f\n\r\t"),
				ModelGrammar.TokenProperty("slash"),
				ModelGrammar.TokenPrimitive("/ & /"),
				ModelGrammar.TokenProperty("alpha"),
				ModelGrammar.TokenPrimitive("abcdefghijklmnopqrstuvwyz"),
				ModelGrammar.TokenProperty("ALPHA"),
				ModelGrammar.TokenPrimitive("ABCDEFGHIJKLMNOPQRSTUVWYZ"),
				ModelGrammar.TokenProperty("digit"),
				ModelGrammar.TokenPrimitive("0123456789"),
				ModelGrammar.TokenProperty("0123456789"),
				ModelGrammar.TokenPrimitive("digit"),
				ModelGrammar.TokenProperty("special"),
				ModelGrammar.TokenPrimitive("`1~!@#$%^&*()_+-={':[,]}|;.</>?"),
				ModelGrammar.TokenProperty("hex"),
				ModelGrammar.TokenPrimitive("\u0123\u4567\u89AB\uCDEF\uabcd\uef4A"),
				ModelGrammar.TokenProperty("true"),
				ModelGrammar.TokenTrue,
				ModelGrammar.TokenProperty("false"),
				ModelGrammar.TokenFalse,
				ModelGrammar.TokenProperty("null"),
				ModelGrammar.TokenNull,
				ModelGrammar.TokenProperty("array"),
				ModelGrammar.TokenArrayBeginUnnamed,
				ModelGrammar.TokenArrayEnd,
				ModelGrammar.TokenProperty("object"),
				ModelGrammar.TokenObjectBeginUnnamed,
				ModelGrammar.TokenObjectEnd,
				ModelGrammar.TokenProperty("address"),
				ModelGrammar.TokenPrimitive("50 St. James Street"),
				ModelGrammar.TokenProperty("url"),
				ModelGrammar.TokenPrimitive("http://www.JSON.org/"),
				ModelGrammar.TokenProperty("comment"),
				ModelGrammar.TokenPrimitive("// /* <!-- --"),
				ModelGrammar.TokenProperty("# -- --> */"),
				ModelGrammar.TokenPrimitive(" "),
				ModelGrammar.TokenProperty(" s p a c e d "),
				ModelGrammar.TokenArrayBeginUnnamed,
				ModelGrammar.TokenPrimitive(1),
				ModelGrammar.TokenPrimitive(2),
				ModelGrammar.TokenPrimitive(3),
				ModelGrammar.TokenPrimitive(4),
				ModelGrammar.TokenPrimitive(5),
				ModelGrammar.TokenPrimitive(6),
				ModelGrammar.TokenPrimitive(7),
				ModelGrammar.TokenArrayEnd,
				ModelGrammar.TokenProperty("compact"),
				ModelGrammar.TokenArrayBeginUnnamed,
				ModelGrammar.TokenPrimitive(1),
				ModelGrammar.TokenPrimitive(2),
				ModelGrammar.TokenPrimitive(3),
				ModelGrammar.TokenPrimitive(4),
				ModelGrammar.TokenPrimitive(5),
				ModelGrammar.TokenPrimitive(6),
				ModelGrammar.TokenPrimitive(7),
				ModelGrammar.TokenArrayEnd,
				ModelGrammar.TokenProperty("jsontext"),
				ModelGrammar.TokenPrimitive("{\"object with 1 member\":[\"array with 1 element\"]}"),
				ModelGrammar.TokenProperty("quotes"),
				ModelGrammar.TokenPrimitive("&#34; \u0022 %22 0x22 034 &#x22;"),
				ModelGrammar.TokenProperty("/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"),
				ModelGrammar.TokenPrimitive("A key can be any string"),
				ModelGrammar.TokenObjectEnd,
				ModelGrammar.TokenPrimitive(0.5),
				ModelGrammar.TokenPrimitive(98.6),
				ModelGrammar.TokenPrimitive(99.44),
				ModelGrammar.TokenPrimitive(1066),
				ModelGrammar.TokenPrimitive(10.0),
				ModelGrammar.TokenPrimitive(1.0),
				ModelGrammar.TokenPrimitive(0.1),
				ModelGrammar.TokenPrimitive(1.0),
				ModelGrammar.TokenPrimitive(2.0),
				ModelGrammar.TokenPrimitive(2.0),
				ModelGrammar.TokenPrimitive("rosebud"),
				ModelGrammar.TokenArrayEnd
			};

			var expected = new Dictionary<DataName, IEnumerable<Token<ModelTokenType>>>
			{
				{ new DataName("url"), new[] { ModelGrammar.TokenPrimitive("http://www.JSON.org/") } },
				{ new DataName("compact"), new[] {
					ModelGrammar.TokenArrayBeginUnnamed,
					ModelGrammar.TokenPrimitive(1),
					ModelGrammar.TokenPrimitive(2),
					ModelGrammar.TokenPrimitive(3),
					ModelGrammar.TokenPrimitive(4),
					ModelGrammar.TokenPrimitive(5),
					ModelGrammar.TokenPrimitive(6),
					ModelGrammar.TokenPrimitive(7),
					ModelGrammar.TokenArrayEnd,
				} }
			};

			Assert.True(input.IsArray());
			Assert.False(input.IsObject());
			Assert.False(input.IsPrimitive());

			// cherry pick properties
			var actual = input
				.ArrayItems(index => index == 8).FirstOrDefault() // select the big object
				.Properties(name => name.LocalName == "url" || name.LocalName == "compact"); // select two properties

			Assert.Equal(expected, actual, false);
		}