public void Deserializes_And_Ignores_Inconsistency_From_Excessive_Specification()
			{
				PageNumberAndSize page = new PageNumberAndSize(7, 20);
				PageNumberAndSize deserializedPage
					= JsonConvert.DeserializeObject<PageNumberAndSize>(
						"{\"Number\":7,\"Size\":20,\"Index\":1111111,\"IsUnbounded\":true}"); // ,\"HasValue\":false

				AssertEquality(page, deserializedPage);
			}
			public void Serializes_All_Properties()
			{
				PageNumberAndSize page = new PageNumberAndSize(7, 20);
				string serializedPage = JsonConvert.SerializeObject(page);

				Assert.AreEqual(
					"{\"Number\":7,\"Size\":20,\"Index\":6,\"IsUnbounded\":false}", // ,\"HasValue\":true
					serializedPage);
			}
			public void Deserializes_From_Minimal_Specification()
			{
				PageNumberAndSize page = new PageNumberAndSize(7);
				PageNumberAndSize deserializedPage
					= JsonConvert.DeserializeObject<PageNumberAndSize>(
						"{\"Number\":7,\"Size\":10}");

				AssertEquality(page, deserializedPage);
			}
		internal static void AssertIsUnbounded(PageNumberAndSize unboundedPage)
		{
			Assert.IsTrue(unboundedPage.IsUnbounded);
			Assert.AreEqual(byte.MinValue, unboundedPage.Size);

			AssertIsFirstPage(unboundedPage);
		}
		internal static void AssertIsFirstPage(PageNumberAndSize firstPage)
		{
			Assert.IsTrue(firstPage.HasValue);
			Assert.GreaterOrEqual(firstPage.Size, byte.MinValue);
			Assert.AreEqual(PageNumberAndSize.FirstPageNumber, firstPage.Number);
			Assert.AreEqual(0, firstPage.Index);
		}
		internal static void AssertIsEmpty(PageNumberAndSize emptyPage)
		{
			Assert.IsFalse(emptyPage.HasValue);
			Assert.IsFalse(emptyPage.IsUnbounded);
			Assert.AreEqual(byte.MinValue, emptyPage.Size);
			Assert.AreEqual(0, emptyPage.Number);
			Assert.AreEqual(-1, emptyPage.Index);
		}
		internal static void AssertEquality(
			PageNumberAndSize expected, PageNumberAndSize actual)
		{
			Assert.IsTrue(expected == actual);
			Assert.IsFalse(expected != actual);
			Assert.IsTrue(expected.Equals(actual));
			Assert.AreEqual(expected, actual);

			Assert.IsTrue(actual == expected);
			Assert.IsFalse(actual != expected);
			Assert.IsTrue(actual.Equals(expected));
			Assert.AreEqual(actual, expected);

			Assert.AreEqual(expected.Number, actual.Number);
			Assert.AreEqual(expected.Size, actual.Size);
			Assert.AreEqual(expected.Index, actual.Index);
			Assert.AreEqual(expected.IsUnbounded, actual.IsUnbounded);
			Assert.AreEqual(expected.HasValue, actual.HasValue);
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="PagingInfoCalculator"/> struct.
		/// Invoked by the private <see cref="PagingInfo.Calculator"/>
		/// property of an owner <see cref="PagingInfo"/> value,
		/// calculates metadata for paging UI, optionally including
		/// a list of all pages and item numbers. For effective lazy
		/// initialization following deserialization from bare essentials.
		/// </summary>
		/// <param name="currentPage">
		/// The page <see cref="PageNumberAndSize.Number"/>
		/// and <see cref="PageNumberAndSize.Size"/>.
		/// If <see cref="PageNumberAndSize.Unbounded"/> is sent,
		/// all of the items are returned on a single page as large
		/// as the number of <paramref name="totalItems"/>.
		/// </param>
		/// <param name="totalItems">
		/// The total number of items in the collection to be paged,
		/// initial value for the immutable <see cref="TotalItems"/> field.
		/// </param>
		/// <param name="includeAllPagesAndItemNumbers">
		/// Whether to fill the set of <see cref="AllPages"/>
		/// including the item numbers on each page,
		/// which may be useful for some paging UI.
		/// Relayed back to the <see cref="PagingInfo"/> via the
		/// <see cref="IncludeAllPagesAndItemNumbers"/> field,
		/// adds the private <see cref="PagingInfo.AllPages"/>
		/// property to the serialization output for JSON.
		/// </param>
		internal PagingInfoCalculator(
			PageNumberAndSize currentPage, int totalItems, bool includeAllPagesAndItemNumbers)
		{
			Contract.Requires<ArgumentException>(
				currentPage.HasValue, "The current page must have a value. \"Unbounded\" is an acceptable value.");
			Contract.Requires<ArgumentOutOfRangeException>(
				totalItems >= 0, "The number of items in the list must not be negative!");

			this.IncludeAllPagesAndItemNumbers = includeAllPagesAndItemNumbers;
			this.CurrentPage = currentPage;
			this.TotalItems = totalItems;

			if (currentPage.IsUnbounded)
			{
				// This is the case where all of the items are returned on
				// the list, and there is just one unbounded page of items.
				// The total number of items may exceed the maximum allowed
				// value of a byte, so the "page size" value remains as zero.
				// Beware of division by zero!
				// There are no calculations here based on the page size!
				this.TotalPages = 1;
				this.CurrentPage = PageNumberAndSize.Unbounded;
				this.FirstItemNumber = this.TotalItems > 0 ? 1 : 0;
				this.LastItemNumber = this.TotalItems;
				this.ItemCount = this.TotalItems;
				this.IsFirstPage = true;
				this.IsLastPage = true;
				this.PreviousPage = PageNumberAndSize.Empty;
				this.NextPage = PageNumberAndSize.Empty;
				this.FirstPage = this.CurrentPage;
				this.LastPage = this.CurrentPage;
			}
			else
			{
				this.CurrentPage = currentPage;

				if ((this.TotalItems > 0) && (this.CurrentPage.Size > 0))
				{
					// Calculate the total pages for a fixed page size and at least one result.
					this.TotalPages = CalculateTotalPages(this.CurrentPage.Size, this.TotalItems);

					// Handle the situation if someone turns past the last page.
					if (this.CurrentPage.Number > this.TotalPages)
					{
						// Reset the current page to be the number of the last possible page.
						this.CurrentPage = new PageNumberAndSize(this.TotalPages, this.CurrentPage.Size);
					}

					this.LastItemNumber = this.CurrentPage.Number * this.CurrentPage.Size;
					this.FirstItemNumber = this.LastItemNumber - this.CurrentPage.Size + 1;
					this.ItemCount = this.LastItemNumber - this.FirstItemNumber + 1;
					this.IsFirstPage = this.CurrentPage.Number == PageNumberAndSize.FirstPageNumber;
					this.IsLastPage = this.CurrentPage.Number == this.TotalPages;

					if (this.IsFirstPage)
					{
						this.FirstPage = this.CurrentPage;
						this.PreviousPage = PageNumberAndSize.Empty;
					}
					else
					{
						this.FirstPage = new PageNumberAndSize(
							PageNumberAndSize.FirstPageNumber, this.CurrentPage.Size);

						this.PreviousPage = new PageNumberAndSize(
							this.CurrentPage.Number - 1, this.CurrentPage.Size);
					}

					if (this.IsLastPage)
					{
						this.LastPage = this.CurrentPage;
						this.NextPage = PageNumberAndSize.Empty;

						// The number of items shown on the last page
						// may be smaller than the number of items per page.
						this.LastItemNumber = this.TotalItems;
					}
					else
					{
						this.LastPage = new PageNumberAndSize(
							this.TotalPages, this.CurrentPage.Size);

						this.NextPage = new PageNumberAndSize(
							this.CurrentPage.Number + 1, this.CurrentPage.Size);
					}
				}
				else
				{
					// This is the case where the count of TotalItems is zero,
					// so reset the page number back to the first page.
					this.CurrentPage = new PageNumberAndSize(
						PageNumberAndSize.FirstPageNumber, this.CurrentPage.Size);

					// There is just one page of results, with no items.
					this.TotalPages = 1;
					this.FirstItemNumber = 0;
					this.LastItemNumber = 0;
					this.ItemCount = 0;
					this.IsFirstPage = true;
					this.IsLastPage = true;
					this.PreviousPage = PageNumberAndSize.Empty;
					this.NextPage = PageNumberAndSize.Empty;
					this.FirstPage = this.CurrentPage;
					this.LastPage = this.CurrentPage;
				}
			}

			this.AllPages = this.IncludeAllPagesAndItemNumbers
				? AllPagesAndItemNumbers(this.CurrentPage.Size, this.TotalItems, this.TotalPages)
				: null;
		}