text:
Highfields lad aiming to break into the exclusive world of horse racing
Kanane wants to break barriers in the predominantly white equestrian world
Asha Patel
Kanane Francis from Highfields who hopes to take the equestrian world by storm. Before December 2019, the 20year-old didn't know anything about horses but less than a year later he is taking leaps towards becoming a jockey (Image: Supplied)
A young horse rider is well on his way to his dream of becoming a jockey after less than a year of training.
Kanane Francis from Highfields recently graduated from the prestigious British Racing School earlier this year and a three-month intensive course.
Before December 2019, the now 20-year-old had no experience working with horses. That was until a Facebook post about the Urban Equestrian Academy in Leicester caught his interest.
"I was scrolling through Facebook one night and thought "why not?" - I wasn't doing anything at the time so I got in touch and asked to volunteer to help out. I didn't expect to be riding," Kanane told LeicestershireLive.
Kanane's mum, Sandra was incredibly proud of her son after he graduated from The British Racing School in September 2020 (Image: Supplied)
Given the opportunity, he took a couple of horse-riding lessons and discovered he had a natural talent.
He was then encouraged by horse trainer and founder of the Urban Equestrian Academy, Tariq Zampaladus, to take his talent further.
"Tariq has helped me a lot- I wouldn't have had this opportunity if it wasn't for him. I'm happy to be under his wing, he has opened a lot of doors for me," Kanane said.
Kanane quickly developed a love for riding so Zampaladus suggested exploring the world of horse racing, which is when Kanane enrolled in The British Racing School based in Suffolk.
Watch former boxing champion and Leicester horse trainer ride through Leicester streets
Therapy centre using horses as teachers awarded £230k National Lottery funding
During the three-month course, Kanane had a 6am start every day which began with cleaning out stables and two hours of riding followed by an afternoon lecture and an evening life-skills or fitness session.
"I had never been away from home before so it was a different experience for me. Three weeks was hard work but I got into it once I settled into the routine," he said.
Less than one year after his first ever experience of horse-riding, Kanane graduated from The British Racing School.
"The day I was graduated was the best feeling. I knew I'd done something life-changing that will open a lot of doors for me," he said.
Kanane has already secured his next job towards a Level 2 qualification - another step closer to his dream of becoming a jockey. (Image: Supplied)
The young rider, known on Instagram as 'The Urban Jockey' celebrated his graduation with his proud family and friends.
"I definitely surprised my family. I'm the only one in the family who has worked with horses.
"My mum always said it's a good thing that I'm getting into something different," he said.
Since graduating in September, the aspiring jockey has already secured a job with the Mick Appleby Racing equestrian centre in Oakham run by the former champion trainer. The job will culminate in a Level 2 qualification and a step closer to Kanane's dream of becoming a jockey.
In what is a predominantly white industry, Kanane said he wanted to "pave the way for other young people to get into horse riding".
"You can do anything you put your mind to," he said.
Black Communities
Highfields

redpajama_set_name: RedPajamaCommonCrawl


text:
Leveraging on the high-performance 3D graphics rendering engine DLP-RE and the powerful video-processing engine DLP-PE, I-Master (Image Master) Series is able to optimize the hardware processing platform, the software architecture, and the system operation platform, results in the maximum level of graphic quality and rendering speed. Capable of managing both the real-time on air play-out and the maintenance of high quality 3D graphics and effects, Smart Series enables the operator to implement real-time render function which solves the problems such as inconvenient post-production rendering and impossible real-time template modifications, substantially improving the branding ability and the efficiency of TV productions as well as offering a novel solution for the innovation and development of TV programs.
Independent control of multiple graphics and DVE effect
I-Master series enables the triggering of multiple graphics including tickers, animated logo or ad on one screen simultaneously. Each graphic item is created separately and treated as an independent layer. For instance, it is possible to manage a logo layer separately from an underlying ticker layer, which could be on top of a lower third or full screen graphic. This allows a flexible and quick access to modify, preview and play each to-be-on-air item and no aliasing, glowing or vibration effect will be introduced while played on air.
With powerful DVE effect, I-Master holds a leading position internationally in terms of the real time video processing technology, which is able to animate multiple real-time videos with negligible less than one frame delay.
Modular Gallery and easy-to-use authoring functionality
Template-based management of the textures, colors, modes of motion, font, light, shadow, various special effects, and all sorts of real-time data enables the users to adopt a Macro Replacement Function when handling duplicated operations, making the play-out of live shows more flexible and convenient. Meanwhile, the system supports interchangeable functionality between HD and SD templates, which makes the compatibility between them no longer a problem, enhancing the efficiency of management and resource sharing.
Seamless Integration to external database, real-time generation of visualized 3D graphics
Some live shows set high standards for external real-time data which should be fast, dynamic and accurate. I-Master rises to the challenge by offering a number of external dynamic data interfaces which share a synchronic operation with the external data servers, allowing seamless integration to the latest data such as stock market, text message, and weather information in real-time, thus significantly improving the timeliness and efficiency of TV productions.
3D real-time video graphic rendering platform
Grounded on the advanced graphic rendering engine technology, I-Magic, a real-time video graphic rendering platform which combines the real-time 3D graphic-rendering technology, the real-time video-processing technology, and the real-time data-processing technology, offering magnificent visual effect and captivating graphics. Equipped with a wide range of graphic management plus highly enhanced timeliness, I-Magic is able to cover a full package of applications of 3D on-air graphics and channel branding on live shows such as news, sports, finance, weather forecasts, and entertainment.
3D graphics real-time creation system
With efficient 3D graphics rendering ability, intuitive and user-friendly interface, resourceful gallery, diversified special effects, plus the 2D/3D template import function through a third party modeling software as well as other powerful functions, I-Builder releases designers from repetitive standard tasks and gives them complete freedom to concentrate on the creative side of their work, taking care of all the design needs.
Real 3D creation tool
The stunning 3D graphics created by I-Builder, the genuine 3D modeling software, can be rendered while on air, separating I-Builder from other simulated 3D modeling software.
Various Motion Effect
Thanks to I-Builder's powerful function of motion editing, any parameter like the position, material, texture of the imported graphics can be designed and edited based on the timeline.
What-you-see-is-what-you-get editing manner
The would-be template can be previewed via the rendering engine and only when what displays on the preview window is in line with what shows on the monitor screen, can the template go on air.
Rich gallery with numerous templates
Rich gallery with numerous templates empowers the designers more creative. Textures, colors, modes of motion, font, light and shadow along with other effects of the graphic elements can be saved as templates , enabling create once ,use many.
I-Play play system I-Play
Based on the DLP-RE graphics rendering engine platform, I-Play, a simple-to-operate yet versatile play-out software, perfectly combines the templates, files, data, texts and graphics, and integrates with existing news net, rapidly generating playlists and visualizing the external data and graphics. This significantly reduces the labor in the studio.
●Seamlessly integrates with other DLP products
●2D/3D scenes along with their fonts, graphics, animations and other properties can be replaced ,modified or played while on air
●Supports various play-out ways such as automatic, manual, timed and sequential ,hotkey-triggering play
●Supports customized short-cuts to smooth user's operation
●Artistic yet practical interface , easy to operate
●A scalable system in which the control of integrated equipments is based on plug-ins
E-mail:infooverseas@dlp-digital.com
Copyright © 2015. DLP-Digital Tech Co., Ltd.All rights reserved


text:
T.A.R. Approval Tracker
Interactive Electoral Map
Trends by State
Favorability Index
2024 Projection
Candidate Favorability Index
A By-the-Numbers Approach
The Favorability Index - or 'FavIndx' - applies a numerical value (by way of our in-house formula) to a presidential candidate. The resulting value is based in the total number of states won and the overall victory spread in a given state - this becoming the candidate's 'favorability' rating showing just how popular (or unpopular) he/she was at the moment of victory/defeat. Generally, the larger the state victories and wider the victory spread, the greater the favorability score. However, in some races - particularly close ones - the losing candidate may score better than the winning candidate.
Based on our formula, two of the best performing candidates are Richard Nixon (1972) and Ronald Reagon (1984) while two of the worst performers are their counterparts, George McGovern (1972) and Walter Mondale (1984). The highest possible FavIndx rating achieved is by Republican Richard Nixon in the 1972 campaign with a score of 1355.5. When averaged out, Ronald Reagan and George W. Bush (865.95 and 306.2, respectively) are the top-performing Republican candidates while Bill Clinton and Barack Obama (289.4 and 289.2, respectively) are the two top-performing Democratic candidates. Reagan, Bush, and Clinton (Bill) all scored better in their second outings (1078.1, 342.5, and 298.4, respectively) whereas Obama scored better in his first (322.8) when compared to his second (255.6). One rarity is Nixon appearing three times on this list and, each time, increasing his favorability score with each outing before finalyl securing the top spot. Of particular note is the top five candidates being leaders during the Cold War period (1947-1991). Favorability scores have become consistently close since the 1988 election.
For the 2020 election between incumpbent Donald Trump and challenger Joe Biden, Republican candidate Donald Trump (247.8) slipped in the FavIndx from his 2016 showing (347.3) against Democratic challenger Hillary Clinton (166.1) - but still managed to edge out Democratic challenger (and eventual winner) Joe Biden (242.1). Trump's 2020 score makes him the higest ranking loser of the list while Biden registers as the lowest ranking winner - reflecting a less-than-excited electorate for the 2020 Presidential Election cycle. Our 2024 election projection (based on historical data) sees the Republican candidate (unannounced as of this publishing) coming out on top with a score of 263.2 versus the losing Democrat's 180.4 score (this candidate also unannouned). This indicates a Republican candidate that will compare slightly less favorably to 1968's Nixon and a Democratic candidate slightly better favored than 2016's Hillary Clinton.
Overall Average
Out of 8 Election Victories
Avg Candidate Winning Score: 596.0
DEMOCRAT Candidate
Republican (W)
Democrat (W)
Republican (L)
Democrat (L)
Bob Dole
Michael Dukakis
Walter Mondale
George McGovern
Site Disclaimer | Privacy Policy | Cookies
Biden Approval Ratings 2024 Projection Interactive Electoral Map Electoral Voting Data by State Candidate Favorability Index American Presidents Died in Office Democratic Party Virtues 2021 Political Migration
www.AmericanPresidents.net • Content ©2019- AmericanPresidents.net • All Rights Reserved. The AmericanPresidents.net logo, its written content, and derived statistical values are unique to this website (unless where indicated) and is protected by all applicable domestic and international intellectual property laws. This resource uses publically-released information. No endorsement of this site by any political group or party should be implied. Information found across this site, verified through publicly available sources, is assumed to be accurate at the time of publication. Material presented throughout this website is for historical and entertainment value only.
www.AmericanPresidents.net • All Rights Reserved • Content ©2019-


text:
South Asia @ LSE
LSE's engagement with South Asia
LSE South Asia Centre
Exposing the great 'poverty reduction' lie
The UN claims that its Millennium Development Campaign has reduced poverty globally. This assertion is far from true, writes Jason Hickel.
The received wisdom comes to us from all directions: Poverty rates are declining and extreme poverty will soon be eradicated. The World Bank, the governments of wealthy countries, and – most importantly – the United Nations Millennium Campaign all agree on this narrative. Relax, they tell us. The world is getting better, thanks to the spread of free market capitalism and western aid. Development is working, and soon, one day in the very near future, poverty will be no more.
It is a comforting story, but unfortunately it is just not true. Poverty is not disappearing as quickly as they say. In fact, according to some measures, poverty has been getting significantly worse. If we are to be serious about eradicating poverty, we need to cut through the sugarcoating and face up to some hard facts.
Image credit: flickr/UNDP in Europe and Central Asia
False accounting
The most powerful expression of the poverty reduction narrative comes from the UN's Millennium Campaign. Building on the Millennium Declaration of 2000, the Campaign's main goal has been to reduce global poverty by half by 2015 – an objective that it proudly claims to have achieved ahead of schedule. But if we look beyond the celebratory rhetoric, it becomes clear that this assertion is deeply misleading.
The world's governments first pledged to end extreme poverty during the World Food Summit in Rome in 1996. They committed to reducing the number of undernourished people by half before 2015, which, given the population at the time, meant slashing the poverty headcount by 836 million. Many critics claimed that this goal was inadequate given that, with the right redistributive policies, extreme poverty could be ended much more quickly.
But instead of making the goals more robust, global leaders surreptitiously diluted it. Yale professor and development watchdog Thomas Pogge points out that when the Millennium Declaration was signed, the goal was rewritten as "Millennium Developmental Goal 1" (MDG-1) and was altered to halve the proportion (as opposed to the absolute number) of the world's people living on less than a dollar a day. By shifting the focus to income levels and switching from absolute numbers to proportional ones, the target became much easier to achieve. Given the rate of population growth, the new goal was effectively reduced by 167 million. And that was just the beginning.
After the UN General Assembly adopted MDG-1, the goal was diluted two more times. First, they changed it from halving the proportion of impoverished people in the world to halving the proportion of impoverished people in developing countries, thus taking advantage of an even faster-growing demographic denominator. Second, they moved the baseline of analysis from 2000 back to 1990, thus retroactively including all poverty reduction accomplished by China throughout the 1990s, due in no part whatsoever to the Millennium Campaign.
This statistical sleight-of-hand narrowed the target by a further 324 million. So what started as a goal to reduce the poverty headcount by 836 million has magically become only 345 million – less than half the original number. Having dramatically redefined the goal, the Millennium Campaign can claim that poverty has been halved when in fact it has not. The triumphalist narrative hailing the death of poverty rests on an illusion of deceitful accounting.
Poor numbers
But there's more. Not only have the goalposts been moved, the definition of poverty itself has been massaged in a way that serves the poverty reduction narrative. What is considered the threshold for poverty – the "poverty line" – is normally calculated by each nation for itself, and is supposed to reflect what an average human adult needs to subsist. In 1990, Martin Ravallion, an Australian economist at the World Bank, noticed that the poverty lines of a group of the world's poorest countries clustered around $1 per day. On Ravallion's recommendation, the World Bank adopted this as the first-ever International Poverty Line (IPL).
But the IPL proved to be somewhat troublesome. Using this threshold, the World Bank announced in its 2000 annual report that "the absolute number of those living on $1 per day or less continues to increase. The worldwide total rose from 1.2 billion in 1987 to 1.5 billion today and, if recent trends persist, will reach 1.9 billion by 2015." This was alarming news, especially because it suggested that the free-market reforms imposed by the World Bank and the IMF on Global South countries during the 1980s and 1990s in the name of "development" were actually making things worse.
This amounted to a PR nightmare for the World Bank. Not long after the report was released, however, their story changed dramatically and they announced the exact opposite news: While poverty had been increasing steadily for some two centuries, they said, the introduction of free-market policies had actually reduced the number of impoverished people by 400 million between 1981 and 2001.
This new story was possible because the Bank shifted the IPL from the original $1.02 (at 1985 PPP) to $1.08 (at 1993 PPP), which, given inflation, was lower in real terms. With this tiny change – a flick of an economist's wrist – the world was magically getting better, and the Bank's PR problem was instantly averted. This new IPL is the one that the Millennium Campaign chose to adopt.
The IPL was changed a second time in 2008, to $1.25 (at 2005 PPP). And once again the story improved overnight. The $1.08 IPL made it seem as though the poverty headcount had been reduced by 316 million people between 1990 and 2005. But the new IPL – even lower than the last, in real terms – inflated the number to 437 million, creating the illusion that an additional 121 million souls had been "saved" from the jaws of debilitating poverty. Not surprisingly, the Millennium Campaign adopted the new IPL, which allowed it to claim yet further chimerical gains.
A more honest view of poverty
We need to seriously rethink these poverty metrics. The dollar-a-day IPL is based on the national poverty lines of the 15 poorest countries, but these lines provide a poor foundation given that many are set by bureaucrats with very little data. More importantly, they tell us nothing about what poverty is like in wealthier countries. A 1990 survey in Sri Lanka found that 35 percent of the population fell under the national poverty line. But the World Bank, using the IPL, reported only 4 percent in the same year. In other words, the IPL makes poverty seem much less serious than it actually is.
The present IPL theoretically reflects what $1.25 could buy in the United States in 2005. But people who live in the US know it is impossible to survive on this amount. The prospect is laughable. In fact, the US government itself calculated that in 2005 the average person needed at least $4.50 per day simply to meet minimum nutritional requirements. The same story can be told in many other countries, where a dollar a day is inadequate for human existence. In India, for example, children living just above the IPL still have a 60 percent chance of being malnourished.
According to Peter Edwards of Newcastle University, if people are to achieve normal life expectancy, they need roughly double the current IPL, or a minimum of $2.50 per day. But adopting this higher standard would seriously undermine the poverty reduction narrative. An IPL of $2.50 shows a poverty headcount of around 3.1 billion, almost triple what the World Bank and the Millennium Campaign would have us believe. It also shows that poverty is getting worse, not better, with nearly 353 million more people impoverished today than in 1981. With China taken out of the equation, that number shoots up to 852 million.
Some economists go further and advocate for an IPL of $5 or even $10 – the upper boundary suggested by the World Bank. At this standard, we see that some 5.1 billion people – nearly 80 percent of the world's population – are living in poverty today. And the number is rising.
These more accurate parameters suggest that the story of global poverty is much worse than the spin doctored versions we are accustomed to hearing. The $1.25 threshold is absurdly low, but it remains in favour because it is the only baseline that shows any progress in the fight against poverty, and therefore justifies the present economic order. Every other line tells the opposite story. In fact, even the $1.25 line shows that, without factoring China, the poverty headcount is worsening, with 108 million people added to the ranks of the poor since 1981. All of this calls the triumphalist narrative into question.
This is a pressing concern; the UN is currently negotiating the new Sustainable Development Goals that will replace the Millennium Campaign in 2015, and they are set to use the same dishonest poverty metrics as before. They will leverage the "poverty reduction" story to argue for business as usual: stick with the status quo and things will keep getting better. We need to demand more. If the Sustainable Development Goals are to have any real value, they need to begin with a more honest poverty line – at least $2.50 per day – and instate rules to preclude the kind of deceit that the World Bank and the Millennium Campaign have practised to date.
Eradicating poverty in this more meaningful sense will require more than just using aid to tinker around the edges of the problem. It will require changing the rules of the global economy to make it fairer for the world's majority. Rich country governments will resist such changes with all their might. But epic problems require courageous solutions, and, with 2015 fast approaching, the moment to act is now.
This article was first published by Al Jazeera English.
Dr Jason Hickel is a Fellow at LSE's Department of Anthropology and serves as an adviser to /The Rules. Follow him on Twitter: @jasonhickel
August 29th, 2014|Development|1 Comment
Roger Leakey October 8, 2015 at 09:28
I agree with this post, but all is not lost, I have published papers on projects in Cameroon where farmers who were living on next to nothing and are now making thousands of $ from tree products – and also a book "Living with the Trees of Life – Towards the Transformation of Tropical Agriculture" (CABI, 2012). This approach also has many other environmental and social benefits, including the sequestration of carbon and land rehabilitation, which takes pressure off remaining forests
Prof Roger Leakey
Join our mailing list to receive monthly newsletters about blogs and events
Categories Select Category Agriculture Book Reviews Cities and Urban Studies Corruption Development Economy Education Environment Events Featured Gender Health History Human Rights Interviews Law LSE Media Nepal Earthquake Anniversary Oceans Photo Essays Politics Religion Religion Rural Areas Security and Foreign Policy Society and Culture Sustainable Development Goals Technology Uncategorized Urban India
Archives Select Month July 2019 June 2019 May 2019 April 2019 March 2019 February 2019 January 2019 December 2018 November 2018 October 2018 September 2018 August 2018 July 2018 June 2018 May 2018 April 2018 March 2018 February 2018 January 2018 December 2017 November 2017 October 2017 September 2017 August 2017 July 2017 June 2017 May 2017 April 2017 March 2017 February 2017 January 2017 December 2016 November 2016 October 2016 September 2016 August 2016 July 2016 June 2016 May 2016 April 2016 March 2016 February 2016 January 2016 December 2015 November 2015 October 2015 September 2015 August 2015 July 2015 June 2015 May 2015 April 2015 March 2015 February 2015 January 2015 December 2014 November 2014 October 2014 September 2014 August 2014 July 2014 June 2014 May 2014 April 2014 March 2014 February 2014 January 2014 December 2013 November 2013 October 2013 September 2013 August 2013 July 2013 June 2013 May 2013 April 2013 March 2013 February 2013 January 2013 December 2012 November 2012 October 2012 September 2012 August 2012 July 2012 June 2012
Association for Asian Studies (AAS)
Development Outlook
Governance Now
IAPS Dialogue
Ideas for India
International Growth Centre
Kafila
LSE blogs
Observer Research Foundation
Oxford India Policy Blog
Scroll.in
TheFiveFortyFive
Sign up here to receive notifications of new posts by email.
South Asia @ LSE welcomes contributions from LSE faculty, fellows, students, alumni and visitors to the school. Please write to southasia@lse.ac.uk with ideas for posts on south Asia-related topics.
Follow South Asia @ LSE on Twitter @SAsiaLSE or sign up for the newsletter by writing to southasia@lse.ac.uk.
Prospective students from South Asia can contact the Student Recruitment Office for more information about studying at the LSE.


text:
Search Results for "lawa"
ACI-NA Announces New Board Members, Other Leaders
by David Ward | Jan 6, 2021
…chair of the U.S. Policy Council. Cameron succeeds Rick Tucker, CEO of Huntsville International Airport (HSV), who will continue serving as the immediate past Chair of the U.S. Policy Council. Justin Erbacci, CEO of Los Angeles World Airports (LAWA), Larry Krauter, CEO of Spokane International Airport (GEG), Mario Rodriguez, executive…
LAX Opens On-Airport COVID-19 Testing Lab
…is another way LAX can help bolster safe travel," said Justin Erbacci, CEO of Los Angeles World Airports (LAWA), operator of LAX. "In our continuous quest to innovate and reimagine the airport experience, we partnered with the private sector to design, construct and open a first-of-its-kind on-site laboratory that will…
LAX Sky Way Construction Ahead of Schedule
by David Ward | Nov 18, 2020
…(LAWA), operator of LAX. Included in the Sky Way transformation project will be Delta's LAX central headhouse, which will include a consolidated check-in lobby, security checkpoint and bag claim facility as early as the first quarter of 2022. The overall facility will also provide connectivity after security between Terminals 2…
LAX Launches COVID-19 Testing Pilot
…government restrictions and helping our guests who must travel during the holidays and beyond to do so safely," said Justin Erbacci, CEO of Los Angeles World Airports (LAWA), operator of LAX. "This is another example of leveraging partnerships within the private sector to deploy solutions quickly that will help us…
LAX Adds Simplified Arrivals Program
by David Ward | Nov 3, 2020
Los Angeles International Airport (LAX) has become the latest U.S. airport to offer Simplified Arrivals for international passengers, after Los Angeles World Airports (LAWA), operator of LAX, announced a partnership with U.S. Customs and Border Protection (CBP) to roll out the biometric technology. Simplified Arrivals uses facial biometrics to automate…


– google search the world s information including webpages images videos and more google has many special features to help you find exactly what you re looking for map of puerto rico travelmaps map of puerto rico –the original free maps of puerto rico puerto rico map and coupons travelmaps travelmaps names on the map are pinpointed on an interactive google map .
– puerto rico google map driving directions and maps the google map above is showing puerto rico with its location central america and the caribbean geographic coordinates 18 15 n 66 30 w and the international borders of puerto rico 0 km furthermore its inland counties boundaries satellite map of puerto rico google™ — share any place google™ map of puerto rico share any place address search weather ruler regions and cities list with marked administrative centers streets roads and buildings photo panoramas puerto rico google satellite map maplandia puerto rico google satellite maps link options link to us there are several options how to link to our maps — including no elsewhere to find free google maps gazetteer search ready to use on your website google map of puerto rico nations line project the map shows puerto rico one of the greater antilles islands in the caribbean with the atlantic ocean to the north and the caribbean sea in south puerto rico lies approximately 1 600 km 1 000 mi south east of miami florida .
Related Post "30 Beautiful Puerto Rico Google Maps"


The Kingston is a local council in the south east of Melbourne, Australia where you can find a Kingston Licensed Plumber from any Suburbs along Kingston, Victoria using our Directory. Advertise your business Kingston local business or businesses using our local business directory listings.
NOTE: We currently only have Plumbers as a listing for Kingston Council. Please visit us soon for more Business listings with the best online advertising services in Kingston.


text:
"""Tests for pyglove.Dict."""

import copy
import inspect
import io
import unittest

from pyglove.core import object_utils
from pyglove.core import typing as pg_typing
from pyglove.core.symbolic import base
from pyglove.core.symbolic import flags
from pyglove.core.symbolic import object as pg_object
from pyglove.core.symbolic.dict import Dict
from pyglove.core.symbolic.list import List
from pyglove.core.symbolic.pure_symbolic import NonDeterministic
from pyglove.core.symbolic.pure_symbolic import PureSymbolic

MISSING_VALUE = object_utils.MISSING_VALUE

class DictTest(unittest.TestCase):
  """Tests for `pg.Dict`."""

  def test_init(self):
    # Schemaless dict.
    sd = Dict()
    self.assertIsNone(sd.value_spec)
    self.assertEqual(len(sd), 0)

    # Schemaless dict created from a regular dict.
    sd = Dict({'a': 1})
    self.assertIsNone(sd.value_spec)
    self.assertEqual(sd, dict(a=1))

    # Schemaless dict created from key value pairs.
    sd = Dict((('a', 1),))
    self.assertIsNone(sd.value_spec)
    self.assertEqual(sd, dict(a=1))

    # Schemaless dict created from keyword args.
    sd = Dict(a=1)
    self.assertIsNone(sd.value_spec)
    self.assertEqual(sd, dict(a=1))

    # Schemaless dict created from both a regular dict and keyword args.
    sd = Dict({'a': 1}, a=2)
    self.assertIsNone(sd.value_spec)
    self.assertEqual(sd, dict(a=2))

    # Schematized dict.
    vs = pg_typing.Dict([('a', pg_typing.Int())])
    sd = Dict(a=1, value_spec=vs)
    self.assertIs(sd.value_spec, vs)
    self.assertEqual(sd, dict(a=1))

    with self.assertRaisesRegex(
        ValueError, 'Required value is not specified.'):
      Dict(value_spec=vs)

    # Schematized dict with default value.
    vs = pg_typing.Dict([('a', pg_typing.Int(default=0))])
    sd = Dict(value_spec=vs)
    self.assertIs(sd.value_spec, vs)
    self.assertEqual(sd, dict(a=0))

    # Bad init.
    with self.assertRaisesRegex(TypeError, '.* is not iterable'):
      Dict(1)

    with self.assertRaisesRegex(
        TypeError, '.* must be a `pg.typing.Dict` object'):
      Dict(value_spec=pg_typing.Int())

  def test_partial(self):
    spec = pg_typing.Dict([
        ('a', pg_typing.Int()),
        ('b', pg_typing.Int())
    ])
    with self.assertRaisesRegex(
        ValueError, 'Required value is not specified.'):
      _ = Dict(a=1, value_spec=spec)
    self.assertTrue(Dict.partial(value_spec=spec).is_partial)

    with flags.allow_partial(True):
      self.assertTrue(Dict(value_spec=spec).is_partial)

    sd = Dict.partial(a=1, value_spec=spec)
    self.assertTrue(sd.is_partial)
    sd.rebind(b=2)
    self.assertFalse(sd.is_partial)

  def test_missing_value_as_values(self):
    sd = Dict(x=1, y=MISSING_VALUE)
    self.assertEqual(sd, Dict(x=1))
    sd.rebind(z=MISSING_VALUE, raise_on_no_change=False)
    self.assertEqual(sd, Dict(x=1))

    sd = Dict(x=1, value_spec=pg_typing.Dict([
        (pg_typing.StrKey(), pg_typing.Int())
    ]))
    sd['y'] = MISSING_VALUE
    sd['x'] = MISSING_VALUE
    self.assertNotIn('y', sd)
    self.assertNotIn('x', sd)

  def test_runtime_type_check(self):
    sd = Dict.partial(
        value_spec=pg_typing.Dict([
            ('a', pg_typing.Int()),
            ('b', pg_typing.Dict()),
            ('c', pg_typing.Dict([
                ('x', pg_typing.Int())]))
        ]))
    with self.assertRaisesRegex(TypeError, 'Expect .* but encountered .*'):
      sd.a = Dict()

    sd.b = Dict()
    sd.c = Dict(x=1)

    # Target field cannot accept Dict of different schema.
    with self.assertRaisesRegex(
        KeyError, 'Keys .* are not allowed in Schema'):
      sd.c = Dict(y=1)

    # Target field can accept Dict with the same pg_typing.
    sd.c = Dict(x=1, value_spec=pg_typing.Dict([('x', pg_typing.Int())]))

    # Target field cannot accept Dict with incompatible pg_typing.
    with self.assertRaisesRegex(
        ValueError, 'Dict .* cannot be assigned to an incompatible field'):
      sd.c = Dict(x=1, value_spec=pg_typing.Dict())

    # Target field can accept any Dict.
    sd.b = Dict(x=1, value_spec=pg_typing.Dict([('x', pg_typing.Int())]))

    # Test compatibility with Any type.
    spec = pg_typing.Dict([('x', pg_typing.Any())])
    sd = Dict(x={'a': 1, 'b': 2}, value_spec=spec)
    self.assertIsNone(sd.x.value_spec)

    # Test compatibility with Union type.
    spec = pg_typing.Dict([
        ('x', pg_typing.Union([
            pg_typing.Int(),
            pg_typing.Bool(),
            pg_typing.Dict([
                ('a', pg_typing.Int()),
                ('b', pg_typing.Str().noneable())
            ])
        ]))])
    sd = Dict(x={'a': 1}, value_spec=spec)
    self.assertEqual(sd.x, {'a': 1, 'b': None})
    self.assertEqual(
        sd.x.value_spec,
        pg_typing.Dict([
            ('a', pg_typing.Int()),
            ('b', pg_typing.Str().noneable())
        ]))

  def test_symbolization_on_nested_containers(self):
    sd = Dict(b={'c': True, 'd': []}, a=0)
    self.assertIsInstance(sd.b, Dict)
    self.assertIsInstance(sd.b.d, List)

  def test_implicit_copy_during_assignment(self):

    class A:
      pass

    # There is no impliit copy when assigning a root symbolic object to
    # another tree.
    sd = Dict(x=dict(), y=list(), z=A())
    sd2 = Dict(b=sd)

    self.assertIs(sd, sd2.b)

    # There is an implicit copy when assigning a symbolic object with
    # a parent to another tree.
    sd3 = Dict(b=sd)
    self.assertIsNot(sd, sd3.b)
    self.assertIsNot(sd.x, sd3.b.x)
    self.assertIsNot(sd.y, sd3.b.y)
    # Non-symbolic member is copied by reference.
    self.assertIs(sd.z, sd3.b.z)

    self.assertEqual(sd, sd3.b)
    self.assertEqual(sd.x, sd3.b.x)
    self.assertEqual(sd.y, sd3.b.y)

  def test_dict_with_special_chars_in_keys(self):
    # Test schema-free dict with special characters.
    sd = Dict()
    sd['x.y'] = Dict(z=List())
    self.assertIn('x.y', sd)
    self.assertEqual(sd['x.y'].z.sym_path, '[x.y].z')
    self.assertEqual(sd.rebind({'[x.y].z': 2}), {
        'x.y': {
            'z': 2
        }
    })

    # Test dict special characters against a Dict pg_typing.
    sd.clone().use_value_spec(pg_typing.Dict())
    sd.clone().use_value_spec(pg_typing.Dict([
        (pg_typing.StrKey(), pg_typing.Dict())
    ]))

  def test_spec_compatibility_during_assignment(self):
    sl = List([], value_spec=pg_typing.List(pg_typing.Dict([
        ('x', pg_typing.Int(min_value=0))])))
    sd = Dict(x=0, value_spec=pg_typing.Dict([('x', pg_typing.Int())]))
    with self.assertRaisesRegex(
        ValueError, 'Dict .* cannot be assigned to an incompatible field'):
      # A list of dicts with non-negative values cannot accept a dict that
      # possibly contains negative integers.
      sl.append(sd)

  def test_inspect(self):
    s = io.StringIO()
    sd = Dict(x=1, y={'a': 'foo'})
    sd.inspect(file=s, compact=True)
    self.assertEqual(s.getvalue(), '{x=1, y={a=\'foo\'}}\n')

    s = io.StringIO()
    sd.inspect(where=lambda v: v == 1, file=s)
    self.assertEqual(s.getvalue(), '{\n  \'x\': 1\n}\n')

  def test_list(self):
    sd = Dict(a=1, b=2)
    self.assertEqual(list(sd), ['a', 'b'])

    spec = pg_typing.Dict([
        ('b', pg_typing.Bool(default=True)),
        ('a', pg_typing.Int()),
    ])
    sd = Dict(a=1, value_spec=spec)
    self.assertEqual(list(sd), ['b', 'a'])

    sd = Dict.partial(value_spec=spec)
    self.assertEqual(list(sd), ['b', 'a'])
    self.assertEqual(sd.a, MISSING_VALUE)

  def test_len(self):
    sd = Dict(a=1, b=2)
    self.assertEqual(len(sd), 2)

    spec = pg_typing.Dict([
        ('b', pg_typing.Bool(default=True)),
        ('a', pg_typing.Int()),
    ])
    sd = Dict(a=1, value_spec=spec)
    self.assertEqual(len(sd), 2)

    sd = Dict.partial(value_spec=spec)
    self.assertEqual(len(sd), 2)
    self.assertEqual(sd.a, MISSING_VALUE)

  def test_in(self):
    sd = Dict(a=1, b=2)
    self.assertIn('a', sd)
    self.assertNotIn('x', sd)

    spec = pg_typing.Dict([
        ('b', pg_typing.Bool(default=True)),
        ('a', pg_typing.Int()),
    ])
    sd = Dict(a=1, value_spec=spec)
    self.assertIn('a', sd)
    self.assertIn('b', sd)
    self.assertNotIn('x', sd)

    sd = Dict.partial(value_spec=spec)
    self.assertIn('a', sd)
    self.assertIn('b', sd)
    self.assertNotIn('x', sd)
    self.assertEqual(sd.a, MISSING_VALUE)

  def test_setitem(self):
    # Set item in a schemaless dict.
    sd = Dict()
    sd['a'] = 0
    sd['a'] = 0
    self.assertEqual(sd, dict(a=0))
    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot modify field of a sealed Dict.'):
        sd['b'] = 1
    with self.assertRaisesRegex(KeyError, 'Key must be string type'):
      sd[0] = 1

    # Set item in a schematized dict.
    sd = Dict(value_spec=pg_typing.Dict([('a', pg_typing.Int(default=0))]))
    self.assertEqual(sd, dict(a=0))
    sd['a'] = 1
    self.assertEqual(sd, dict(a=1))
    with self.assertRaisesRegex(KeyError, 'Key \'b\' is not allowed'):
      sd['b'] = 2

    # Set item in a schematized dict with StrKey.
    sd = Dict(value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        (pg_typing.StrKey('x.*'), pg_typing.Int())
    ]))
    self.assertEqual(sd, dict(a=0))
    sd['a'] = 1
    self.assertEqual(sd, dict(a=1))
    sd['x1'] = 2
    sd['x2'] = 3
    self.assertEqual(sd, dict(a=1, x1=2, x2=3))
    with self.assertRaisesRegex(KeyError, 'Key \'y1\' is not allowed'):
      sd['y1'] = 4

  def test_getitem(self):
    sd = Dict(a=1)
    self.assertEqual(sd['a'], 1)
    with self.assertRaisesRegex(KeyError, 'x'):
      _ = sd['x']

  def test_delitem(self):
    # Delete an item from a schemaless dict.
    sd = Dict(a=1, b=0)
    del sd['a']
    self.assertEqual(len(sd), 1)

    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot del item from a sealed Dict.'):
        del sd['b']
    del sd['b']
    self.assertEqual(len(sd), 0)

    with self.assertRaisesRegex(KeyError, 'Key does not exist in Dict'):
      del sd['x']

    # Delete a required field from a schematized dict.
    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int())
    ]))
    with self.assertRaisesRegex(ValueError, 'Required value is not specified.'):
      del sd['a']

    # Deleting a key with default value restores its default value.
    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0))
    ]))
    del sd['a']
    self.assertEqual(sd, dict(a=0))

    # Deleting a dynamic key.
    sd = Dict(x1=1, x2=2, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        (pg_typing.StrKey('x.*'), pg_typing.Int())
    ]))
    del sd['x1']
    self.assertEqual(sd, dict(a=0, x2=2))

  def test_setattr(self):
    sd = Dict()
    sd.a = 0
    self.assertEqual(sd, dict(a=0))
    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot modify field of a sealed Dict.'):
        sd.b = 1

    # Set item in a schematized dict.
    sd = Dict(value_spec=pg_typing.Dict([('a', pg_typing.Int(default=0))]))
    self.assertEqual(sd, dict(a=0))
    sd.a = 1
    self.assertEqual(sd, dict(a=1))
    with self.assertRaisesRegex(KeyError, 'Key \'b\' is not allowed'):
      sd.b = 2

    # Set item in a schematized dict with StrKey.
    sd = Dict(value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        (pg_typing.StrKey('x.*'), pg_typing.Int())
    ]))
    self.assertEqual(sd, dict(a=0))
    sd.a = 1
    self.assertEqual(sd, dict(a=1))
    sd.x1 = 2
    sd.x2 = 3
    self.assertEqual(sd, dict(a=1, x1=2, x2=3))
    with self.assertRaisesRegex(KeyError, 'Key \'y1\' is not allowed'):
      sd.y1 = 4

  def test_getattr(self):
    sd = Dict(a=1)
    self.assertEqual(sd.a, 1)
    with self.assertRaisesRegex(AttributeError, 'Attribute .* does not exist'):
      _ = sd.x

  def test_delattr(self):
    # Delete an item from a schemaless dict.
    sd = Dict(a=1, b=0)
    del sd.a
    self.assertEqual(len(sd), 1)
    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot del item from a sealed Dict.'):
        del sd.b
    del sd.b
    self.assertEqual(len(sd), 0)
    with self.assertRaisesRegex(KeyError, 'Key does not exist in Dict'):
      del sd.x

    # Delete a required field from a schematized dict.
    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int())
    ]))
    with self.assertRaisesRegex(ValueError, 'Required value is not specified.'):
      del sd.a

    # Deleting a key with default value restores its default value.
    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0))
    ]))
    del sd.a
    self.assertEqual(sd, dict(a=0))

    # Deleting a dynamic key.
    sd = Dict(x1=1, x2=2, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        (pg_typing.StrKey('x.*'), pg_typing.Int())
    ]))
    del sd.x1
    self.assertEqual(sd, dict(a=0, x2=2))

  def test_pop(self):
    sd = Dict(b=0, a=1, c=2)
    self.assertEqual(sd.pop('a'), 1)
    self.assertEqual(list(sd.keys()), ['b', 'c'])

    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot del item from a sealed Dict.'):
        sd.pop('b')

    self.assertIsNone(sd.pop('a', None))
    with self.assertRaisesRegex(KeyError, 'a'):
      sd.pop('a')

    # Poping a required key triggers value error.
    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int())
    ]))
    with self.assertRaisesRegex(ValueError, 'Required value is not specified.'):
      sd.pop('a')

    # Poping a key with default value restores its default value.
    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0))
    ]))
    self.assertEqual(sd.pop('a'), 1)
    self.assertEqual(sd, dict(a=0))

  def test_popitem(self):
    sd = Dict(b=0, a=1)
    self.assertEqual(sd.popitem(), ('a', 1))
    self.assertEqual(list(sd.keys()), ['b'])

    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot pop item from a sealed Dict.'):
        sd.popitem()

    self.assertEqual(sd.popitem(), ('b', 0))
    self.assertEqual(len(sd), 0)

    with self.assertRaisesRegex(
        KeyError, 'dictionary is empty'):
      sd.popitem()

    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int())
    ]))
    with self.assertRaisesRegex(
        ValueError, 'cannot be performed on a Dict with value spec'):
      sd.popitem()

  def test_clear(self):
    sd = Dict(b=0, a=1, c=2)
    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot clear a sealed Dict.'):
        sd.clear()
    sd.clear()
    self.assertEqual(len(sd), 0)

    # Clearing a dict with required keys triggers value error.
    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int())
    ]))
    with self.assertRaisesRegex(ValueError, 'Required value is not specified.'):
      sd.clear()

    # Clearing a dict with all keys with default values restore their default
    # values.
    sd = Dict(a=1, b=2, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        ('b', pg_typing.Int(default=1))
    ]))
    sd.clear()
    self.assertEqual(sd, dict(a=0, b=1))

  def test_copy(self):

    class A:
      pass

    sd = Dict(a=0, b=A(), c=dict(d=A()))
    sd2 = sd.copy()
    self.assertIsInstance(sd2, Dict)
    self.assertEqual(sd, sd2)
    self.assertIsNot(sd, sd2)
    # Shallow copy of regular objet.
    self.assertIs(sd.b, sd2.b)
    self.assertIs(sd.c.d, sd2.c.d)

    # Deep copy of symbolic object.
    self.assertIsNot(sd.c, sd2.c)

    sd = Dict(a=0, value_spec=pg_typing.Dict([('a', pg_typing.Int())]))
    sd2 = sd.copy()
    self.assertIs(sd.value_spec, sd2.value_spec)

    # Test copy.copy
    sd = Dict(a=0, b=A(), c=dict(d=A()))
    sd3 = copy.copy(sd)
    self.assertIsInstance(sd3, Dict)
    self.assertEqual(sd, sd3)
    self.assertIsNot(sd, sd3)
    # Shallow copy of regular objet.
    self.assertIs(sd.b, sd3.b)
    self.assertIs(sd.c.d, sd3.c.d)
    # Deep copy of symbolic object.
    self.assertIsNot(sd.c, sd3.c)

    # Test copy.deepcopy.

    class B:

      def __init__(self, v):
        self.v = v

      def __eq__(self, other):
        return isinstance(other, B) and self.v == other.v

    sd = Dict(a=0, b=B(1), c=dict(d=B(2)))
    sd4 = copy.deepcopy(sd)
    self.assertIsInstance(sd4, Dict)
    self.assertEqual(sd, sd4)
    self.assertIsNot(sd, sd4)
    # Deep copy of regular objet.
    self.assertIsNot(sd.b, sd4.b)
    self.assertIsNot(sd.c.d, sd4.c.d)
    # Deep copy of symbolic object.
    self.assertIsNot(sd.c, sd4.c)

  def test_setdefault(self):
    sd = Dict(a=0, b=1)
    self.assertEqual(sd.setdefault('a'), 0)
    self.assertIsNone(sd.setdefault('x'))
    self.assertIsNone(sd.x)

    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot modify field of a sealed Dict.'):
        sd.setdefault('y')

    sd = Dict(a=0, value_spec=pg_typing.Dict([('a', pg_typing.Int())]))
    self.assertEqual(sd.setdefault('a'), 0)
    with self.assertRaisesRegex(KeyError, 'Key .* is not allowed'):
      sd.setdefault('y', 1)

    sd = Dict.partial(value_spec=pg_typing.Dict([('a', pg_typing.Int())]))
    self.assertEqual(sd.setdefault('a', 1), 1)

  def test_update(self):
    sd = Dict(b=0, a=1, c=2)
    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot rebind key .* of sealed Dict'):
        sd.update(a=1)
    sd.update({'a': MISSING_VALUE, 'b': 1, 'd': 3})
    self.assertEqual(sd, {'b': 1, 'c': 2, 'd': 3})

    sd.update((('c', 3),))
    self.assertEqual(sd, {'b': 1, 'c': 3, 'd': 3})

    sd.update(b=2, d=MISSING_VALUE, e=4)
    self.assertEqual(sd, {'b': 2, 'c': 3, 'e': 4})

    sd.update({'b': 3}, b=4)
    self.assertEqual(sd, {'b': 4, 'c': 3, 'e': 4})

    sd = Dict(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0))
    ]))
    sd.update(a=2)
    self.assertEqual(sd, dict(a=2))
    with self.assertRaisesRegex(KeyError, 'Key \'b\' is not allowed.'):
      sd.update(b=2)

  def test_use_value_spec(self):
    spec = pg_typing.Dict([
        ('a', pg_typing.Int(min_value=0)),
        ('b', pg_typing.Bool().noneable()),
    ])
    sd = Dict(a=1, b=True)
    with self.assertRaisesRegex(
        ValueError, 'Value spec for list must be a `pg.typing.Dict` object'):
      sd.use_value_spec(pg_typing.Int())
    sd.use_value_spec(spec)

    # Apply the same schema twice to verify its eligibility.
    sd.use_value_spec(spec)

    with self.assertRaisesRegex(
        RuntimeError, 'Dict is already bound with a different value spec: .*'):
      sd.use_value_spec(
          pg_typing.Dict([
              ('a', pg_typing.Int(min_value=2)),
              ('b', pg_typing.Bool().noneable()),
          ]))

    # Remove schema constraint and insert new keys.
    sd.use_value_spec(None)
    sd['c'] = 1
    self.assertIn('c', sd)

    with flags.enable_type_check(False):
      # Shall not trigger error, since type/value check is not enabled.
      sd.use_value_spec(spec)

  def test_iter(self):
    sd = Dict(b={'c': True, 'd': []}, a=0)
    self.assertEqual(list(sd), ['b', 'a'])

  def test_reversed(self):
    sd = Dict(b={'c': True, 'd': []}, a=0)
    self.assertEqual(list(reversed(sd)), ['a', 'b'])

  def test_keys(self):
    sd = Dict(b={'c': True, 'd': []}, a=0)
    self.assertEqual(list(sd.keys()), ['b', 'a'])

    # For schematized dict, key order is determined by schema.
    sd = Dict(a=1, b=2, c=3, value_spec=pg_typing.Dict([
        ('b', pg_typing.Int()),
        ('a', pg_typing.Int()),
        ('c', pg_typing.Int()),
    ]))
    self.assertEqual(list(sd.keys()), ['b', 'a', 'c'])

  def test_values(self):
    sd = Dict(b={'c': True, 'd': []}, a=0)
    self.assertEqual(list(sd.values()), [{'c': True, 'd': []}, 0])

    # For schematized dict, value order is determined by schema.
    sd = Dict(a=1, b=2, c=3, value_spec=pg_typing.Dict([
        ('b', pg_typing.Int()),
        ('a', pg_typing.Int()),
        ('c', pg_typing.Int()),
    ]))
    self.assertEqual(list(sd.values()), [2, 1, 3])

  def test_items(self):
    sd = Dict(b={'c': True, 'd': []}, a=0)
    self.assertEqual(list(sd.items()), [('b', {'c': True, 'd': []}), ('a', 0)])

    # For schematized dict, item order is determined by schema.
    sd = Dict(a=1, b=2, c=3, value_spec=pg_typing.Dict([
        ('b', pg_typing.Int()),
        ('a', pg_typing.Int()),
        ('c', pg_typing.Int()),
    ]))
    self.assertEqual(list(sd.items()), [('b', 2), ('a', 1), ('c', 3)])

  def test_non_default(self):
    sd = Dict(a=1)
    self.assertEqual(len(sd.non_default_values()), 1)

    sd = Dict(a=1, b=0, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        ('b', pg_typing.Int(default=0))
    ]))
    self.assertEqual(sd.non_default_values(), dict(a=1))

    sd = Dict(a=1, b=dict(c=2), value_spec=pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        ('b', pg_typing.Dict([
            ('c', pg_typing.Int(default=1))
        ]))
    ]))
    self.assertEqual(sd.non_default_values(), {'a': 1, 'b.c': 2})
    self.assertEqual(
        sd.non_default_values(flatten=False), {'a': 1, 'b': {'c': 2}})

    # After rebind, the non_default_values are updated.
    sd.rebind({'b.c': 1})
    self.assertEqual(sd.non_default_values(flatten=False), {'a': 1})

    # A non-schematized dict has a schematized child.
    sd = Dict(x=sd)
    self.assertIsNone(sd.value_spec)
    self.assertEqual(sd.non_default_values(), {'x.a': 1})

  def test_missing_values(self):
    sd = Dict(a=1)
    self.assertEqual(len(sd.missing_values()), 0)

    sd = Dict.partial(a=1, value_spec=pg_typing.Dict([
        ('a', pg_typing.Int()),
        ('b', pg_typing.Int())
    ]))
    self.assertEqual(sd.missing_values(), dict(b=MISSING_VALUE))

    sd = Dict.partial(b=dict(d=1), value_spec=pg_typing.Dict([
        ('a', pg_typing.Int()),
        ('b', pg_typing.Dict([
            ('c', pg_typing.Int()),
            ('d', pg_typing.Int())
        ]))
    ]))
    self.assertEqual(
        sd.missing_values(),
        {'a': MISSING_VALUE, 'b.c': MISSING_VALUE})
    self.assertEqual(
        sd.missing_values(flatten=False),
        {'a': MISSING_VALUE, 'b': {'c': MISSING_VALUE}})

    # After rebind, `missing_values` is updated.
    sd.rebind({'b.c': 1})
    self.assertEqual(
        sd.missing_values(flatten=False), {'a': MISSING_VALUE})

    # A non-schematized dict has a schematized child.
    sd = Dict(x=sd)
    self.assertIsNone(sd.value_spec)
    self.assertEqual(sd.missing_values(), {'x.a': MISSING_VALUE})

  def test_sym_has(self):
    sd = Dict(x=1, y=Dict(z=2))
    self.assertTrue(sd.sym_has('x'))
    self.assertTrue(sd.sym_has('y.z'))
    self.assertTrue(sd.sym_has(object_utils.KeyPath.parse('y.z')))
    self.assertFalse(sd.sym_has('x.z'))

  def test_sym_get(self):
    sd = Dict(x=1, y=Dict(z=2))
    self.assertEqual(sd.sym_get('x'), 1)
    self.assertEqual(sd.sym_get('y.z'), 2)
    self.assertIsNone(sd.sym_get('x.z', None))
    with self.assertRaisesRegex(
        KeyError, 'Cannot query sub-key \'z\' of object.'):
      sd.sym_get('x.z')

  def test_sym_hasattr(self):
    sd = Dict(x=1, y=Dict(z=2))
    self.assertTrue(sd.sym_hasattr('x'))
    self.assertFalse(sd.sym_hasattr('y.z'))
    self.assertFalse(sd.sym_hasattr('a'))

  def test_sym_getattr(self):
    sd = Dict(x=1, y=Dict(z=2))
    self.assertEqual(sd.sym_getattr('x'), 1)
    self.assertIsNone(sd.sym_getattr('a', None))
    with self.assertRaisesRegex(
        AttributeError,
        '.* object has no symbolic attribute \'a\'.'):
      sd.sym_getattr('a')

  def test_sym_field(self):
    sd = Dict(x=1, y=Dict(z=2))
    self.assertIsNone(sd.sym_field)

    spec = pg_typing.Dict([
        ('x', pg_typing.Int()),
        ('y', pg_typing.Dict())
    ])
    sd.use_value_spec(spec)
    self.assertIsNone(sd.sym_field)
    self.assertIs(sd.y.sym_field, spec.schema.get_field('y'))

  def test_sym_attr_field(self):
    sd = Dict(x=1, y=Dict(z=2))
    self.assertIsNone(sd.sym_attr_field('x'))

    spec = pg_typing.Dict([
        ('x', pg_typing.Int()),
        ('y', pg_typing.Dict())
    ])
    sd.use_value_spec(spec)
    self.assertIs(sd.sym_attr_field('x'), spec.schema.get_field('x'))

  def test_sym_keys(self):
    sd = Dict(x=1, y=2)
    self.assertEqual(next(sd.sym_keys()), 'x')
    self.assertEqual(list(sd.sym_keys()), ['x', 'y'])

    sd = Dict(x=1, z=3, y=2, value_spec=pg_typing.Dict([
        (pg_typing.StrKey(), pg_typing.Int())
    ]))
    self.assertEqual(next(sd.sym_keys()), 'x')
    self.assertEqual(list(sd.sym_keys()), ['x', 'z', 'y'])

  def test_sym_values(self):
    sd = Dict(x=1, y=2)
    self.assertEqual(next(sd.sym_values()), 1)
    self.assertEqual(list(sd.sym_values()), [1, 2])

    sd = Dict(x=1, z=3, y=2, value_spec=pg_typing.Dict([
        (pg_typing.StrKey(), pg_typing.Int())
    ]))
    self.assertEqual(next(sd.sym_values()), 1)
    self.assertEqual(list(sd.sym_values()), [1, 3, 2])

  def test_sym_items(self):
    sd = Dict(x=1, y=2)
    self.assertEqual(next(sd.sym_items()), ('x', 1))
    self.assertEqual(list(sd.sym_items()), [('x', 1), ('y', 2)])

    sd = Dict(x=1, z=3, y=2, value_spec=pg_typing.Dict([
        (pg_typing.StrKey(), pg_typing.Int())
    ]))
    self.assertEqual(next(sd.sym_items()), ('x', 1))
    self.assertEqual(list(sd.sym_items()), [('x', 1), ('z', 3), ('y', 2)])

  def test_sym_jsonify(self):
    # Refer to SerializationTest for more detailed tests.
    sd = Dict(x=1, y=2)
    self.assertEqual(
        sd.sym_jsonify(), {'x': 1, 'y': 2})

  def test_sym_rebind(self):
    # Refer to RebindTest for more detailed tests.
    sd = Dict(x=1, y=2)
    sd.sym_rebind(x=2)
    self.assertEqual(sd, dict(x=2, y=2))

  def test_sym_clone(self):
    class A:
      pass

    sd = Dict(x=[], y=dict(), z=A())
    sd2 = sd.clone()
    self.assertEqual(sd, sd2)
    self.assertIsNot(sd, sd2)
    # Symbolic members are always copied by value.
    self.assertIsNot(sd.x, sd2.x)
    self.assertIsNot(sd.y, sd2.y)

    # Non-symbolic members are copied by reference.
    self.assertIs(sd.z, sd2.z)

    value_spec = pg_typing.Dict([
        (pg_typing.StrKey(), pg_typing.Any())
    ])
    sd = Dict(x=list(), z=dict(), y=A(), value_spec=value_spec)
    sd2 = sd.sym_clone(deep=True)

    # Instances of `A` are compared by reference.
    # During deep clone A() is copied which results in a different instance.
    self.assertNotEqual(sd, sd2)
    self.assertIs(sd.value_spec, sd2.value_spec)
    self.assertIsNot(sd, sd2)
    self.assertIsNot(sd.x, sd2.x)
    self.assertIsNot(sd.y, sd2.y)
    self.assertIsNot(sd.z, sd2.z)

  def test_sym_origin(self):
    # Refer `object_test.test_sym_origin` for more details.
    sd = Dict(x=1)
    sd.sym_setorigin(Dict.__init__, 'constructor')
    self.assertEqual(sd.sym_origin.source, Dict.__init__)
    self.assertEqual(sd.sym_origin.tag, 'constructor')

  def test_sym_partial(self):
    # Refer to `test_partial` for more details.
    sd = Dict.partial(x=1, value_spec=pg_typing.Dict([
        ('x', pg_typing.Int()),
        ('y', pg_typing.Dict([
            ('z', pg_typing.Int())
        ])),
    ]))
    self.assertTrue(sd.sym_partial)
    sd.rebind({'y.z': 2})
    self.assertFalse(sd.sym_partial)

  def test_sym_missing(self):
    # Refer to `test_missing_values` for more details.
    sd = Dict.partial(x=1, value_spec=pg_typing.Dict([
        ('x', pg_typing.Int()),
        ('y', pg_typing.Int()),
    ]))
    self.assertEqual(sd.sym_missing(), {'y': MISSING_VALUE})

  def test_sym_nondefault(self):
    # Refer to `test_non_default_values` for more details.
    sd = Dict(x=1, value_spec=pg_typing.Dict([
        ('x', pg_typing.Int(default=0)),
        ('y', pg_typing.Dict([
            ('z', pg_typing.Int(default=1))
        ])),
    ]))
    self.assertEqual(sd.sym_nondefault(), {'x': 1})
    sd.rebind({'y.z': 2}, x=0)
    self.assertEqual(sd.sym_nondefault(), {'y.z': 2})

  def test_sym_puresymbolic(self):
    self.assertFalse(Dict(x=1).sym_puresymbolic)

    class A(PureSymbolic):
      pass

    self.assertTrue(Dict(x=A()).sym_puresymbolic)

  def test_sym_abstract(self):
    self.assertFalse(Dict(x=1).sym_abstract)
    self.assertTrue(Dict.partial(value_spec=pg_typing.Dict([
        ('x', pg_typing.Int())
    ])).sym_abstract)

    class A(PureSymbolic):
      pass

    self.assertTrue(Dict(x=A()).sym_abstract)

  def test_is_deterministic(self):

    class X(NonDeterministic):
      pass

    self.assertTrue(Dict().is_deterministic)
    self.assertFalse(Dict(x=X()).is_deterministic)
    self.assertFalse(Dict(x=[dict(y=X())]).is_deterministic)

  def test_sym_contains(self):
    sd = Dict(x=dict(y=[dict(z=1)]))
    self.assertTrue(sd.sym_contains(value=1))
    self.assertFalse(sd.sym_contains(value=2))
    self.assertTrue(sd.sym_contains(type=int))
    self.assertFalse(sd.sym_contains(type=str))

  def test_sym_eq(self):
    # Use cases that `__eq__` and `sym_eq` have the same results.
    self.assertEqual(Dict(), Dict())
    self.assertTrue(Dict().sym_eq(Dict()))
    self.assertTrue(base.eq(Dict(), Dict()))

    self.assertEqual(Dict(a=1), Dict(a=1))
    self.assertTrue(Dict(a=1).sym_eq(Dict(a=1)))
    self.assertTrue(base.eq(Dict(a=1), Dict(a=1)))

    self.assertEqual(
        Dict(a=1),
        Dict(a=1, value_spec=pg_typing.Dict([('a', pg_typing.Int())])))
    self.assertTrue(base.eq(
        Dict(a=1),
        Dict(a=1, value_spec=pg_typing.Dict([('a', pg_typing.Int())]))))
    self.assertEqual(Dict(a=Dict()), Dict(a=dict()))
    self.assertTrue(base.eq(Dict(a=Dict()), Dict(a=dict())))

    # Use case that `__eq__` rules both Python equality and `pg.eq`.
    class A:

      def __init__(self, value):
        self.value = value

      def __eq__(self, other):
        return ((isinstance(other, A) and self.value == other.value)
                or self.value == other)

    self.assertEqual(Dict(a=A(1)), Dict(a=1))
    self.assertTrue(base.eq(Dict(a=A(1)), Dict(a=1)))

    # Use case that `sym_eq` only rule `pg.eq` but not Python equality.
    class B:

      def __init__(self, value):
        self.value = value

      def sym_eq(self, other):
        return ((isinstance(other, A) and self.value == other.value)
                or self.value == other)

    self.assertNotEqual(Dict(a=B(1)), Dict(a=1))
    self.assertTrue(base.eq(Dict(a=B(1)), Dict(a=1)))

  def test_sym_ne(self):
    # Refer test_sym_eq for more details.
    self.assertNotEqual(Dict(), 1)
    self.assertTrue(base.ne(Dict(), 1))
    self.assertNotEqual(Dict(), Dict(a=1))
    self.assertTrue(base.ne(Dict(), Dict(a=1)))
    self.assertNotEqual(Dict(a=0), Dict(a=1))
    self.assertTrue(base.ne(Dict(a=0), Dict(a=1)))

  def test_sym_lt(self):
    self.assertFalse(Dict().sym_lt(MISSING_VALUE))
    self.assertFalse(Dict().sym_lt(None))
    self.assertFalse(Dict().sym_lt(True))
    self.assertFalse(Dict().sym_lt(1))
    self.assertFalse(Dict().sym_lt(2.0))
    self.assertFalse(Dict().sym_lt('abc'))
    self.assertFalse(Dict().sym_lt([]))
    self.assertFalse(Dict().sym_lt(tuple()))
    self.assertFalse(Dict().sym_lt(set()))
    self.assertFalse(Dict().sym_lt(Dict()))

    self.assertTrue(Dict().sym_lt(Dict(a=0)))
    self.assertTrue(Dict(a=0).sym_lt(Dict(a=1)))
    self.assertTrue(Dict(a=0).sym_lt(Dict(a=0, b=1)))
    self.assertTrue(Dict(a=0, b=1).sym_lt(Dict(a=1)))
    self.assertFalse(Dict(a=0).sym_lt(Dict(a=0)))
    self.assertFalse(Dict(a=1).sym_lt(Dict(a=0)))
    self.assertFalse(Dict(a=0, b=1).sym_lt(Dict(a=0)))
    self.assertFalse(Dict(a=1).sym_lt(Dict(a=0, b=1)))

    class A:
      pass

    self.assertTrue(Dict().sym_lt(A()))

  def test_sym_gt(self):
    self.assertTrue(Dict().sym_gt(MISSING_VALUE))
    self.assertTrue(Dict().sym_gt(None))
    self.assertTrue(Dict().sym_gt(True))
    self.assertTrue(Dict().sym_gt(1))
    self.assertTrue(Dict().sym_gt(2.0))
    self.assertTrue(Dict().sym_gt('abc'))
    self.assertTrue(Dict().sym_gt([]))
    self.assertTrue(Dict().sym_gt((1,)))
    self.assertTrue(Dict().sym_gt(set()))

    self.assertTrue(Dict(a=0).sym_gt(Dict()))
    self.assertTrue(Dict(a=1).sym_gt(Dict(a=0)))
    self.assertTrue(Dict(a=0, b=1).sym_gt(Dict(a=0)))
    self.assertTrue(Dict(b=0).sym_gt(Dict(a=0)))
    self.assertFalse(Dict().sym_gt(Dict(a=0)))
    self.assertFalse(Dict(a=0).sym_gt(Dict(a=1)))
    self.assertFalse(Dict(a=0).sym_gt(Dict(a=0, b=1)))
    self.assertFalse(Dict(a=0, b=1).sym_gt(Dict(a=1)))
    self.assertFalse(Dict(a=0).sym_gt(Dict(a=0)))
    self.assertFalse(Dict(a=0).sym_gt(Dict(a=1)))
    self.assertFalse(Dict(a=0).sym_gt(Dict(a=0, b=1)))

    class A:
      pass

    self.assertFalse(Dict().sym_gt(A()))

  def test_sym_hash(self):
    self.assertEqual(hash(Dict()), hash(Dict()))
    self.assertEqual(hash(Dict(a=1)), hash(Dict(a=1)))
    self.assertEqual(hash(Dict(a=dict(x=1))), hash(Dict(a=dict(x=1))))
    self.assertNotEqual(hash(Dict()), hash(Dict(a=1)))
    self.assertNotEqual(hash(Dict(a=1)), hash(Dict(a=2)))

    class A:
      pass

    a = A()
    b = A()
    self.assertNotEqual(hash(Dict(x=a)), hash(Dict(x=b)))

    class B:

      def __init__(self, value):
        self.value = value

      def __hash__(self):
        return hash((B, self.value))

    a = B(1)
    b = B(1)
    self.assertEqual(hash(a), hash(b))
    self.assertEqual(hash(Dict(x=a)), hash(Dict(x=b)))
    self.assertNotEqual(hash(Dict(x=B(1))), hash(Dict(x=B(2))))

  def test_sym_parent(self):
    sd = Dict(x=dict(a=1), y=[])
    self.assertIsNone(sd.sym_parent)

    self.assertIs(sd.x.sym_parent, sd)
    self.assertIs(sd.y.sym_parent, sd)

    pd = Dict(a=sd)
    self.assertIs(sd.sym_parent, pd)

  def test_sym_path(self):
    sd = Dict(x=dict(a=dict()), y=[dict(b=dict())])
    self.assertEqual(sd.sym_path, '')
    self.assertEqual(sd.x.sym_path, 'x')
    self.assertEqual(sd.x.a.sym_path, 'x.a')
    self.assertEqual(sd.y[0].b.sym_path, 'y[0].b')

    sd.sym_setpath(object_utils.KeyPath('a'))
    self.assertEqual(sd.sym_path, 'a')
    self.assertEqual(sd.x.sym_path, 'a.x')
    self.assertEqual(sd.x.a.sym_path, 'a.x.a')
    self.assertEqual(sd.y[0].b.sym_path, 'a.y[0].b')

  def test_accessor_writable(self):
    sd = Dict(a=0, accessor_writable=False)
    with self.assertRaisesRegex(
        base.WritePermissionError,
        'Cannot modify Dict field by attribute or key while accessor_writable '
        'is set to False.'):
      sd.a = 2

    with flags.allow_writable_accessors(True):
      sd.a = 2
      self.assertEqual(sd.a, 2)

    with self.assertRaisesRegex(
        base.WritePermissionError,
        'Cannot modify Dict field by attribute or key while accessor_writable '
        'is set to False.'):
      sd['a'] = 1

    with flags.allow_writable_accessors(True):
      sd['a'] = 1
      self.assertEqual(sd.a, 1)

    with self.assertRaisesRegex(
        base.WritePermissionError,
        'Cannot del Dict field by attribute or key while accessor_writable is '
        'set to False.'):
      del sd.a

    with flags.allow_writable_accessors(True):
      del sd.a
      self.assertNotIn('a', sd)
      sd.a = 1

    with self.assertRaisesRegex(
        base.WritePermissionError,
        'Cannot del Dict field by attribute or key while accessor_writable is '
        'set to False.'):
      del sd['a']

    with flags.allow_writable_accessors(True):
      del sd['a']
      self.assertNotIn('a', sd)

    sd.rebind(a=2)
    self.assertEqual(sd.a, 2)

    # Delete key with rebind.
    sd.rebind(a=MISSING_VALUE)
    self.assertEqual(0, len(sd))

    sd.set_accessor_writable(True)
    sd.a = 1
    self.assertEqual(sd.a, 1)
    with flags.allow_writable_accessors(False):
      with self.assertRaisesRegex(
          base.WritePermissionError,
          'Cannot modify Dict field by attribute or key while '
          'accessor_writable is set to False.'):
        sd.a = 2

      with self.assertRaisesRegex(
          base.WritePermissionError,
          'Cannot modify Dict field by attribute or key while '
          'accessor_writable is set to False.'):
        sd['a'] = 2

      with self.assertRaisesRegex(
          base.WritePermissionError,
          'Cannot del Dict field by attribute or key while accessor_writable is '
          'set to False.'):
        del sd.a

      with self.assertRaisesRegex(
          base.WritePermissionError,
          'Cannot del Dict field by attribute or key while accessor_writable is '
          'set to False.'):
        del sd['a']

  def test_mark_missing_values(self):
    # For schemaless Dict.
    sd = Dict(x=1, y=2)
    self.assertIn('x', sd)
    self.assertIn('y', sd)

    # Set field to MISSING_VALUE will delete field.
    sd.x = MISSING_VALUE
    self.assertNotIn('x', sd)

    # Clear will empty the dict.
    sd.clear()
    self.assertEqual(0, len(sd))

    # Mark missing values in symbolic Dict.
    value_spec = pg_typing.Dict([
        ('x', pg_typing.Int()),
        ('y', pg_typing.Dict([
            ('z', pg_typing.Bool(True)),
            ('p', pg_typing.Str())]))
    ])
    sd = Dict.partial(value_spec=value_spec)
    self.assertEqual(sd, {
        'x': MISSING_VALUE,
        'y': {
            'z': True,
            'p': MISSING_VALUE,
        }
    })
    sd.y.z = False

    # Assign MISSING_VALUE to a field with default value
    # will reset field to default value
    sd.y.z = MISSING_VALUE
    self.assertEqual(sd.y.z, True)

    # Clear will reset default values.
    sd.clear()
    self.assertEqual(sd, {
        'x': MISSING_VALUE,
        'y': {
            'z': True,
            'p': MISSING_VALUE,
        }
    })

  def test_seal(self):
    sd = Dict({'a': 0}, sealed=True)
    self.assertTrue(sd.is_sealed)

    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot modify field of a sealed Dict.'):
      sd.a = 1

    with flags.as_sealed(False):
      sd.a = 2
      self.assertEqual(sd.a, 2)
      # Object-level is_sealed flag is not modified.
      self.assertTrue(sd.is_sealed)

    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot modify field of a sealed Dict.'):
      sd['a'] = 1

    with flags.as_sealed(False):
      sd['a'] = 1
      self.assertEqual(sd['a'], 1)

    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot rebind key .* of sealed Dict.'):
      sd.rebind(a=1)

    with flags.as_sealed(False):
      sd.rebind(a=2)
      self.assertEqual(sd.a, 2)

    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot rebind key .* of sealed Dict.'):
      sd.update({'a': 1})

    with flags.as_sealed(False):
      sd.update({'a': 1})
      self.assertEqual(sd.a, 1)

    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot del item from a sealed Dict.'):
      del sd['a']

    with flags.as_sealed(False):
      del sd['a']
      self.assertNotIn('a', sd)
      sd.a = 1

    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot del item from a sealed Dict.'):
      del sd.a

    with flags.as_sealed(False):
      del sd.a
      self.assertNotIn('a', sd)
      sd.a = 1

    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot clear a sealed Dict.'):
      sd.clear()

    with flags.as_sealed(False):
      sd.clear()
      self.assertEqual(sd, {})

    # Unseal.
    sd.seal(False)
    self.assertFalse(sd.is_sealed)

    # Test repeated seal has no side effect.
    sd.seal(False)
    self.assertFalse(sd.is_sealed)

    sd.a = 2
    sd['b'] = 1
    self.assertEqual(sd.a, 2)
    self.assertEqual(sd.b, 1)
    sd.rebind(b=2)

    with flags.as_sealed(True):
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot modify field of a sealed Dict.'):
        sd.a = 1

      # Object-level sealed state is not changed,
      self.assertFalse(sd.is_sealed)

    # Seal again.
    self.assertEqual(sd.b, 2)
    sd.seal()
    with self.assertRaisesRegex(
        base.WritePermissionError, 'Cannot rebind key .* of sealed Dict.'):
      sd.rebind(a=0)

    # Test nested sealed Dict.
    sd = Dict(a=Dict())
    self.assertFalse(sd.is_sealed)
    self.assertFalse(sd.a.is_sealed)
    sd.seal()
    self.assertTrue(sd.is_sealed)
    self.assertTrue(sd.a.is_sealed)

class RebindTest(unittest.TestCase):
  """Dedicated tests for `pg.Dict.rebind`."""

  def test_rebind_on_schemaless_dicts(self):
    sd = Dict(a=Dict(b=1, c=2), d=3)
    sd.rebind({
        # Update a.b.
        'a.b': 'bar',
        # Delete a.c.
        'a.c': MISSING_VALUE,
        # Insert a.x.
        'a.x': 1,
        # Insert y.
        'y': 2,
        # No-op.
        'z': MISSING_VALUE
    })
    self.assertEqual(sd, {
        'a': {
            'b': 'bar',
            'x': 1
        },
        'd': 3,
        'y': 2
    })

  def test_rebind_with_kwargs(self):
    # Rebind using only kwargs.
    sd = Dict(a=1, b=2, c=3)
    sd.rebind(a=2, c=4)
    self.assertEqual(sd, dict(a=2, b=2, c=4))

    # Rebind using both update dict and kwargs.
    sd = Dict(a=1, b=2, c=3)
    sd.rebind({'a': 2, 'b': 3}, a=3, c=3)
    self.assertEqual(sd, dict(a=3, b=3, c=3))

  def test_rebind_with_typing(self):
    spec = pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        ('b', pg_typing.Str(regex='foo.*')),
        ('c', pg_typing.Dict([
            ('x', pg_typing.Int(min_value=1, default=1)),
            ('y', pg_typing.Bool()),
        ]))
    ])
    sd = Dict(b='foo', c=dict(x=1, y=True), value_spec=spec)
    sd.rebind({
        'b': 'foo1',
        'c.x': 2
    })
    self.assertEqual(sd, {
        'a': 0,
        'b': 'foo1',
        'c': {'x': 2, 'y': True}
    })
    with self.assertRaisesRegex(
        ValueError, '.* does not match regular expression'):
      sd.rebind({'b': 'bar'})

    with self.assertRaisesRegex(ValueError, '.* is out of range'):
      sd.rebind({'c.x': 0})

  def test_rebind_with_reset_default(self):
    spec = pg_typing.Dict([
        ('a', pg_typing.Int(default=0)),
        ('b', pg_typing.Str()),
        ('c', pg_typing.Dict([
            ('x', pg_typing.Int(default=1)),
            ('y', pg_typing.Bool(default=False)),
        ]))
    ])
    sd = Dict(a=1, b='foo', c=dict(x=0, y=True), value_spec=spec)

    # Reset the default value of `a` and `c`, and update `b`.
    sd.rebind({
        'a': MISSING_VALUE,
        'b': 'bar',
        'c': MISSING_VALUE,
    })
    self.assertEqual(sd, {
        'a': 0,
        'b': 'bar',
        'c': {'x': 1, 'y': False}
    })

  def test_rebind_on_sealed_dict(self):
    sd = Dict(a=1, b=2)
    with flags.as_sealed():
      with self.assertRaisesRegex(
          base.WritePermissionError, 'Cannot rebind key .* of sealed Dict'):
        sd.rebind(a=2)
    sd.rebind(a=2)
    self.assertEqual(sd.a, 2)

  def test_rebind_with_no_updates(self):
    def on_dict_change(field_updates):
      del field_updates
      assert False
    sd = Dict(a=1, b=2, c=3, onchange_callback=on_dict_change)
    with self.assertRaisesRegex(
        ValueError, 'There are no values to rebind'):
      sd.rebind()
    with self.assertRaisesRegex(
        ValueError, 'There are no values to rebind'):
      sd.rebind(lambda k, v, p: v)
    sd.rebind(a=1, b=2, raise_on_no_change=False)

  def test_rebind_with_skipping_notification(self):
    def on_dict_change(field_updates):
      del field_updates
      assert False
    sd = Dict(a=1, b=2, c=3, onchange_callback=on_dict_change)
    sd.rebind(a=2, skip_notification=True)
    self.assertEqual(sd, dict(a=2, b=2, c=3))

  def test_rebind_with_field_updates_notification(self):
    updates = []
    def on_dict_change(field_updates):
      updates.append(field_updates)

    sd = Dict(
        a=1,
        b=Dict(x=1,
               y=Dict(onchange_callback=on_dict_change),
               onchange_callback=on_dict_change),
        c=List([Dict(p=1, onchange_callback=on_dict_change)],
               onchange_callback=on_dict_change),
        d='foo',
        onchange_callback=on_dict_change)
    sd.rebind({
        'a': 2,
        'b.x': 2,
        'b.y.z': 1,
        'c[0].p': MISSING_VALUE,
        'c[0].q': 2,
        'd': 'foo',  # Unchanged.
        'e': 'bar'
    })
    self.assertEqual(updates, [
        {  # Notification to `sd.c[0]`.
            'p': base.FieldUpdate(
                object_utils.KeyPath.parse('c[0].p'),
                target=sd.c[0],
                field=None,
                old_value=1,
                new_value=MISSING_VALUE),
            'q': base.FieldUpdate(
                object_utils.KeyPath.parse('c[0].q'),
                target=sd.c[0],
                field=None,
                old_value=MISSING_VALUE,
                new_value=2),
        },
        {  # Notification to `sd.c`.
            '[0].p': base.FieldUpdate(
                object_utils.KeyPath.parse('c[0].p'),
                target=sd.c[0],
                field=None,
                old_value=1,
                new_value=MISSING_VALUE),
            '[0].q': base.FieldUpdate(
                object_utils.KeyPath.parse('c[0].q'),
                target=sd.c[0],
                field=None,
                old_value=MISSING_VALUE,
                new_value=2),
        },
        {  # Notification to `sd.b.y`.
            'z': base.FieldUpdate(
                object_utils.KeyPath.parse('b.y.z'),
                target=sd.b.y,
                field=None,
                old_value=MISSING_VALUE,
                new_value=1),
        },
        {  # Notification to `sd.b`.
            'x': base.FieldUpdate(
                object_utils.KeyPath.parse('b.x'),
                target=sd.b,
                field=None,
                old_value=1,
                new_value=2),
            'y.z': base.FieldUpdate(
                object_utils.KeyPath.parse('b.y.z'),
                target=sd.b.y,
                field=None,
                old_value=MISSING_VALUE,
                new_value=1),
        },
        {  # Notification to `sd`.
            'a': base.FieldUpdate(
                object_utils.KeyPath.parse('a'),
                target=sd,
                field=None,
                old_value=1,
                new_value=2),
            'b.x': base.FieldUpdate(
                object_utils.KeyPath.parse('b.x'),
                target=sd.b,
                field=None,
                old_value=1,
                new_value=2),
            'b.y.z': base.FieldUpdate(
                object_utils.KeyPath.parse('b.y.z'),
                target=sd.b.y,
                field=None,
                old_value=MISSING_VALUE,
                new_value=1),
            'c[0].p': base.FieldUpdate(
                object_utils.KeyPath.parse('c[0].p'),
                target=sd.c[0],
                field=None,
                old_value=1,
                new_value=MISSING_VALUE),
            'c[0].q': base.FieldUpdate(
                object_utils.KeyPath.parse('c[0].q'),
                target=sd.c[0],
                field=None,
                old_value=MISSING_VALUE,
                new_value=2),
            'e': base.FieldUpdate(
                object_utils.KeyPath.parse('e'),
                target=sd,
                field=None,
                old_value=MISSING_VALUE,
                new_value='bar')
        }
    ])

  def test_rebind_with_fn(self):
    sd = Dict(a=1, b=dict(x=2, y='foo', z=[0, 1, 2]))
    def increment(k, v, p):
      del k, p
      if isinstance(v, int):
        return v + 1
      return v
    sd.rebind(increment)
    self.assertEqual(sd, Dict(a=2, b=Dict(x=3, y='foo', z=[1, 2, 3])))

  def test_notify_on_change(self):
    context = Dict(num_changes=0)
    def increment_change(unused_updates):
      context.num_changes += 1

    sd = Dict(a=1, onchange_callback=increment_change)
    sd.a = 2
    sd.b = 4
    del sd['a']
    sd.a = 4
    self.assertEqual(context.num_changes, 4)

    context.num_changes = 0
    sd = Dict(a=1, onchange_callback=increment_change)
    with flags.notify_on_change(False):
      sd.a = 2
      sd.b = 4
      del sd['a']
      sd.a = 4
    self.assertEqual(context.num_changes, 0)

  def test_bad_rebind(self):
    # Rebind is invalid on root object.
    with self.assertRaisesRegex(
        KeyError, 'Root key .* cannot be used in .*rebind.'):
      Dict().rebind({'': 1})

    # Rebind is invalid on non-symbolic object.
    with self.assertRaisesRegex(
        KeyError, 'Cannot rebind key .* is not a symbolic type.'):
      Dict(a=1).rebind({'a.x': 1})

    with self.assertRaisesRegex(
        ValueError, 'Argument \'path_value_pairs\' should be a dict.'):
      Dict().rebind(1)

    with self.assertRaisesRegex(
        ValueError, 'There are no values to rebind.'):
      Dict().rebind({})

    with self.assertRaisesRegex(
        KeyError, 'Key must be string type. Encountered 1'):
      Dict().rebind({1: 1})

    with self.assertRaisesRegex(
        ValueError, 'Required value is not specified.'):
      Dict(a=1, value_spec=pg_typing.Dict([('a', pg_typing.Int())])).rebind({
          'a': MISSING_VALUE})

class SerializationTest(unittest.TestCase):
  """Dedicated tests for `pg.Dict` serialization."""

  def test_schemaless(self):
    sd = Dict()
    sd.b = 0
    sd.c = None
    sd.a = 'foo'

    # Key order is preserved.
    self.assertEqual(sd.to_json_str(), '{"b": 0, "c": null, "a": "foo"}')

  def test_schematized(self):
    sd = Dict.partial(
        x=1,
        value_spec=pg_typing.Dict([
            ('w', pg_typing.Str()),
            ('x', pg_typing.Int()),
            ('y', pg_typing.Str().noneable()),
            # Frozen field shall not be written.
            ('z', pg_typing.Bool(True).freeze()),
        ]))
    self.assertEqual(sd.to_json_str(), '{"x": 1, "y": null}')

  def test_serialization_with_converter(self):

    class A:

      def __init__(self, value: float):
        self.value = value

      def __eq__(self, other):
        return isinstance(other, A) and other.value == self.value

    spec = pg_typing.Dict([
        ('x', pg_typing.Int()),
        ('y', pg_typing.Object(A))
    ])
    sd = Dict(x=1, y=A(2.0), value_spec=spec)
    with self.assertRaisesRegex(
        ValueError, 'Cannot convert complex type .* to JSON.'):
      sd.to_json_str()

    pg_typing.register_converter(A, float, convert_fn=lambda x: x.value)
    pg_typing.register_converter(float, A, convert_fn=A)

    self.assertEqual(sd.to_json(), {'x': 1, 'y': 2.0})
    self.assertEqual(Dict.from_json(sd.to_json(), value_spec=spec), sd)

    self.assertEqual(sd.to_json_str(), '{"x": 1, "y": 2.0}')
    self.assertEqual(base.from_json_str(sd.to_json_str(), value_spec=spec), sd)

  def test_hide_default_values(self):
    sd = Dict.partial(
        x=1,
        value_spec=pg_typing.Dict([
            ('w', pg_typing.Str()),
            ('x', pg_typing.Int()),
            ('y', pg_typing.Str().noneable()),
            # Frozen field shall not be written.
            ('z', pg_typing.Bool(True).freeze()),
        ]))
    self.assertEqual(sd.to_json_str(hide_default_values=True), '{"x": 1}')

  def test_from_json(self):
    spec = pg_typing.Dict([
        ('w', pg_typing.Str()),
        ('x', pg_typing.Int()),
        ('y', pg_typing.Str().noneable()),
        # Frozen field shall not be written.
        ('z', pg_typing.Bool(True).freeze()),
    ])
    self.assertEqual(
        base.from_json_str('{"x": 1}').use_value_spec(spec, allow_partial=True),
        Dict.partial(x=1, value_spec=spec))

  def test_to_json_on_regular_dict(self):
    self.assertEqual(
        base.to_json_str({
            'x': 1,
            'y': None,
            'z': True
        }), '{"x": 1, "y": null, "z": true}')

  def test_unsupported_types(self):

    class A:
      pass

    with self.assertRaisesRegex(
        ValueError, 'Cannot convert complex type .* to JSON.'):
      base.to_json(Dict(x=A()))

class FormatTest(unittest.TestCase):
  """Dedicated tests for `pg.Dict.format`."""

  def setUp(self):
    super().setUp()

    @pg_object.members([
        ('x', pg_typing.Int(1)),
        ('y', pg_typing.Str()),
        ('z', pg_typing.Dict([
            ('p', pg_typing.List(pg_typing.Bool().noneable())),
            ('q', pg_typing.Enum('foo', ['foo', 'bar'])),
            ('t', pg_typing.Str())
        ]))
    ])
    class A(pg_object.Object):
      pass

    value_spec = pg_typing.Dict([
        ('a1', pg_typing.Int(1), 'Field a1.'),
        ('a2', pg_typing.Dict([
            ('b1', pg_typing.Dict([
                ('c1', pg_typing.List(pg_typing.Dict([
                    ('d1', pg_typing.Str(), 'Field d1.'),
                    ('d2', pg_typing.Bool(True)),
                    ('d3', pg_typing.Object(A)),
                ])), 'Field c1.')]), 'Field b1.')]),
         'Field a2.')
    ])
    self._dict = Dict.partial({
        'a1': 1,
        'a2': {
            'b1': {
                'c1': [{
                    'd3': A.partial(x=2, z={'p': [None, True], 't': 'foo'})
                }]
            }
        }
    }, value_spec=value_spec)

  def test_compact(self):
    self.assertEqual(
        self._dict.format(compact=True),
        '{a1=1, a2={b1={c1=[0: {d1=MISSING_VALUE, d2=True, d3='
        'A(x=2, y=MISSING_VALUE, z={p=[0: None, 1: True], '
        'q=\'foo\', t=\'foo\'})}]}}}')

  def test_noncompact_nonverbose(self):
    self.assertEqual(
        self._dict.format(compact=False, verbose=False),
        inspect.cleandoc("""{
          a1 = 1,
          a2 = {
            b1 = {
              c1 = [
                0 : {
                  d1 = MISSING_VALUE(Str()),
                  d2 = True,
                  d3 = A(
                    x = 2,
                    y = MISSING_VALUE(Str()),
                    z = {
                      p = [
                        0 : None,
                        1 : True
                      ],
                      q = 'foo',
                      t = 'foo'
                    }
                  )
                }
              ]
            }
          }
        }"""))

  def test_noncompact_verbose(self):
    self.assertEqual(
        self._dict.format(compact=False, verbose=True),
        inspect.cleandoc("""{
          # Field a1.
          a1 = 1,

          # Field a2.
          a2 = {
            # Field b1.
            b1 = {
              # Field c1.
              c1 = [
                0 : {
                  # Field d1.
                  d1 = MISSING_VALUE(Str()),
                  d2 = True,
                  d3 = A(
                    x = 2,
                    y = MISSING_VALUE(Str()),
                    z = {
                      p = [
                        0 : None,
                        1 : True
                      ],
                      q = 'foo',
                      t = 'foo'
                    }
                  )
                }
              ]
            }
          }
        }"""))

  def test_noncompact_verbose_hide_default_and_missing_values(self):
    self.assertEqual(
        self._dict.format(
            compact=False,
            verbose=True,
            hide_default_values=True,
            hide_missing_values=True),
        inspect.cleandoc("""{
          # Field a2.
          a2 = {
            # Field b1.
            b1 = {
              # Field c1.
              c1 = [
                0 : {
                  d3 = A(
                    x = 2,
                    z = {
                      p = [
                        0 : None,
                        1 : True
                      ],
                      t = 'foo'
                    }
                  )
                }
              ]
            }
          }
        }"""))

if __name__ == '__main__':
  unittest.main()

redpajama_set_name: RedPajamaGithub


Special Deputy Mickey Bolton keeps track of offenders by maintaining relationships with state, federal, and local agencies. He maintains address and registration information for sexual and violent offenders gathered through public and professional business telephone calls. Special Deputy Bolton can provide educational meetings on sexual and violent offenders for schools and local neighborhoods with a PowerPoint presentation.
Special Deputy Bolton has been employed with the BSBLED since March 2005 and works with minimal supervision. Special Deputy Bolton maintains good communication with the public, local and state law enforcement, as well as numerous federal agencies, including detention centers, Offices of the Attorney General, ICE, and the U.S. Marshal's Service.


I thoroughly enjoyed my many trips around the world quilt. You can find out more about the pattern and quilt along in my previous post. It was a complete pleasure to make this quilt from start to finish. I was excited to pull scrap strips in my favorite color palette of the moment. I ended up raiding my stash as well, but it was worth it. I made 25 blocks; the quilt finishes at 60 inches square.
The colors are moody and wintry, and the rich teals are a new obsession for me. It is a hard color to find in fabrics. I want it rich and saturated, without white accents that disrupt the value. If you have a favorite dark turquoise or teal, let me know!
The Anna Maria Horner pastry voile I chose for the binding is the perfect teal. And let me tell you, if you have not hand sewn a voile binding, you are missing out! It was such a pleasure. I will definitely be using it again.
I decided to try out a new free motion quilting design. I thought the busy quilt top would camouflage any imperfections, which thankfully seems to have been the case. I picked the concentric circles design from Angela's first book. The design is really quick and easy, although next time I will make the circles on a smaller scale (less than four inches across would be good I think), as the large circles were hard to keep even on my tiny machine. I really love the texture they create, almost like raindrop ripples in a puddle.
The back is a text print from the Mama Said Sew line. I saw it on lucyandnorman's gorgeous scrappy trip along quilt and had to have it.
You can find the throw sized quilt in my etsy shop.
If you instagram (I'm @teaginny over there), no doubt you've seen many #scrappytripalong blocks popping up. There is also a flickr group for this informal quilt along. The tutorial for the block can be found on Quiltville, and it is rather ingenious. It uses strip sets to create blocks like the ones below.
I started with my scrap bin of 2.5 inch strips and chose any fabrics in plum, teal, aqua, gray, and white. I've wanted to work with this color scheme for a while, so it's good to finally see them all together. It feels rich and wintry to me.
When you put the blocks together in this configuration, you get the effect of many trips around the world. If you keep it scrappy, the "trips" are a little wonky and broken up, which makes it all the more interesting to me. It is really fun to arrange and rearrange them on my design wall.
I have 16 blocks so far with strips for another four. Perhaps 25 would be a better size. It's a lot of fun, so I'm not in a rush to finish.
No, I didn't make this pillow quickly today. I actually finished it last month; I just forgot to show it. All 144 squares (1.5 inch finished) came from my scrap bin and provided some much needed therapeutic sewing during the holidays.
I quilted it simply with a crosshatch through every other squares and added a zipper closure using my favorite method. Here you an see what it looks like then you use matching fabric for the flap, rather than contrasting as I did with my last pillow.
I bound the edges like a quilt. A thin binding strip looks a lot like piping and frames the pillow nicely. This quilted cover fits an 18 inch pillow form. You can find it in my etsy shop.
Here is a quick look back at some of my favorite projects from 2012. Here's to lots of fun sewing in 2013!


text:
Refrigerated Frozen Foods logo
Refrigerated Foods Processor of the Year
Frozen Foods Processor of the Year
Foodservice Distributor of the Year
Sustainable Processor of the year
Top 150 Frozen Food Processors
Best New Retail Products
New Retail Products
Technology Showcase
Directories & Guides
Cold Storage Warehouse Guide
Cold Storage Construction Guide
Trade Education
Directory of Associations
Nominations for RFF Awards
R&FF Store
Food Automation & Manufacturing (FA&M) Conference & Expo
Home » Inline Plastics opens Innovation Center
Inline Plastics opens Innovation Center
This new Innovation Center is dedicated to the research, development and engineering of food packaging solutions.
KEYWORDS plastics / thermoforming
Inline Plastics Corp., Shelton, Conn., completed a total facility renovation to its Innovation Center in Milford, Conn.
After more than a year of planning, design and construction, one of the original Inline Plastics buildings on Seemans Lane in Milford was completely upgraded and rebuilt to house a high-tech corporate research and development (R&D) facility. This new Innovation Center is dedicated to the research, development and engineering of food packaging solutions.
"The Inline Plastics RD&E team is constantly developing new ideas and products that transform how food products are packaged and marketed to consumers," says Steve Introne, director of research, development, engineering and corporate quality. "This new Innovation Center was strategically designed to not only foster that creativity, but [also] increase the efficiency and effectiveness of our new product development processes. This enables us to quickly get new ideas and products into the hands of our customers."
"Our investment in building the Innovation Center showcases our commitment to excellence and continuous improvement to our product lines," says Tom Orkisz, president and owner. "This Innovation Center is the home of a high-performance team that can capitalize on the tools and environment that inspire creative thinking, promote collaboration and streamline communication in the research, development and engineering of the next generation of Inline Plastics' products."
In addition to the structural and building renovations, which included adding a technical library and upgraded quality lab, the company also invested in new R&D equipment consisting of rapid prototyping, state-of-the-art sample size and full-size market testing thermoforming machines, which are used in the design, development and testing of new products before mass production. Individual and group workspaces are laid out in a cross-functional format and integrated with smart technologies to improve collaboration and information sharing.
Hussmann Opens Global Hub Retail Innovation Center
Cambridge consolidates 2 facilities, opens innovation center
Bettcher Industries opens state-of-the-art Innovation Center
InContext Solutions opens virtual reality innovation center on East Coast
Handbook of Frozen Food Processing and Packaging, Second Edition
Trends and Opportunities in the US Packaging Industry...
Methods for Developing New Food Products, Expanded Second Edition
What's Hot in Cold eNewsletter
New Smithfield Foods President and CEO Dennis Organ Appoints 16 to Executive Leadership Team
Greenleaf Foods: Our 2021 Refrigerated Foods Processor of the Year
Honor Foods to Open $50 Million Temperature-Controlled F&B Distribution Facility in Philadelphia
Is Vaccine Distribution the Future of Cold Storage?
Dedeaux Properties Sells Four Los Angeles Cold Storage and Food Processing Facilities Totaling Nearly 300,000 Square Feet
COVID-19 Vaccine and Cold Storage
Will the COVID-19 vaccine and cold storage necessary to distribute it affect your business plans for 2021?
Slightly—we already planned to expand our cold storage capacity in 2021 for e-commerce, so we'll likely add or acquire more to accommodate the vaccine.
Not at all—we have plenty of cold storage capacity to meet vaccine distribution demands.
Very much—we weren't planning to add cold storage capacity in 2021 so we'll need to decide how existing space can be re-allocated to store the vaccine.
Refrigerated & Frozen Foods Magazine
Check out the December 2020 edition of Refrigerated & Frozen Foods: Cold Foods Processors to Watch in 2021, McCain Foods adapting and expanding, cold facility flooring, cold food safety regulations for 2021 and much more!


text:
Previous Next Table of Contents
e2v camera system to search for 'dark energy'
Fourteen of the UK company's high-end CCD devices will feature in an ambitious new astronomical project.
T250 telescope: under construction
e2v Technologies has won a $5.4 million, two-year contract to supply a complete camera system for a major new astronomy project in Spain that is seeking to answer one of the mysteries of the Universe.
The UK firm, which provides high-end CCD and CMOS detectors for scientific, defense and industrial applications, will deliver a gigapixel-scale camera to be used in a five-year survey of the northern sky by the Observatorio Astrofísico de Javalambre's new T250 wide-field 2.5 meter telescope, which is currently under construction in Belgium.
The observatory, based in eastern Spain between Madrid and Valencia, said that e2v was one of five bidders for the camera system contract.
Astronomers working on the survey, which is a collaboration between institutes in Brazil and Spain, will be aiming to shed some light on the cosmological mystery of 'dark energy' by creating a map of the observable Universe across the near-ultraviolet, visible and near-infrared parts of the spectrum (350-1000 nm).
To do that, the camera will use 14 of e2v's new 85 megapixel detectors, assembled into a near-square mosaic format to yield an image containing 1.2 billion pixels and measuring nearly half a meter wide.
The camera system, which will also include wavefront sensors, should be able to produce the giant images in just ten seconds, with spectroscopic information contained in each of the pixels. Weighing more than half a ton in all, the system will operate in a vacuum at minus 110 °C, and each image that it captures will need 2 GB of computer storage.
Giant galaxy survey
Professor Mariano Moles, Collaboration Board Chair for the Javalambre Physics-of-the-Accelerating-Universe Astrophysical Survey (J-PAS), said: "J-PAS is a 10,000 square degree survey whose main goal is trying to solve the mystery of dark energy by measuring tiny modulations in the distribution of galaxies."
Dark energy is the term used by cosmologists to describe the mysterious force that is causing the Universe to expand at an accelerating rate (itself a discovery that scooped this year's Nobel prize for physics), but for whose existence there is little in the way of a theoretical explanation or physical evidence.
Cryogenic camera system for the T250 telescope
"[The camera] will obtain low-resolution spectral information from every pixel, something akin to observing the whole northern sky through a gigantic prism," Moles explained. "This enormously rich dataset, which will include hundreds of millions of galaxies, and millions of stars and quasars, will have enormous value for most areas of astrophysics, from cosmology to solar system studies, including galaxy evolution and stellar physics."
In particular, the J-PAS survey will aim to measure the red-shift of 14 million early-stage galaxies, by viewing them through 56 continuous wavebands across the 350-1000 nm spectral range. That should allow astronomers to map galaxy distribution according to red-shift (and therefore distance from Earth) in three dimensions and with unprecedented precision.
In e2v's most recent financial statement, the firm's CEO Keith Attwood had said that while demand for defense applications was softening in Europe, scientific end user demand remained healthy.
Just last week the company said that it had signed another "multi-million Euro" contract, this time to manufacture and supply high-end CMOS imaging sensors for the third generation of "Meteosat" weather satellites being built by Thales Alenia Space, including supply chain management.
The e2v detectors will feature in the Flexible Combined Imager (FCI) instruments on board the series of six geostationary satellites, which are designed to provide better spatial, temporal and radiometric weather data than the current Meteostat constellation. The new satellites are slated for launch from 2017 onwards.
More from Applications
Metasurface gratings offer new route to polarization imaging
Quanergy lidar sensors land security mission
Trumpf platform brings high productivity to denture manufacture
Partnership to enable 'plug-and-play' 3D facial recognition
LASER 2019: German firms tout auto lidar components
LASER 2019: Tributes for retiring Poprawe as he considers industrial laser trends
e2v Technologies
e2v beats sales and profit targets
e2v profits grow while sensors probe ozone layer
e2v sees orders shift from defense to commercial


It is for adults who have Asperger syndrome and anxiety. We will discuss how to understand our disability and finding solutions that help us to thrive in social situations. First meeting will be June 8th at the LaMesa library at 6pm.
Learn about our individual symptoms of AS, and how to handle them.
We talk about our difficulties and strengths with Asperger syndrome, so that we can help each other manage better in every aspect of our lives.